kurl.cpp
00001 /* 00002 Copyright (C) 1999 Torben Weis <weis@kde.org> 00003 00004 This library is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Library General Public 00006 License as published by the Free Software Foundation; either 00007 version 2 of the License, or (at your option) any later version. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00017 Boston, MA 02110-1301, USA. 00018 */ 00019 00020 /* 00021 * The currently active RFC for URL/URIs is RFC3986 00022 * Previous (and now deprecated) RFCs are RFC1738 and RFC2396 00023 */ 00024 00025 #include "kurl.h" 00026 00027 // KDE_QT_ONLY is first used for dcop/client (e.g. marshalling) 00028 #ifndef KDE_QT_ONLY 00029 #include <kdebug.h> 00030 #include <tdeglobal.h> 00031 #include <kidna.h> 00032 #include <kprotocolinfo.h> 00033 #endif 00034 00035 #include <stdio.h> 00036 #include <assert.h> 00037 #include <ctype.h> 00038 #include <stdlib.h> 00039 #include <unistd.h> 00040 00041 #include <tqurl.h> 00042 #include <tqdir.h> 00043 #include <tqstringlist.h> 00044 #include <tqregexp.h> 00045 #include <tqstylesheet.h> 00046 #include <tqmap.h> 00047 #include <tqtextcodec.h> 00048 #include <tqmutex.h> 00049 00050 #ifdef Q_WS_WIN 00051 # define KURL_ROOTDIR_PATH "C:/" 00052 #else 00053 # define KURL_ROOTDIR_PATH "/" 00054 #endif 00055 00056 static const TQString fileProt = "file"; 00057 00058 static TQTextCodec * codecForHint( int encoding_hint /* not 0 ! */ ) 00059 { 00060 return TQTextCodec::codecForMib( encoding_hint ); 00061 } 00062 00063 // encoding_offset: 00064 // 0 encode both @ and / 00065 // 1 encode @ but not / 00066 // 2 encode neither @ or / 00067 static TQString encode( const TQString& segment, int encoding_offset, int encoding_hint, bool isRawURI = false ) 00068 { 00069 const char *encode_string = "/@<>#\"&?={}|^~[]\'`\\:+%"; 00070 encode_string += encoding_offset; 00071 00072 TQCString local; 00073 if (encoding_hint==0) 00074 local = segment.local8Bit(); 00075 else 00076 { 00077 TQTextCodec * textCodec = codecForHint( encoding_hint ); 00078 if (!textCodec) 00079 local = segment.local8Bit(); 00080 else 00081 local = textCodec->fromUnicode( segment ); 00082 } 00083 00084 int old_length = isRawURI ? local.size() - 1 : local.length(); 00085 00086 if ( old_length < 1 ) 00087 return segment.isNull() ? TQString::null : TQString(""); // differentiate null and empty 00088 00089 // a worst case approximation 00090 TQChar *new_segment = new TQChar[ old_length * 3 + 1 ]; 00091 int new_length = 0; 00092 00093 for ( int i = 0; i < old_length; i++ ) 00094 { 00095 // 'unsave' and 'reserved' characters 00096 // according to RFC 1738, 00097 // 2.2. URL Character Encoding Issues (pp. 3-4) 00098 // WABA: Added non-ascii 00099 unsigned char character = local[i]; 00100 if ( (character <= 32) || (character >= 127) || 00101 strchr(encode_string, character) ) 00102 { 00103 new_segment[ new_length++ ] = '%'; 00104 00105 unsigned int c = character / 16; 00106 c += (c > 9) ? ('A' - 10) : '0'; 00107 new_segment[ new_length++ ] = c; 00108 00109 c = character % 16; 00110 c += (c > 9) ? ('A' - 10) : '0'; 00111 new_segment[ new_length++ ] = c; 00112 00113 } 00114 else 00115 new_segment[ new_length++ ] = (TQChar)local[i]; 00116 } 00117 00118 TQString result = TQString(new_segment, new_length); 00119 delete [] new_segment; 00120 return result; 00121 } 00122 00123 static TQString encodeHost( const TQString& segment, bool encode_slash, int encoding_hint ) 00124 { 00125 // Hostnames are encoded differently 00126 // we use the IDNA transformation instead 00127 00128 // Note: when merging qt-addon, use QResolver::domainToAscii here 00129 #ifndef KDE_QT_ONLY 00130 Q_UNUSED( encode_slash ); 00131 Q_UNUSED( encoding_hint ); 00132 TQString host = KIDNA::toAscii(segment); 00133 if (host.isEmpty()) 00134 return segment; 00135 return host; 00136 #else 00137 return encode(segment, encode_slash ? 0 : 1, encoding_hint); 00138 #endif 00139 } 00140 00141 static int hex2int( unsigned int _char ) 00142 { 00143 if ( _char >= 'A' && _char <='F') 00144 return _char - 'A' + 10; 00145 if ( _char >= 'a' && _char <='f') 00146 return _char - 'a' + 10; 00147 if ( _char >= '0' && _char <='9') 00148 return _char - '0'; 00149 return -1; 00150 } 00151 00152 // WABA: The result of lazy_encode isn't usable for a URL which 00153 // needs to satisfies RFC requirements. However, the following 00154 // operation will make it usable again: 00155 // encode(decode(...)) 00156 // 00157 // As a result one can see that url.prettyURL() does not result in 00158 // a RFC compliant URL but that the following sequence does: 00159 // KURL(url.prettyURL()).url() 00160 00161 00162 static TQString lazy_encode( const TQString& segment, bool encodeAt=true ) 00163 { 00164 int old_length = segment.length(); 00165 00166 if ( !old_length ) 00167 return TQString::null; 00168 00169 // a worst case approximation 00170 TQChar *new_segment = new TQChar[ old_length * 3 + 1 ]; 00171 int new_length = 0; 00172 00173 for ( int i = 0; i < old_length; i++ ) 00174 { 00175 unsigned int character = segment[i].unicode(); // Don't use latin1() 00176 // It returns 0 for non-latin1 values 00177 // Small set of really ambiguous chars 00178 if ((character < 32) || // Low ASCII 00179 ((character == '%') && // The escape character itself 00180 (i+2 < old_length) && // But only if part of a valid escape sequence! 00181 (hex2int(segment[i+1].unicode())!= -1) && 00182 (hex2int(segment[i+2].unicode())!= -1)) || 00183 (character == '?') || // Start of query delimiter 00184 ((character == '@') && encodeAt) || // Username delimiter 00185 (character == '#') || // Start of reference delimiter 00186 ((character == 32) && (i+1 == old_length || segment[i+1] == (TQChar)' '))) // A trailing space 00187 { 00188 new_segment[ new_length++ ] = '%'; 00189 00190 unsigned int c = character / 16; 00191 c += (c > 9) ? ('A' - 10) : '0'; 00192 new_segment[ new_length++ ] = c; 00193 00194 c = character % 16; 00195 c += (c > 9) ? ('A' - 10) : '0'; 00196 new_segment[ new_length++ ] = c; 00197 } 00198 else 00199 new_segment[ new_length++ ] = segment[i]; 00200 } 00201 00202 TQString result = TQString(new_segment, new_length); 00203 delete [] new_segment; 00204 return result; 00205 } 00206 00207 static void decode( const TQString& segment, TQString &decoded, TQString &encoded, int encoding_hint=0, bool updateDecoded = true, bool isRawURI = false ) 00208 { 00209 decoded = TQString::null; 00210 encoded = segment; 00211 00212 int old_length = segment.length(); 00213 if ( !old_length ) 00214 return; 00215 00216 TQTextCodec *textCodec = 0; 00217 if (encoding_hint) 00218 textCodec = codecForHint( encoding_hint ); 00219 00220 if (!textCodec) 00221 textCodec = TQTextCodec::codecForLocale(); 00222 00223 TQCString csegment = textCodec->fromUnicode(segment); 00224 // Check if everything went ok 00225 if (textCodec->toUnicode(csegment) != segment) 00226 { 00227 // Uh oh 00228 textCodec = codecForHint( 106 ); // Fall back to utf-8 00229 csegment = textCodec->fromUnicode(segment); 00230 } 00231 old_length = csegment.length(); 00232 00233 int new_length = 0; 00234 int new_length2 = 0; 00235 00236 // make a copy of the old one 00237 char *new_segment = new char[ old_length + 1 ]; 00238 TQChar *new_usegment = new TQChar[ old_length * 3 + 1 ]; 00239 00240 int i = 0; 00241 while( i < old_length ) 00242 { 00243 bool bReencode = false; 00244 unsigned char character = csegment[ i++ ]; 00245 if ((character <= ' ') || (character > 127)) 00246 bReencode = true; 00247 00248 new_usegment [ new_length2++ ] = character; 00249 if (character == '%' ) 00250 { 00251 int a = i+1 < old_length ? hex2int( csegment[i] ) : -1; 00252 int b = i+1 < old_length ? hex2int( csegment[i+1] ) : -1; 00253 if ((a == -1) || (b == -1)) // Only replace if sequence is valid 00254 { 00255 // Contains stray %, make sure to re-encode! 00256 bReencode = true; 00257 } 00258 else 00259 { 00260 // Valid %xx sequence 00261 character = a * 16 + b; // Replace with value of %dd 00262 if (!isRawURI && !character && updateDecoded) 00263 break; // Stop at %00 00264 00265 new_usegment [ new_length2++ ] = (unsigned char) csegment[i++]; 00266 new_usegment [ new_length2++ ] = (unsigned char) csegment[i++]; 00267 } 00268 } 00269 if (bReencode) 00270 { 00271 new_length2--; 00272 new_usegment [ new_length2++ ] = '%'; 00273 00274 unsigned int c = character / 16; 00275 c += (c > 9) ? ('A' - 10) : '0'; 00276 new_usegment[ new_length2++ ] = c; 00277 00278 c = character % 16; 00279 c += (c > 9) ? ('A' - 10) : '0'; 00280 new_usegment[ new_length2++ ] = c; 00281 } 00282 00283 new_segment [ new_length++ ] = character; 00284 } 00285 new_segment [ new_length ] = 0; 00286 00287 encoded = TQString( new_usegment, new_length2); 00288 00289 // Encoding specified 00290 if (updateDecoded) 00291 { 00292 decoded = textCodec->toUnicode( new_segment ); 00293 if ( isRawURI ) { 00294 int length = tqstrlen( new_segment ); 00295 while ( length < new_length ) { 00296 decoded += TQChar::null; 00297 length += 1; 00298 decoded += textCodec->toUnicode( new_segment + length ); 00299 length += tqstrlen( new_segment + length ); 00300 } 00301 } 00302 00303 TQCString validate = textCodec->fromUnicode(decoded); 00304 00305 if (strcmp(validate.data(), new_segment) != 0) 00306 { 00307 decoded = TQString::fromLocal8Bit(new_segment, new_length); 00308 } 00309 } 00310 00311 delete [] new_segment; 00312 delete [] new_usegment; 00313 } 00314 00315 static TQString decode(const TQString &segment, int encoding_hint = 0, bool isRawURI = false) 00316 { 00317 TQString result; 00318 TQString tmp; 00319 decode(segment, result, tmp, encoding_hint, true, isRawURI); 00320 return result; 00321 } 00322 00323 static TQString cleanpath(const TQString &_path, bool cleanDirSeparator, bool decodeDots) 00324 { 00325 if (_path.isEmpty()) return TQString::null; 00326 00327 if (TQDir::isRelativePath(_path)) 00328 return _path; // Don't mangle mailto-style URLs 00329 00330 TQString path = _path; 00331 00332 int len = path.length(); 00333 00334 if (decodeDots) 00335 { 00336 #ifndef KDE_QT_ONLY 00337 static const TQString &encodedDot = TDEGlobal::staticQString("%2e"); 00338 #else 00339 TQString encodedDot("%2e"); 00340 #endif 00341 if (path.find(encodedDot, 0, false) != -1) 00342 { 00343 #ifndef KDE_QT_ONLY 00344 static const TQString &encodedDOT = TDEGlobal::staticQString("%2E"); // Uppercase! 00345 #else 00346 TQString encodedDOT("%2E"); 00347 #endif 00348 path.replace(encodedDot, "."); 00349 path.replace(encodedDOT, "."); 00350 len = path.length(); 00351 } 00352 } 00353 00354 bool slash = (len && path[len-1] == '/') || 00355 (len > 1 && path[len-2] == '/' && path[len-1] == '.'); 00356 00357 // The following code cleans up directory path much like 00358 // TQDir::cleanDirPath() except it can be made to ignore multiple 00359 // directory separators by setting the flag to false. That fixes 00360 // bug# 15044, mail.altavista.com and other similar brain-dead server 00361 // implementations that do not follow what has been specified in 00362 // RFC 2396!! (dA) 00363 TQString result; 00364 int cdUp, orig_pos, pos; 00365 00366 cdUp = 0; 00367 pos = orig_pos = len; 00368 while ( pos && (pos = path.findRev('/',--pos)) != -1 ) 00369 { 00370 len = orig_pos - pos - 1; 00371 if ( len == 2 && path[pos+1] == '.' && path[pos+2] == '.' ) 00372 cdUp++; 00373 else 00374 { 00375 // Ignore any occurrences of '.' 00376 // This includes entries that simply do not make sense like /..../ 00377 if ( (len || !cleanDirSeparator) && 00378 (len != 1 || path[pos+1] != '.' ) ) 00379 { 00380 if ( !cdUp ) 00381 result.prepend(path.mid(pos, len+1)); 00382 else 00383 cdUp--; 00384 } 00385 } 00386 orig_pos = pos; 00387 } 00388 00389 #ifdef Q_WS_WIN // prepend drive letter if exists (js) 00390 if (orig_pos >= 2 && isalpha(path[0].latin1()) && path[1]==':') { 00391 result.prepend(TQString(path[0])+":"); 00392 } 00393 #endif 00394 00395 if ( result.isEmpty() ) 00396 result = KURL_ROOTDIR_PATH; 00397 else if ( slash && result[result.length()-1] != '/' ) 00398 result.append('/'); 00399 00400 return result; 00401 } 00402 00403 class KURLPrivate 00404 { 00405 public: 00406 TQString m_strInternalReferenceURL; 00407 }; 00408 00409 bool KURL::isRelativeURL(const TQString &_url) 00410 { 00411 int len = _url.length(); 00412 if (!len) return true; // Very short relative URL. 00413 const TQChar *str = _url.unicode(); 00414 00415 // Absolute URL must start with alpha-character 00416 if (!isalpha(str[0].latin1())) 00417 return true; // Relative URL 00418 00419 for(int i = 1; i < len; i++) 00420 { 00421 char c = str[i].latin1(); // Note: non-latin1 chars return 0! 00422 if (c == ':') 00423 return false; // Absolute URL 00424 00425 // Protocol part may only contain alpha, digit, + or - 00426 if (!isalpha(c) && !isdigit(c) && (c != '+') && (c != '-')) 00427 return true; // Relative URL 00428 } 00429 // URL did not contain ':' 00430 return true; // Relative URL 00431 } 00432 00433 KURL::List::List(const KURL &url) 00434 { 00435 append( url ); 00436 } 00437 00438 KURL::List::List(const TQStringList &list) 00439 { 00440 for (TQStringList::ConstIterator it = list.begin(); 00441 it != list.end(); 00442 it++) 00443 { 00444 append( KURL(*it) ); 00445 } 00446 } 00447 00448 TQStringList KURL::List::toStringList() const 00449 { 00450 TQStringList lst; 00451 for( KURL::List::ConstIterator it = begin(); 00452 it != end(); 00453 it++) 00454 { 00455 lst.append( (*it).url() ); 00456 } 00457 return lst; 00458 } 00459 00460 00461 KURL::KURL() 00462 { 00463 d = new KURLPrivate(); 00464 reset(); 00465 } 00466 00467 KURL::~KURL() 00468 { 00469 if (d) { 00470 delete d; 00471 } 00472 } 00473 00474 00475 KURL::KURL( const TQString &url, int encoding_hint ) 00476 { 00477 d = new KURLPrivate(); 00478 reset(); 00479 parse( url, encoding_hint ); 00480 } 00481 00482 KURL::KURL( const char * url, int encoding_hint ) 00483 { 00484 d = new KURLPrivate(); 00485 reset(); 00486 parse( TQString::fromLatin1(url), encoding_hint ); 00487 } 00488 00489 KURL::KURL( const TQCString& url, int encoding_hint ) 00490 { 00491 d = new KURLPrivate(); 00492 reset(); 00493 parse( TQString::fromLatin1(url), encoding_hint ); 00494 } 00495 00496 KURL::KURL( const KURL& _u ) 00497 { 00498 d = new KURLPrivate(); 00499 *this = _u; 00500 d->m_strInternalReferenceURL = _u.d->m_strInternalReferenceURL; 00501 } 00502 00503 TQDataStream & operator<< (TQDataStream & s, const KURL & a) 00504 { 00505 TQString QueryForWire=a.m_strQuery_encoded; 00506 if (!a.m_strQuery_encoded.isNull()) 00507 QueryForWire.prepend("?"); 00508 00509 s << a.m_strProtocol << a.m_strUser << a.m_strPass << a.m_strHost 00510 << a.m_strPath << a.m_strPath_encoded << QueryForWire << a.m_strRef_encoded 00511 << TQ_INT8(a.m_bIsMalformed ? 1 : 0) << a.m_iPort; 00512 return s; 00513 } 00514 00515 TQDataStream & operator>> (TQDataStream & s, KURL & a) 00516 { 00517 TQ_INT8 malf; 00518 TQString QueryFromWire; 00519 s >> a.m_strProtocol >> a.m_strUser >> a.m_strPass >> a.m_strHost 00520 >> a.m_strPath >> a.m_strPath_encoded >> QueryFromWire >> a.m_strRef_encoded 00521 >> malf >> a.m_iPort; 00522 a.m_bIsMalformed = (malf != 0); 00523 00524 if ( QueryFromWire.isNull() ) 00525 a.m_strQuery_encoded = TQString::null; 00526 else if ( QueryFromWire.length() == 1 ) // empty query 00527 a.m_strQuery_encoded = ""; 00528 else 00529 a.m_strQuery_encoded = QueryFromWire.mid(1); 00530 00531 a.m_iUriMode = KURL::uriModeForProtocol( a.m_strProtocol ); 00532 00533 return s; 00534 } 00535 00536 #ifndef QT_NO_NETWORKPROTOCOL 00537 KURL::KURL( const TQUrl &u ) 00538 { 00539 d = new KURLPrivate(); 00540 *this = u; 00541 } 00542 #endif 00543 00544 KURL::KURL( const KURL& _u, const TQString& _rel_url, int encoding_hint ) 00545 { 00546 d = new KURLPrivate(); 00547 d->m_strInternalReferenceURL = _u.d->m_strInternalReferenceURL; 00548 00549 if (_u.hasSubURL()) // Operate on the last suburl, not the first 00550 { 00551 KURL::List lst = split( _u ); 00552 KURL u(lst.last(), _rel_url, encoding_hint); 00553 lst.remove( lst.last() ); 00554 lst.append( u ); 00555 *this = join( lst ); 00556 d->m_strInternalReferenceURL = _u.d->m_strInternalReferenceURL; 00557 return; 00558 } 00559 // WORKAROUND THE RFC 1606 LOOPHOLE THAT ALLOWS 00560 // http:/index.html AS A VALID SYNTAX FOR RELATIVE 00561 // URLS. ( RFC 2396 section 5.2 item # 3 ) 00562 TQString rUrl = _rel_url; 00563 int len = _u.m_strProtocol.length(); 00564 if ( !_u.m_strHost.isEmpty() && !rUrl.isEmpty() && 00565 rUrl.find( _u.m_strProtocol, 0, false ) == 0 && 00566 rUrl[len] == ':' && (rUrl[len+1] != '/' || 00567 (rUrl[len+1] == '/' && rUrl[len+2] != '/')) ) 00568 { 00569 rUrl.remove( 0, rUrl.find( ':' ) + 1 ); 00570 } 00571 00572 if ( rUrl.isEmpty() ) 00573 { 00574 *this = _u; 00575 } 00576 else if ( rUrl[0] == '#' ) 00577 { 00578 *this = _u; 00579 m_strRef_encoded = rUrl.mid(1); 00580 if ( m_strRef_encoded.isNull() ) 00581 m_strRef_encoded = ""; // we know there was an (empty) html ref, we saw the '#' 00582 } 00583 else if ( isRelativeURL( rUrl) ) 00584 { 00585 *this = _u; 00586 m_strQuery_encoded = TQString::null; 00587 m_strRef_encoded = TQString::null; 00588 if ( rUrl[0] == '/') 00589 { 00590 if ((rUrl.length() > 1) && (rUrl[1] == '/')) 00591 { 00592 m_strHost = TQString::null; 00593 // File protocol returns file:/// without host, strip // from rUrl 00594 if (_u.m_strProtocol == fileProt) 00595 rUrl.remove(0, 2); 00596 } 00597 m_strPath = TQString::null; 00598 m_strPath_encoded = TQString::null; 00599 } 00600 else if ( rUrl[0] != '?' ) 00601 { 00602 int pos = m_strPath.findRev( '/' ); 00603 if (pos >= 0) 00604 m_strPath.truncate(pos); 00605 m_strPath += '/'; 00606 if (!m_strPath_encoded.isEmpty()) 00607 { 00608 pos = m_strPath_encoded.findRev( '/' ); 00609 if (pos >= 0) 00610 m_strPath_encoded.truncate(pos); 00611 m_strPath_encoded += '/'; 00612 } 00613 } 00614 else 00615 { 00616 if ( m_strPath.isEmpty() ) 00617 m_strPath = '/'; 00618 } 00619 KURL tmp( url() + rUrl, encoding_hint); 00620 *this = tmp; 00621 cleanPath(false); 00622 } 00623 else 00624 { 00625 KURL tmp( rUrl, encoding_hint); 00626 *this = tmp; 00627 // Preserve userinfo if applicable. 00628 if (!_u.m_strUser.isEmpty() && m_strUser.isEmpty() && (_u.m_strHost == m_strHost) && (_u.m_strProtocol == m_strProtocol)) 00629 { 00630 m_strUser = _u.m_strUser; 00631 m_strPass = _u.m_strPass; 00632 } 00633 cleanPath(false); 00634 } 00635 } 00636 00637 void KURL::reset() 00638 { 00639 m_strProtocol = TQString::null; 00640 m_strUser = TQString::null; 00641 m_strPass = TQString::null; 00642 m_strHost = TQString::null; 00643 m_strPath = TQString::null; 00644 m_strPath_encoded = TQString::null; 00645 m_strQuery_encoded = TQString::null; 00646 m_strRef_encoded = TQString::null; 00647 m_bIsMalformed = true; 00648 m_iPort = 0; 00649 m_iUriMode = Auto; 00650 } 00651 00652 bool KURL::isEmpty() const 00653 { 00654 return (m_strPath.isEmpty() && m_strProtocol.isEmpty()); 00655 } 00656 00657 void KURL::parse( const TQString& _url, int encoding_hint ) 00658 { 00659 if ( _url.isEmpty() || m_iUriMode == Invalid ) 00660 { 00661 m_strProtocol = _url; 00662 m_iUriMode = Invalid; 00663 return; 00664 } 00665 00666 const TQChar* buf = _url.unicode(); 00667 const TQChar* orig = buf; 00668 uint len = _url.length(); 00669 uint pos = 0; 00670 00671 // Node 1: Accept alpha or slash 00672 TQChar x = buf[pos++]; 00673 #ifdef Q_WS_WIN 00674 /* win32: accept <letter>: or <letter>:/ or <letter>:\ */ 00675 const bool alpha = isalpha((int)x); 00676 if (alpha && len<2) 00677 goto NodeErr; 00678 if (alpha && buf[pos]==':' && (len==2 || (len>2 && (buf[pos+1]=='/' || buf[pos+1]=='\\')))) 00679 #else 00680 if ( x == (TQChar)'/' ) 00681 #endif 00682 { 00683 // A slash means we immediately proceed to parse it as a file URL. 00684 m_iUriMode = URL; 00685 m_strProtocol = fileProt; 00686 parseURL( _url, encoding_hint ); 00687 return; 00688 } 00689 if ( !isalpha( (int)x ) ) 00690 goto NodeErr; 00691 00692 // Node 2: Accept any amount of (alpha|digit|'+'|'-') 00693 // '.' is not currently accepted, because current KURL may be confused. 00694 // Proceed with :// :/ or : 00695 while( pos < len && (isalpha((int)buf[pos]) || isdigit((int)buf[pos]) || 00696 buf[pos] == (TQChar)'+' || buf[pos] == (TQChar)'-')) pos++; 00697 00698 if (pos < len && buf[pos] == (TQChar)':' ) 00699 { 00700 m_strProtocol = TQString( orig, pos ).lower(); 00701 if ( m_iUriMode == Auto ) 00702 m_iUriMode = uriModeForProtocol( m_strProtocol ); 00703 // Proceed to correct parse function. 00704 switch ( m_iUriMode ) 00705 { 00706 case RawURI: 00707 parseRawURI( _url ); 00708 return; 00709 case Mailto: 00710 parseMailto( _url ); 00711 return; 00712 case URL: 00713 parseURL( _url, encoding_hint ); 00714 return; 00715 default: 00716 // Unknown URI mode results in an invalid URI. 00717 break; 00718 } 00719 } 00720 00721 NodeErr: 00722 reset(); 00723 m_strProtocol = _url; 00724 m_iUriMode = Invalid; 00725 } 00726 00727 void KURL::parseRawURI( const TQString& _url, int encoding_hint ) 00728 { 00729 uint len = _url.length(); 00730 const TQChar* buf = _url.unicode(); 00731 00732 uint pos = 0; 00733 00734 // Accept any amount of (alpha|digit|'+'|'-') 00735 // '.' is not currently accepted, because current KURL may be confused. 00736 // Proceed with : 00737 while( pos < len && (isalpha((int)buf[pos]) || isdigit((int)buf[pos]) || 00738 buf[pos] == (TQChar)'+' || buf[pos] == (TQChar)'-')) pos++; 00739 00740 // Note that m_strProtocol is already set here, so we just skip over the protocol. 00741 if (pos < len && buf[pos] == (TQChar)':' ) 00742 pos++; 00743 else { // can't happen, the caller checked all this already 00744 reset(); 00745 m_strProtocol = _url; 00746 m_iUriMode = Invalid; 00747 return; 00748 } 00749 00750 if ( pos == len ) // can't happen, the caller checked this already 00751 m_strPath = TQString::null; 00752 else 00753 m_strPath = decode( TQString( buf + pos, len - pos ), encoding_hint, true ); 00754 00755 m_bIsMalformed = false; 00756 00757 return; 00758 } 00759 00760 void KURL::parseMailto( const TQString& _url, int encoding_hint ) 00761 { 00762 parseURL( _url, encoding_hint); 00763 if ( m_bIsMalformed ) 00764 return; 00765 TQRegExp mailre("(.+@)(.+)"); 00766 if ( mailre.exactMatch( m_strPath ) ) 00767 { 00768 #ifndef KDE_QT_ONLY 00769 TQString host = KIDNA::toUnicode( mailre.cap( 2 ) ); 00770 if (host.isEmpty()) 00771 host = TQString(mailre.cap( 2 )).lower(); 00772 #else 00773 TQString host = TQString(mailre.cap( 2 )).lower(); 00774 #endif 00775 m_strPath = mailre.cap( 1 ) + host; 00776 } 00777 } 00778 00779 void KURL::parseURL( const TQString& _url, int encoding_hint ) 00780 { 00781 TQString port; 00782 bool badHostName = false; 00783 int start = 0; 00784 uint len = _url.length(); 00785 const TQChar* buf = _url.unicode(); 00786 00787 TQChar delim; 00788 TQString tmp; 00789 00790 uint pos = 0; 00791 00792 // Node 1: Accept alpha or slash 00793 TQChar x = buf[pos++]; 00794 #ifdef Q_WS_WIN 00795 /* win32: accept <letter>: or <letter>:/ or <letter>:\ */ 00796 const bool alpha = isalpha((int)x); 00797 if (alpha && len<2) 00798 goto NodeErr; 00799 if (alpha && buf[pos]==(TQChar)':' && (len==2 || (len>2 && (buf[pos+1]==(TQChar)'/' || buf[pos+1]==(TQChar)'\\')))) 00800 #else 00801 if ( x == (TQChar)'/' ) 00802 #endif 00803 goto Node9; 00804 if ( !isalpha( (int)x ) ) 00805 goto NodeErr; 00806 00807 // Node 2: Accept any amount of (alpha|digit|'+'|'-') 00808 // '.' is not currently accepted, because current KURL may be confused. 00809 // Proceed with :// :/ or : 00810 while( pos < len && (isalpha((int)buf[pos]) || isdigit((int)buf[pos]) || 00811 buf[pos] == (TQChar)'+' || buf[pos] == (TQChar)'-')) pos++; 00812 00813 // Note that m_strProtocol is already set here, so we just skip over the protocol. 00814 if ( pos+2 < len && buf[pos] == (TQChar)':' && buf[pos+1] == (TQChar)'/' && buf[pos+2] == (TQChar)'/' ) 00815 { 00816 pos += 3; 00817 } 00818 else if (pos+1 < len && buf[pos] == (TQChar)':' ) // Need to always compare length()-1 otherwise KURL passes "http:" as legal!! 00819 { 00820 pos++; 00821 start = pos; 00822 goto Node9; 00823 } 00824 else 00825 goto NodeErr; 00826 00827 //Node 3: We need at least one character here 00828 if ( pos == len ) 00829 goto NodeErr; 00830 start = pos; 00831 00832 // Node 4: Accept any amount of characters. 00833 if (buf[pos] == (TQChar)'[') // An IPv6 host follows. 00834 goto Node8; 00835 // Terminate on / or @ or ? or # or " or ; or < 00836 x = buf[pos]; 00837 while( (x != (TQChar)':') && (x != (TQChar)'@') && (x != (TQChar)'/') && (x != (TQChar)'?') && (x != (TQChar)'#') ) 00838 { 00839 if ((x == (TQChar)'\"') || (x == (TQChar)';') || (x == (TQChar)'<')) 00840 badHostName = true; 00841 if (++pos == len) 00842 break; 00843 x = buf[pos]; 00844 } 00845 if ( pos == len ) 00846 { 00847 if (badHostName) 00848 goto NodeErr; 00849 00850 setHost(decode(TQString( buf + start, pos - start ), encoding_hint)); 00851 goto NodeOk; 00852 } 00853 if ( x == (TQChar)'@' ) 00854 { 00855 m_strUser = decode(TQString( buf + start, pos - start ), encoding_hint); 00856 pos++; 00857 goto Node7; 00858 } 00859 else if ( (x == (TQChar)'/') || (x == (TQChar)'?') || (x == (TQChar)'#')) 00860 { 00861 if (badHostName) 00862 goto NodeErr; 00863 00864 setHost(decode(TQString( buf + start, pos - start ), encoding_hint)); 00865 start = pos; 00866 goto Node9; 00867 } 00868 else if ( x != (TQChar)':' ) 00869 goto NodeErr; 00870 m_strUser = decode(TQString( buf + start, pos - start ), encoding_hint); 00871 pos++; 00872 00873 // Node 5: We need at least one character 00874 if ( pos == len ) 00875 goto NodeErr; 00876 start = pos++; 00877 00878 // Node 6: Read everything until @, /, ? or # 00879 while( (pos < len) && 00880 (buf[pos] != (TQChar)'@') && 00881 (buf[pos] != (TQChar)'/') && 00882 (buf[pos] != (TQChar)'?') && 00883 (buf[pos] != (TQChar)'#')) pos++; 00884 // If we now have a '@' the ':' seperates user and password. 00885 // Otherwise it seperates host and port. 00886 if ( (pos == len) || (buf[pos] != (TQChar)'@') ) 00887 { 00888 // Ok the : was used to separate host and port 00889 if (badHostName) 00890 goto NodeErr; 00891 setHost(m_strUser); 00892 m_strUser = TQString::null; 00893 TQString tmp( buf + start, pos - start ); 00894 char *endptr; 00895 m_iPort = (unsigned short int)strtol(tmp.ascii(), &endptr, 10); 00896 if ((pos == len) && (strlen(endptr) == 0)) 00897 goto NodeOk; 00898 // there is more after the digits 00899 pos -= strlen(endptr); 00900 if ((buf[pos] != (TQChar)'@') && 00901 (buf[pos] != (TQChar)'/') && 00902 (buf[pos] != (TQChar)'?') && 00903 (buf[pos] != (TQChar)'#')) 00904 goto NodeErr; 00905 00906 start = pos; 00907 goto Node9; 00908 } 00909 m_strPass = decode(TQString( buf + start, pos - start), encoding_hint); 00910 pos++; 00911 00912 // Node 7: We need at least one character 00913 Node7: 00914 if ( pos == len ) 00915 goto NodeErr; 00916 00917 Node8: 00918 if (buf[pos] == (TQChar)'[') 00919 { 00920 // IPv6 address 00921 start = ++pos; // Skip '[' 00922 00923 if (pos == len) 00924 { 00925 badHostName = true; 00926 goto NodeErr; 00927 } 00928 // Node 8a: Read everything until ] or terminate 00929 badHostName = false; 00930 x = buf[pos]; 00931 while( (x != (TQChar)']') ) 00932 { 00933 if ((x == (TQChar)'\"') || (x == (TQChar)';') || (x == (TQChar)'<')) 00934 badHostName = true; 00935 if (++pos == len) 00936 { 00937 badHostName = true; 00938 break; 00939 } 00940 x = buf[pos]; 00941 } 00942 if (badHostName) 00943 goto NodeErr; 00944 setHost(decode(TQString( buf + start, pos - start ), encoding_hint)); 00945 if (pos < len) pos++; // Skip ']' 00946 if (pos == len) 00947 goto NodeOk; 00948 } 00949 else 00950 { 00951 // Non IPv6 address, with a user 00952 start = pos; 00953 00954 // Node 8b: Read everything until / : or terminate 00955 badHostName = false; 00956 x = buf[pos]; 00957 while( (x != (TQChar)':') && (x != (TQChar)'@') && (x != (TQChar)'/') && (x != (TQChar)'?') && (x != (TQChar)'#') ) 00958 { 00959 if ((x == (TQChar)'\"') || (x == (TQChar)';') || (x == (TQChar)'<')) 00960 badHostName = true; 00961 if (++pos == len) 00962 break; 00963 x = buf[pos]; 00964 } 00965 if (badHostName) 00966 goto NodeErr; 00967 if ( pos == len ) 00968 { 00969 setHost(decode(TQString( buf + start, pos - start ), encoding_hint)); 00970 goto NodeOk; 00971 } 00972 setHost(decode(TQString( buf + start, pos - start ), encoding_hint)); 00973 } 00974 x = buf[pos]; 00975 if ( x == (TQChar)'/' || x == (TQChar)'#' || x == (TQChar)'?' ) 00976 { 00977 start = pos; 00978 goto Node9; 00979 } 00980 else if ( x != (TQChar)':' ) 00981 goto NodeErr; 00982 pos++; 00983 00984 // Node 8c: Accept at least one digit 00985 if ( pos == len ) 00986 goto NodeErr; 00987 start = pos; 00988 if ( !isdigit( buf[pos++] ) ) 00989 goto NodeErr; 00990 00991 // Node 8d: Accept any amount of digits 00992 while( pos < len && isdigit( buf[pos] ) ) pos++; 00993 port = TQString( buf + start, pos - start ); 00994 m_iPort = port.toUShort(); 00995 if ( pos == len ) 00996 goto NodeOk; 00997 start = pos; 00998 00999 Node9: // parse path until query or reference reached 01000 01001 while( pos < len && buf[pos] != (TQChar)'#' && buf[pos]!=(TQChar)'?' ) pos++; 01002 01003 tmp = TQString( buf + start, pos - start ); 01004 //kdDebug(126)<<" setting encoded path to:"<<tmp<<endl; 01005 setEncodedPath( tmp, encoding_hint ); 01006 01007 if ( pos == len ) 01008 goto NodeOk; 01009 01010 //Node10: // parse query or reference depending on what comes first 01011 delim = (buf[pos++]==(TQChar)'#'?(TQChar)'?':(TQChar)'#'); 01012 01013 start = pos; 01014 01015 while(pos < len && buf[pos]!=delim ) pos++; 01016 01017 tmp = TQString(buf + start, pos - start); 01018 if (delim==(TQChar)'#') 01019 _setQuery(tmp, encoding_hint); 01020 else 01021 m_strRef_encoded = tmp; 01022 01023 if (pos == len) 01024 goto NodeOk; 01025 01026 //Node11: // feed the rest into the remaining variable 01027 tmp = TQString( buf + pos + 1, len - pos - 1); 01028 if (delim == (TQChar)'#') 01029 m_strRef_encoded = tmp; 01030 else 01031 _setQuery(tmp, encoding_hint); 01032 01033 NodeOk: 01034 //kdDebug(126)<<"parsing finished. m_strProtocol="<<m_strProtocol<<" m_strHost="<<m_strHost<<" m_strPath="<<m_strPath<<endl; 01035 m_bIsMalformed = false; // Valid URL 01036 01037 //kdDebug()<<"Prot="<<m_strProtocol<<"\nUser="<<m_strUser<<"\nPass="<<m_strPass<<"\nHost="<<m_strHost<<"\nPath="<<m_strPath<<"\nQuery="<<m_strQuery_encoded<<"\nRef="<<m_strRef_encoded<<"\nPort="<<m_iPort<<endl; 01038 if (m_strProtocol.isEmpty()) 01039 { 01040 m_iUriMode = URL; 01041 m_strProtocol = fileProt; 01042 } 01043 return; 01044 01045 NodeErr: 01046 // kdDebug(126) << "KURL couldn't parse URL \"" << _url << "\"" << endl; 01047 reset(); 01048 m_strProtocol = _url; 01049 m_iUriMode = Invalid; 01050 } 01051 01052 KURL& KURL::operator=( const TQString& _url ) 01053 { 01054 reset(); 01055 parse( _url ); 01056 01057 return *this; 01058 } 01059 01060 KURL& KURL::operator=( const char * _url ) 01061 { 01062 reset(); 01063 parse( TQString::fromLatin1(_url) ); 01064 01065 return *this; 01066 } 01067 01068 #ifndef QT_NO_NETWORKPROTOCOL 01069 KURL& KURL::operator=( const TQUrl & u ) 01070 { 01071 m_strProtocol = u.protocol(); 01072 m_iUriMode = Auto; 01073 m_strUser = u.user(); 01074 m_strPass = u.password(); 01075 m_strHost = u.host(); 01076 m_strPath = u.path( false ); 01077 m_strPath_encoded = TQString::null; 01078 m_strQuery_encoded = u.query(); 01079 m_strRef_encoded = u.ref(); 01080 m_bIsMalformed = !u.isValid(); 01081 m_iPort = u.port(); 01082 01083 return *this; 01084 } 01085 #endif 01086 01087 KURL& KURL::operator=( const KURL& _u ) 01088 { 01089 m_strProtocol = _u.m_strProtocol; 01090 m_strUser = _u.m_strUser; 01091 m_strPass = _u.m_strPass; 01092 m_strHost = _u.m_strHost; 01093 m_strPath = _u.m_strPath; 01094 m_strPath_encoded = _u.m_strPath_encoded; 01095 m_strQuery_encoded = _u.m_strQuery_encoded; 01096 m_strRef_encoded = _u.m_strRef_encoded; 01097 m_bIsMalformed = _u.m_bIsMalformed; 01098 m_iPort = _u.m_iPort; 01099 m_iUriMode = _u.m_iUriMode; 01100 d->m_strInternalReferenceURL = _u.d->m_strInternalReferenceURL; 01101 01102 return *this; 01103 } 01104 01105 bool KURL::operator<( const KURL& _u) const 01106 { 01107 int i; 01108 if (!_u.isValid()) 01109 { 01110 if (!isValid()) 01111 { 01112 i = m_strProtocol.compare(_u.m_strProtocol); 01113 return (i < 0); 01114 } 01115 return false; 01116 } 01117 if (!isValid()) 01118 return true; 01119 01120 i = m_strProtocol.compare(_u.m_strProtocol); 01121 if (i) return (i < 0); 01122 01123 i = m_strHost.compare(_u.m_strHost); 01124 if (i) return (i < 0); 01125 01126 if (m_iPort != _u.m_iPort) return (m_iPort < _u.m_iPort); 01127 01128 i = m_strPath.compare(_u.m_strPath); 01129 if (i) return (i < 0); 01130 01131 i = m_strQuery_encoded.compare(_u.m_strQuery_encoded); 01132 if (i) return (i < 0); 01133 01134 i = m_strRef_encoded.compare(_u.m_strRef_encoded); 01135 if (i) return (i < 0); 01136 01137 i = m_strUser.compare(_u.m_strUser); 01138 if (i) return (i < 0); 01139 01140 i = m_strPass.compare(_u.m_strPass); 01141 if (i) return (i < 0); 01142 01143 i = d->m_strInternalReferenceURL.compare(_u.d->m_strInternalReferenceURL); 01144 if (i) return (i < 0); 01145 01146 return false; 01147 } 01148 01149 bool KURL::operator==( const KURL& _u ) const 01150 { 01151 if ( !isValid() || !_u.isValid() ) 01152 return false; 01153 01154 if ( m_strProtocol == _u.m_strProtocol && 01155 m_strUser == _u.m_strUser && 01156 m_strPass == _u.m_strPass && 01157 m_strHost == _u.m_strHost && 01158 m_strPath == _u.m_strPath && 01159 // The encoded path may be null, but the URLs are still equal (David) 01160 ( m_strPath_encoded.isNull() || _u.m_strPath_encoded.isNull() || 01161 m_strPath_encoded == _u.m_strPath_encoded ) && 01162 m_strQuery_encoded == _u.m_strQuery_encoded && 01163 m_strRef_encoded == _u.m_strRef_encoded && 01164 m_iPort == _u.m_iPort && 01165 d->m_strInternalReferenceURL == _u.d->m_strInternalReferenceURL ) 01166 { 01167 return true; 01168 } 01169 01170 return false; 01171 } 01172 01173 bool KURL::operator==( const TQString& _u ) const 01174 { 01175 KURL u( _u ); 01176 return ( *this == u ); 01177 } 01178 01179 bool KURL::cmp( const KURL &u, bool ignore_trailing ) const 01180 { 01181 return equals( u, ignore_trailing ); 01182 } 01183 01184 bool KURL::equals( const KURL &_u, bool ignore_trailing ) const 01185 { 01186 return equals(_u, ignore_trailing, false); 01187 } 01188 01189 bool KURL::equals( const KURL &_u, bool ignore_trailing, bool ignore_internalReferenceURLS ) const 01190 { 01191 if ( !isValid() || !_u.isValid() ) 01192 return false; 01193 01194 if ( ignore_trailing ) 01195 { 01196 TQString path1 = path(1); 01197 TQString path2 = _u.path(1); 01198 if ( path1 != path2 ) 01199 return false; 01200 01201 if ( m_strProtocol == _u.m_strProtocol && 01202 m_strUser == _u.m_strUser && 01203 m_strPass == _u.m_strPass && 01204 m_strHost == _u.m_strHost && 01205 m_strQuery_encoded == _u.m_strQuery_encoded && 01206 m_strRef_encoded == _u.m_strRef_encoded && 01207 m_iPort == _u.m_iPort && 01208 ((ignore_internalReferenceURLS) || (d->m_strInternalReferenceURL == _u.d->m_strInternalReferenceURL)) ) 01209 return true; 01210 01211 return false; 01212 } 01213 01214 return ( *this == _u ); 01215 } 01216 01217 bool KURL::isParentOf( const KURL& _u ) const 01218 { 01219 if ( !isValid() || !_u.isValid() ) 01220 return false; 01221 01222 if ( m_strProtocol == _u.m_strProtocol && 01223 m_strUser == _u.m_strUser && 01224 m_strPass == _u.m_strPass && 01225 m_strHost == _u.m_strHost && 01226 m_strQuery_encoded == _u.m_strQuery_encoded && 01227 m_strRef_encoded == _u.m_strRef_encoded && 01228 m_iPort == _u.m_iPort ) 01229 { 01230 if ( path().isEmpty() || _u.path().isEmpty() ) 01231 return false; // can't work with implicit paths 01232 01233 TQString p1( cleanpath( path(), true, false ) ); 01234 if ( p1[p1.length()-1] != '/' ) 01235 p1 += '/'; 01236 TQString p2( cleanpath( _u.path(), true, false ) ); 01237 if ( p2[p2.length()-1] != '/' ) 01238 p2 += '/'; 01239 01240 //kdDebug(126) << "p1=" << p1 << endl; 01241 //kdDebug(126) << "p2=" << p2 << endl; 01242 //kdDebug(126) << "p1.length()=" << p1.length() << endl; 01243 //kdDebug(126) << "p2.left(!$)=" << p2.left( p1.length() ) << endl; 01244 return p2.startsWith( p1 ); 01245 } 01246 return false; 01247 } 01248 01249 void KURL::setFileName( const TQString& _txt ) 01250 { 01251 m_strRef_encoded = TQString::null; 01252 int i = 0; 01253 while( _txt[i] == (TQChar)'/' ) ++i; 01254 TQString tmp; 01255 if ( i ) 01256 tmp = _txt.mid( i ); 01257 else 01258 tmp = _txt; 01259 01260 TQString path = m_strPath_encoded.isEmpty() ? m_strPath : m_strPath_encoded; 01261 if ( path.isEmpty() ) 01262 path = "/"; 01263 else 01264 { 01265 int lastSlash = path.findRev( '/' ); 01266 if ( lastSlash == -1) 01267 { 01268 // The first character is not a '/' ??? 01269 // This looks strange ... 01270 path = "/"; 01271 } 01272 else if ( path.right(1) != "/" ) 01273 path.truncate( lastSlash+1 ); // keep the "/" 01274 } 01275 if (m_strPath_encoded.isEmpty()) 01276 { 01277 path += tmp; 01278 setPath( path ); 01279 } 01280 else 01281 { 01282 path += encode_string(tmp); 01283 setEncodedPath( path ); 01284 } 01285 cleanPath(); 01286 } 01287 01288 void KURL::cleanPath( bool cleanDirSeparator ) // taken from the old KURL 01289 { 01290 if (m_iUriMode != URL) return; 01291 m_strPath = cleanpath(m_strPath, cleanDirSeparator, false); 01292 // WABA: Is this safe when "/../" is encoded with %? 01293 m_strPath_encoded = cleanpath(m_strPath_encoded, cleanDirSeparator, true); 01294 } 01295 01296 static TQString trailingSlash( int _trailing, const TQString &path ) 01297 { 01298 TQString result = path; 01299 01300 if ( _trailing == 0 ) 01301 return result; 01302 else if ( _trailing == 1 ) 01303 { 01304 int len = result.length(); 01305 if ( (len == 0) || (result[ len - 1 ] != (TQChar)'/') ) 01306 result += "/"; 01307 return result; 01308 } 01309 else if ( _trailing == -1 ) 01310 { 01311 if ( result == "/" ) 01312 return result; 01313 int len = result.length(); 01314 while (len > 1 && result[ len - 1 ] == (TQChar)'/') 01315 { 01316 len--; 01317 } 01318 result.truncate( len ); 01319 return result; 01320 } 01321 else { 01322 assert( 0 ); 01323 return TQString::null; 01324 } 01325 } 01326 01327 void KURL::adjustPath( int _trailing ) 01328 { 01329 if (!m_strPath_encoded.isEmpty()) 01330 { 01331 m_strPath_encoded = trailingSlash( _trailing, m_strPath_encoded ); 01332 } 01333 m_strPath = trailingSlash( _trailing, m_strPath ); 01334 } 01335 01336 01337 TQString KURL::encodedPathAndQuery( int _trailing, bool _no_empty_path, int encoding_hint ) const 01338 { 01339 TQString tmp; 01340 if (!m_strPath_encoded.isEmpty() && encoding_hint == 0) 01341 { 01342 tmp = trailingSlash( _trailing, m_strPath_encoded ); 01343 } 01344 else 01345 { 01346 tmp = path( _trailing ); 01347 if ( _no_empty_path && tmp.isEmpty() ) 01348 tmp = "/"; 01349 if (m_iUriMode == Mailto) 01350 { 01351 tmp = encode( tmp, 2, encoding_hint ); 01352 } 01353 else 01354 { 01355 tmp = encode( tmp, 1, encoding_hint ); 01356 } 01357 } 01358 01359 // TODO apply encoding_hint to the query 01360 if (!m_strQuery_encoded.isNull()) 01361 tmp += '?' + m_strQuery_encoded; 01362 return tmp; 01363 } 01364 01365 void KURL::setEncodedPath( const TQString& _txt, int encoding_hint ) 01366 { 01367 m_strPath_encoded = _txt; 01368 01369 decode( m_strPath_encoded, m_strPath, m_strPath_encoded, encoding_hint ); 01370 // Throw away encoding for local files, makes file-operations faster. 01371 if (m_strProtocol == fileProt) 01372 m_strPath_encoded = TQString::null; 01373 01374 if ( m_iUriMode == Auto ) 01375 m_iUriMode = URL; 01376 } 01377 01378 01379 void KURL::setEncodedPathAndQuery( const TQString& _txt, int encoding_hint ) 01380 { 01381 int pos = _txt.find( '?' ); 01382 if ( pos == -1 ) 01383 { 01384 setEncodedPath(_txt, encoding_hint); 01385 m_strQuery_encoded = TQString::null; 01386 } 01387 else 01388 { 01389 setEncodedPath(_txt.left( pos ), encoding_hint); 01390 _setQuery(_txt.right(_txt.length() - pos - 1), encoding_hint); 01391 } 01392 } 01393 01394 TQString KURL::path( int _trailing ) const 01395 { 01396 return trailingSlash( _trailing, path() ); 01397 } 01398 01399 bool KURL::isLocalFile() const 01400 { 01401 if ( (m_strProtocol != fileProt ) || hasSubURL() ) 01402 return false; 01403 01404 if (m_strHost.isEmpty() || (m_strHost == "localhost")) 01405 return true; 01406 01407 char hostname[ 256 ]; 01408 hostname[ 0 ] = '\0'; 01409 if (!gethostname( hostname, 255 )) 01410 hostname[sizeof(hostname)-1] = '\0'; 01411 01412 for(char *p = hostname; *p; p++) 01413 *p = tolower(*p); 01414 01415 return (m_strHost == hostname); 01416 } 01417 01418 void KURL::setFileEncoding(const TQString &encoding) 01419 { 01420 if (!isLocalFile()) 01421 return; 01422 01423 TQString q = query(); 01424 01425 if (!q.isEmpty() && (q[0] == '?')) 01426 q = q.mid(1); 01427 01428 TQStringList args = TQStringList::split('&', q); 01429 for(TQStringList::Iterator it = args.begin(); 01430 it != args.end();) 01431 { 01432 TQString s = decode_string(*it); 01433 if (s.startsWith("charset=")) 01434 it = args.erase(it); 01435 else 01436 ++it; 01437 } 01438 if (!encoding.isEmpty()) 01439 args.append("charset="+encode_string(encoding)); 01440 01441 if (args.isEmpty()) 01442 _setQuery(TQString::null); 01443 else 01444 _setQuery(args.join("&")); 01445 } 01446 01447 TQString KURL::fileEncoding() const 01448 { 01449 if (!isLocalFile()) 01450 return TQString::null; 01451 01452 TQString q = query(); 01453 01454 if (q.isEmpty()) 01455 return TQString::null; 01456 01457 if (q[0] == '?') 01458 q = q.mid(1); 01459 01460 TQStringList args = TQStringList::split('&', q); 01461 for(TQStringList::ConstIterator it = args.begin(); 01462 it != args.end(); 01463 ++it) 01464 { 01465 TQString s = decode_string(*it); 01466 if (s.startsWith("charset=")) 01467 return s.mid(8); 01468 } 01469 return TQString::null; 01470 } 01471 01472 bool KURL::hasSubURL() const 01473 { 01474 if ( m_strProtocol.isEmpty() || m_bIsMalformed ) 01475 return false; 01476 if (m_strRef_encoded.isEmpty()) 01477 return false; 01478 if (m_strRef_encoded.startsWith("gzip:")) 01479 return true; 01480 if (m_strRef_encoded.startsWith("bzip:")) 01481 return true; 01482 if (m_strRef_encoded.startsWith("bzip2:")) 01483 return true; 01484 if (m_strRef_encoded.startsWith("tar:")) 01485 return true; 01486 if (m_strRef_encoded.startsWith("ar:")) 01487 return true; 01488 if (m_strRef_encoded.startsWith("zip:")) 01489 return true; 01490 if (m_strRef_encoded.startsWith("lzma:")) 01491 return true; 01492 if (m_strRef_encoded.startsWith("xz:")) 01493 return true; 01494 if ( m_strProtocol == "error" ) // anything that starts with error: has suburls 01495 return true; 01496 return false; 01497 } 01498 01499 TQString KURL::url( int _trailing, int encoding_hint ) const 01500 { 01501 if( m_bIsMalformed ) 01502 { 01503 // Return the whole url even when the url is 01504 // malformed. Under such conditions the url 01505 // is stored in m_strProtocol. 01506 return m_strProtocol; 01507 } 01508 01509 TQString u = m_strProtocol; 01510 if (!u.isEmpty()) 01511 u += ":"; 01512 01513 if ( hasHost() || (m_strProtocol == fileProt) ) 01514 { 01515 u += "//"; 01516 if ( hasUser() ) 01517 { 01518 u += encode(m_strUser, 0, encoding_hint); 01519 if ( hasPass() ) 01520 { 01521 u += ":"; 01522 u += encode(m_strPass, 0, encoding_hint); 01523 } 01524 u += "@"; 01525 } 01526 if ( m_iUriMode == URL ) 01527 { 01528 bool IPv6 = (m_strHost.find(':') != -1); 01529 if (IPv6) 01530 u += '[' + m_strHost + ']'; 01531 else 01532 u += encodeHost(m_strHost, true, encoding_hint); 01533 if ( m_iPort != 0 ) { 01534 TQString buffer; 01535 buffer.sprintf( ":%u", m_iPort ); 01536 u += buffer; 01537 } 01538 } 01539 else 01540 { 01541 u += m_strHost; 01542 } 01543 } 01544 01545 if ( m_iUriMode == URL || m_iUriMode == Mailto ) 01546 u += encodedPathAndQuery( _trailing, false, encoding_hint ); 01547 else 01548 u += encode( m_strPath, 21, encoding_hint, true ); 01549 01550 if ( hasRef() ) 01551 { 01552 u += "#"; 01553 u += m_strRef_encoded; 01554 } 01555 01556 return u; 01557 } 01558 01559 TQString KURL::prettyURL( int _trailing ) const 01560 { 01561 if( m_bIsMalformed ) 01562 { 01563 // Return the whole url even when the url is 01564 // malformed. Under such conditions the url 01565 // is stored in m_strProtocol. 01566 return m_strProtocol; 01567 } 01568 01569 TQString u = m_strProtocol; 01570 if (!u.isEmpty()) 01571 u += ":"; 01572 01573 if ( hasHost() || (m_strProtocol == fileProt) ) 01574 { 01575 u += "//"; 01576 if ( hasUser() ) 01577 { 01578 u += encode(m_strUser, 0, 0); 01579 // Don't show password! 01580 u += "@"; 01581 } 01582 if ( m_iUriMode == URL ) 01583 { 01584 bool IPv6 = (m_strHost.find(':') != -1); 01585 if (IPv6) 01586 { 01587 u += '[' + m_strHost + ']'; 01588 } 01589 else 01590 { 01591 u += lazy_encode(m_strHost); 01592 } 01593 } 01594 else 01595 { 01596 u += lazy_encode(m_strHost); 01597 } 01598 if ( m_iPort != 0 ) { 01599 TQString buffer; 01600 buffer.sprintf( ":%u", m_iPort ); 01601 u += buffer; 01602 } 01603 } 01604 01605 if (m_iUriMode == Mailto) 01606 { 01607 u += lazy_encode( m_strPath, false ); 01608 } 01609 else 01610 { 01611 u += trailingSlash( _trailing, lazy_encode( m_strPath ) ); 01612 } 01613 01614 if (!m_strQuery_encoded.isNull()) 01615 u += '?' + m_strQuery_encoded; 01616 01617 if ( hasRef() ) 01618 { 01619 u += "#"; 01620 u += m_strRef_encoded; 01621 } 01622 01623 return u; 01624 } 01625 01626 TQString KURL::prettyURL( int _trailing, AdjustementFlags _flags) const 01627 { 01628 TQString u = prettyURL(_trailing); 01629 if (_flags & StripFileProtocol && u.startsWith("file://")) { 01630 u.remove(0, 7); 01631 #ifdef Q_WS_WIN 01632 return TQDir::convertSeparators(u); 01633 #endif 01634 } 01635 return u; 01636 } 01637 01638 TQString KURL::pathOrURL() const 01639 { 01640 if ( isLocalFile() && m_strRef_encoded.isNull() && m_strQuery_encoded.isNull() ) { 01641 return path(); 01642 } else { 01643 return prettyURL(); 01644 } 01645 } 01646 01647 TQString KURL::htmlURL() const 01648 { 01649 return TQStyleSheet::escape(prettyURL()); 01650 } 01651 01652 KURL::List KURL::split( const KURL& _url ) 01653 { 01654 TQString ref; 01655 KURL::List lst; 01656 KURL url = _url; 01657 01658 while(true) 01659 { 01660 KURL u = url; 01661 u.m_strRef_encoded = TQString::null; 01662 lst.append(u); 01663 if (url.hasSubURL()) 01664 { 01665 url = KURL(url.m_strRef_encoded); 01666 } 01667 else 01668 { 01669 ref = url.m_strRef_encoded; 01670 break; 01671 } 01672 } 01673 01674 // Set HTML ref in all URLs. 01675 KURL::List::Iterator it; 01676 for( it = lst.begin() ; it != lst.end(); ++it ) 01677 { 01678 (*it).m_strRef_encoded = ref; 01679 } 01680 01681 return lst; 01682 } 01683 01684 KURL::List KURL::split( const TQString& _url ) 01685 { 01686 return split(KURL(_url)); 01687 } 01688 01689 KURL KURL::join( const KURL::List & lst ) 01690 { 01691 if (lst.isEmpty()) return KURL(); 01692 KURL tmp; 01693 01694 KURL::List::ConstIterator first = lst.fromLast(); 01695 for( KURL::List::ConstIterator it = first; it != lst.end(); --it ) 01696 { 01697 KURL u(*it); 01698 if (it != first) 01699 { 01700 if (!u.m_strRef_encoded) u.m_strRef_encoded = tmp.url(); 01701 else u.m_strRef_encoded += "#" + tmp.url(); // Support more than one suburl thingy 01702 } 01703 tmp = u; 01704 } 01705 01706 return tmp; 01707 } 01708 01709 TQString KURL::fileName( bool _strip_trailing_slash ) const 01710 { 01711 TQString fname; 01712 if (hasSubURL()) { // If we have a suburl, then return the filename from there 01713 KURL::List list = KURL::split(*this); 01714 KURL::List::Iterator it = list.fromLast(); 01715 return (*it).fileName(_strip_trailing_slash); 01716 } 01717 const TQString &path = m_strPath; 01718 01719 int len = path.length(); 01720 if ( len == 0 ) 01721 return fname; 01722 01723 if ( _strip_trailing_slash ) 01724 { 01725 while ( len >= 1 && path[ len - 1 ] == TQChar('/') ) 01726 len--; 01727 } 01728 else if ( path[ len - 1 ] == TQChar('/') ) 01729 return fname; 01730 01731 // Does the path only consist of '/' characters ? 01732 if ( len == 1 && path[ 0 ] == TQChar('/') ) 01733 return fname; 01734 01735 // Skip last n slashes 01736 int n = 1; 01737 if (!m_strPath_encoded.isEmpty()) 01738 { 01739 // This is hairy, we need the last unencoded slash. 01740 // Count in the encoded string how many encoded slashes follow the last 01741 // unencoded one. 01742 int i = m_strPath_encoded.findRev( TQChar('/'), len - 1 ); 01743 TQString fileName_encoded = m_strPath_encoded.mid(i+1); 01744 n += fileName_encoded.contains("%2f", false); 01745 } 01746 int i = len; 01747 do { 01748 i = path.findRev( TQChar('/'), i - 1 ); 01749 } 01750 while (--n && (i > 0)); 01751 01752 // If ( i == -1 ) => the first character is not a '/' 01753 // So it's some URL like file:blah.tgz, return the whole path 01754 if ( i == -1 ) { 01755 if ( len == (int)path.length() ) 01756 fname = path; 01757 else 01758 // Might get here if _strip_trailing_slash is true 01759 fname = path.left( len ); 01760 } 01761 else 01762 { 01763 fname = path.mid( i + 1, len - i - 1 ); // TO CHECK 01764 } 01765 return fname; 01766 } 01767 01768 void KURL::addPath( const TQString& _txt ) 01769 { 01770 if (hasSubURL()) 01771 { 01772 KURL::List lst = split( *this ); 01773 KURL &u = lst.last(); 01774 u.addPath(_txt); 01775 *this = join( lst ); 01776 return; 01777 } 01778 01779 m_strPath_encoded = TQString::null; 01780 01781 if ( _txt.isEmpty() ) 01782 return; 01783 01784 int i = 0; 01785 int len = m_strPath.length(); 01786 // Add the trailing '/' if it is missing 01787 if ( _txt[0] != (TQChar)'/' && ( len == 0 || m_strPath[ len - 1 ] != (TQChar)'/' ) ) 01788 m_strPath += "/"; 01789 01790 // No double '/' characters 01791 i = 0; 01792 if ( len != 0 && m_strPath[ len - 1 ] == (TQChar)'/' ) 01793 { 01794 while( _txt[i] == (TQChar)'/' ) 01795 ++i; 01796 } 01797 01798 m_strPath += _txt.mid( i ); 01799 } 01800 01801 TQString KURL::directory( bool _strip_trailing_slash_from_result, 01802 bool _ignore_trailing_slash_in_path ) const 01803 { 01804 TQString result = m_strPath_encoded.isEmpty() ? m_strPath : m_strPath_encoded; 01805 if ( _ignore_trailing_slash_in_path ) 01806 result = trailingSlash( -1, result ); 01807 01808 if ( result.isEmpty() || result == "/" ) 01809 return result; 01810 01811 int i = result.findRev( "/" ); 01812 // If ( i == -1 ) => the first character is not a '/' 01813 // So it's some URL like file:blah.tgz, with no path 01814 if ( i == -1 ) 01815 return TQString::null; 01816 01817 if ( i == 0 ) 01818 { 01819 result = "/"; 01820 return result; 01821 } 01822 01823 if ( _strip_trailing_slash_from_result ) 01824 result = result.left( i ); 01825 else 01826 result = result.left( i + 1 ); 01827 01828 if (!m_strPath_encoded.isEmpty()) 01829 result = decode(result); 01830 01831 return result; 01832 } 01833 01834 01835 bool KURL::cd( const TQString& _dir ) 01836 { 01837 if ( _dir.isEmpty() || m_bIsMalformed ) 01838 return false; 01839 01840 if (hasSubURL()) 01841 { 01842 KURL::List lst = split( *this ); 01843 KURL &u = lst.last(); 01844 u.cd(_dir); 01845 *this = join( lst ); 01846 return true; 01847 } 01848 01849 // absolute path ? 01850 if ( _dir[0] == (TQChar)'/' ) 01851 { 01852 m_strPath_encoded = TQString::null; 01853 m_strPath = _dir; 01854 setHTMLRef( TQString::null ); 01855 m_strQuery_encoded = TQString::null; 01856 return true; 01857 } 01858 01859 // Users home directory on the local disk ? 01860 if ( ( _dir[0] == (TQChar)'~' ) && ( m_strProtocol == fileProt )) 01861 { 01862 m_strPath_encoded = TQString::null; 01863 m_strPath = TQDir::homeDirPath(); 01864 m_strPath += "/"; 01865 m_strPath += _dir.right(m_strPath.length() - 1); 01866 setHTMLRef( TQString::null ); 01867 m_strQuery_encoded = TQString::null; 01868 return true; 01869 } 01870 01871 // relative path 01872 // we always work on the past of the first url. 01873 // Sub URLs are not touched. 01874 01875 // append '/' if necessary 01876 TQString p = path(1); 01877 p += _dir; 01878 p = cleanpath( p, true, false ); 01879 setPath( p ); 01880 01881 setHTMLRef( TQString::null ); 01882 m_strQuery_encoded = TQString::null; 01883 01884 return true; 01885 } 01886 01887 KURL KURL::upURL( ) const 01888 { 01889 if (!query().isEmpty()) 01890 { 01891 KURL u(*this); 01892 u._setQuery(TQString::null); 01893 return u; 01894 }; 01895 01896 if (!hasSubURL()) 01897 { 01898 KURL u(*this); 01899 01900 u.cd("../"); 01901 01902 return u; 01903 } 01904 01905 // We have a subURL. 01906 KURL::List lst = split( *this ); 01907 if (lst.isEmpty()) 01908 return KURL(); // Huh? 01909 while (true) 01910 { 01911 KURL &u = lst.last(); 01912 TQString old = u.path(); 01913 u.cd("../"); 01914 if (u.path() != old) 01915 break; // Finshed. 01916 if (lst.count() == 1) 01917 break; // Finished. 01918 lst.remove(lst.fromLast()); 01919 } 01920 return join( lst ); 01921 } 01922 01923 TQString KURL::htmlRef() const 01924 { 01925 if ( !hasSubURL() ) 01926 { 01927 return decode( ref() ); 01928 } 01929 01930 List lst = split( *this ); 01931 return decode( (*lst.begin()).ref() ); 01932 } 01933 01934 TQString KURL::encodedHtmlRef() const 01935 { 01936 if ( !hasSubURL() ) 01937 { 01938 return ref(); 01939 } 01940 01941 List lst = split( *this ); 01942 return (*lst.begin()).ref(); 01943 } 01944 01945 void KURL::setHTMLRef( const TQString& _ref ) 01946 { 01947 if ( !hasSubURL() ) 01948 { 01949 m_strRef_encoded = encode( _ref, 0, 0 /*?*/); 01950 return; 01951 } 01952 01953 List lst = split( *this ); 01954 01955 (*lst.begin()).setRef( encode( _ref, 0, 0 /*?*/) ); 01956 01957 *this = join( lst ); 01958 } 01959 01960 bool KURL::hasHTMLRef() const 01961 { 01962 if ( !hasSubURL() ) 01963 { 01964 return hasRef(); 01965 } 01966 01967 List lst = split( *this ); 01968 return (*lst.begin()).hasRef(); 01969 } 01970 01971 void 01972 KURL::setProtocol( const TQString& _txt ) 01973 { 01974 m_strProtocol = _txt; 01975 if ( m_iUriMode == Auto ) m_iUriMode = uriModeForProtocol( m_strProtocol ); 01976 m_bIsMalformed = false; 01977 } 01978 01979 void 01980 KURL::setUser( const TQString& _txt ) 01981 { 01982 if ( _txt.isEmpty() ) 01983 m_strUser = TQString::null; 01984 else 01985 m_strUser = _txt; 01986 } 01987 01988 void 01989 KURL::setPass( const TQString& _txt ) 01990 { 01991 if ( _txt.isEmpty() ) 01992 m_strPass = TQString::null; 01993 else 01994 m_strPass = _txt; 01995 } 01996 01997 void 01998 KURL::setHost( const TQString& _txt ) 01999 { 02000 if ( m_iUriMode == Auto ) 02001 m_iUriMode = URL; 02002 switch ( m_iUriMode ) 02003 { 02004 case URL: 02005 #ifndef KDE_QT_ONLY 02006 m_strHost = KIDNA::toUnicode(_txt); 02007 if (m_strHost.isEmpty()) 02008 m_strHost = _txt.lower(); // Probably an invalid hostname, but... 02009 #else 02010 m_strHost = _txt.lower(); 02011 #endif 02012 break; 02013 default: 02014 m_strHost = _txt; 02015 break; 02016 } 02017 } 02018 02019 void 02020 KURL::setPort( unsigned short int _p ) 02021 { 02022 m_iPort = _p; 02023 } 02024 02025 void KURL::setPath( const TQString & path ) 02026 { 02027 if (isEmpty()) 02028 m_bIsMalformed = false; 02029 if (m_strProtocol.isEmpty()) 02030 { 02031 m_strProtocol = fileProt; 02032 } 02033 m_strPath = path; 02034 m_strPath_encoded = TQString::null; 02035 if ( m_iUriMode == Auto ) 02036 m_iUriMode = URL; 02037 } 02038 02039 void KURL::setDirectory( const TQString &dir) 02040 { 02041 if ( dir.endsWith("/")) 02042 setPath(dir); 02043 else 02044 setPath(dir+"/"); 02045 } 02046 02047 void KURL::setQuery( const TQString &_txt, int encoding_hint) 02048 { 02049 if (_txt[0] == (TQChar)'?') 02050 _setQuery( _txt.length() > 1 ? _txt.mid(1) : "" /*empty, not null*/, encoding_hint ); 02051 else 02052 _setQuery( _txt, encoding_hint ); 02053 } 02054 02055 // This is a private function that expects a query without '?' 02056 void KURL::_setQuery( const TQString &_txt, int encoding_hint) 02057 { 02058 m_strQuery_encoded = _txt; 02059 if (!_txt.length()) 02060 return; 02061 02062 int l = m_strQuery_encoded.length(); 02063 int i = 0; 02064 TQString result; 02065 while (i < l) 02066 { 02067 int s = i; 02068 // Re-encode. Break encoded string up according to the reserved 02069 // characters '&:;=/?' and re-encode part by part. 02070 while(i < l) 02071 { 02072 char c = m_strQuery_encoded[i].latin1(); 02073 if ((c == '&') || (c == ':') || (c == ';') || 02074 (c == '=') || (c == '/') || (c == '?')) 02075 break; 02076 i++; 02077 } 02078 if (i > s) 02079 { 02080 TQString tmp = m_strQuery_encoded.mid(s, i-s); 02081 TQString newTmp; 02082 decode( tmp, newTmp, tmp, encoding_hint, false ); 02083 result += tmp; 02084 } 02085 if (i < l) 02086 { 02087 result += m_strQuery_encoded[i]; 02088 i++; 02089 } 02090 } 02091 m_strQuery_encoded = result; 02092 } 02093 02094 TQString KURL::query() const 02095 { 02096 if (m_strQuery_encoded.isNull()) 02097 return TQString::null; 02098 return '?'+m_strQuery_encoded; 02099 } 02100 02101 TQString KURL::decode_string(const TQString &str, int encoding_hint) 02102 { 02103 return decode(str, encoding_hint); 02104 } 02105 02106 TQString KURL::encode_string(const TQString &str, int encoding_hint) 02107 { 02108 return encode(str, 1, encoding_hint); 02109 } 02110 02111 TQString KURL::encode_string_no_slash(const TQString &str, int encoding_hint) 02112 { 02113 return encode(str, 0, encoding_hint); 02114 } 02115 02116 bool urlcmp( const TQString& _url1, const TQString& _url2 ) 02117 { 02118 // Both empty ? 02119 if ( _url1.isEmpty() && _url2.isEmpty() ) 02120 return true; 02121 // Only one empty ? 02122 if ( _url1.isEmpty() || _url2.isEmpty() ) 02123 return false; 02124 02125 KURL::List list1 = KURL::split( _url1 ); 02126 KURL::List list2 = KURL::split( _url2 ); 02127 02128 // Malformed ? 02129 if ( list1.isEmpty() || list2.isEmpty() ) 02130 return false; 02131 02132 return ( list1 == list2 ); 02133 } 02134 02135 bool urlcmp( const TQString& _url1, const TQString& _url2, bool _ignore_trailing, bool _ignore_ref ) 02136 { 02137 // Both empty ? 02138 if ( _url1.isEmpty() && _url2.isEmpty() ) 02139 return true; 02140 // Only one empty ? 02141 if ( _url1.isEmpty() || _url2.isEmpty() ) 02142 return false; 02143 02144 KURL::List list1 = KURL::split( _url1 ); 02145 KURL::List list2 = KURL::split( _url2 ); 02146 02147 // Malformed ? 02148 if ( list1.isEmpty() || list2.isEmpty() ) 02149 return false; 02150 02151 unsigned int size = list1.count(); 02152 if ( list2.count() != size ) 02153 return false; 02154 02155 if ( _ignore_ref ) 02156 { 02157 (*list1.begin()).setRef(TQString::null); 02158 (*list2.begin()).setRef(TQString::null); 02159 } 02160 02161 KURL::List::Iterator it1 = list1.begin(); 02162 KURL::List::Iterator it2 = list2.begin(); 02163 for( ; it1 != list1.end() ; ++it1, ++it2 ) 02164 if ( !(*it1).equals( *it2, _ignore_trailing ) ) 02165 return false; 02166 02167 return true; 02168 } 02169 02170 TQMap< TQString, TQString > KURL::queryItems( int options ) const { 02171 return queryItems(options, 0); 02172 } 02173 02174 TQMap< TQString, TQString > KURL::queryItems( int options, int encoding_hint ) const { 02175 if ( m_strQuery_encoded.isEmpty() ) 02176 return TQMap<TQString,TQString>(); 02177 02178 TQMap< TQString, TQString > result; 02179 TQStringList items = TQStringList::split( '&', m_strQuery_encoded ); 02180 for ( TQStringList::const_iterator it = items.begin() ; it != items.end() ; ++it ) { 02181 int equal_pos = (*it).find( '=' ); 02182 if ( equal_pos > 0 ) { // = is not the first char... 02183 TQString name = (*it).left( equal_pos ); 02184 if ( options & CaseInsensitiveKeys ) 02185 name = name.lower(); 02186 TQString value = (*it).mid( equal_pos + 1 ); 02187 if ( value.isEmpty() ) 02188 result.insert( name, TQString::fromLatin1("") ); 02189 else { 02190 // ### why is decoding name not necessary? 02191 value.replace( '+', ' ' ); // + in queries means space 02192 result.insert( name, decode_string( value, encoding_hint ) ); 02193 } 02194 } else if ( equal_pos < 0 ) { // no = 02195 TQString name = (*it); 02196 if ( options & CaseInsensitiveKeys ) 02197 name = name.lower(); 02198 result.insert( name, TQString::null ); 02199 } 02200 } 02201 02202 return result; 02203 } 02204 02205 TQString KURL::queryItem( const TQString& _item ) const 02206 { 02207 return queryItem( _item, 0 ); 02208 } 02209 02210 TQString KURL::queryItem( const TQString& _item, int encoding_hint ) const 02211 { 02212 TQString item = _item + '='; 02213 if ( m_strQuery_encoded.length() <= 1 ) 02214 return TQString::null; 02215 02216 TQStringList items = TQStringList::split( '&', m_strQuery_encoded ); 02217 unsigned int _len = item.length(); 02218 for ( TQStringList::ConstIterator it = items.begin(); it != items.end(); ++it ) 02219 { 02220 if ( (*it).startsWith( item ) ) 02221 { 02222 if ( (*it).length() > _len ) 02223 { 02224 TQString str = (*it).mid( _len ); 02225 str.replace( '+', ' ' ); // + in queries means space. 02226 return decode_string( str, encoding_hint ); 02227 } 02228 else // empty value 02229 return TQString::fromLatin1(""); 02230 } 02231 } 02232 02233 return TQString::null; 02234 } 02235 02236 void KURL::removeQueryItem( const TQString& _item ) 02237 { 02238 TQString item = _item + '='; 02239 if ( m_strQuery_encoded.length() <= 1 ) 02240 return; 02241 02242 TQStringList items = TQStringList::split( '&', m_strQuery_encoded ); 02243 for ( TQStringList::Iterator it = items.begin(); it != items.end(); ) 02244 { 02245 if ( (*it).startsWith( item ) || (*it == _item) ) 02246 { 02247 TQStringList::Iterator deleteIt = it; 02248 ++it; 02249 items.remove(deleteIt); 02250 } 02251 else 02252 { 02253 ++it; 02254 } 02255 } 02256 m_strQuery_encoded = items.join( "&" ); 02257 } 02258 02259 void KURL::addQueryItem( const TQString& _item, const TQString& _value, int encoding_hint ) 02260 { 02261 TQString item = _item + '='; 02262 TQString value = encode( _value, 0, encoding_hint ); 02263 02264 if (!m_strQuery_encoded.isEmpty()) 02265 m_strQuery_encoded += '&'; 02266 m_strQuery_encoded += item + value; 02267 } 02268 02269 // static 02270 KURL KURL::fromPathOrURL( const TQString& text ) 02271 { 02272 if ( text.isEmpty() ) 02273 return KURL(); 02274 02275 KURL url; 02276 if (!TQDir::isRelativePath(text)) 02277 url.setPath( text ); 02278 else 02279 url = text; 02280 02281 return url; 02282 } 02283 02284 static TQString _relativePath(const TQString &base_dir, const TQString &path, bool &isParent) 02285 { 02286 TQString _base_dir(TQDir::cleanDirPath(base_dir)); 02287 TQString _path(TQDir::cleanDirPath(path.isEmpty() || (path[0] != (TQChar)'/') ? _base_dir+"/"+path : path)); 02288 02289 if (_base_dir.isEmpty()) 02290 return _path; 02291 02292 if (_base_dir[_base_dir.length()-1] != '/') 02293 _base_dir.append('/'); 02294 02295 TQStringList list1 = TQStringList::split('/', _base_dir); 02296 TQStringList list2 = TQStringList::split('/', _path); 02297 02298 // Find where they meet 02299 uint level = 0; 02300 uint maxLevel = TQMIN(list1.count(), list2.count()); 02301 while((level < maxLevel) && (list1[level] == list2[level])) level++; 02302 02303 TQString result; 02304 // Need to go down out of the first path to the common branch. 02305 for(uint i = level; i < list1.count(); i++) 02306 result.append("../"); 02307 02308 // Now up up from the common branch to the second path. 02309 for(uint i = level; i < list2.count(); i++) 02310 result.append(list2[i]).append("/"); 02311 02312 if ((level < list2.count()) && (path[path.length()-1] != (TQChar)'/')) 02313 result.truncate(result.length()-1); 02314 02315 isParent = (level == list1.count()); 02316 02317 return result; 02318 } 02319 02320 TQString KURL::relativePath(const TQString &base_dir, const TQString &path, bool *isParent) 02321 { 02322 bool parent = false; 02323 TQString result = _relativePath(base_dir, path, parent); 02324 if (parent) 02325 result.prepend("./"); 02326 02327 if (isParent) 02328 *isParent = parent; 02329 02330 return result; 02331 } 02332 02333 void KURL::setInternalReferenceURL( const TQString& url ) { 02334 d->m_strInternalReferenceURL = url; 02335 } 02336 02337 TQString KURL::internalReferenceURL( void ) const { 02338 return d->m_strInternalReferenceURL; 02339 } 02340 02341 TQString KURL::relativeURL(const KURL &base_url, const KURL &url, int encoding_hint) 02342 { 02343 if ((url.protocol() != base_url.protocol()) || 02344 (url.host() != base_url.host()) || 02345 (url.port() && url.port() != base_url.port()) || 02346 (url.hasUser() && url.user() != base_url.user()) || 02347 (url.hasPass() && url.pass() != base_url.pass())) 02348 { 02349 return url.url(0, encoding_hint); 02350 } 02351 02352 TQString relURL; 02353 02354 if ((base_url.path() != url.path()) || (base_url.query() != url.query())) 02355 { 02356 bool dummy; 02357 TQString basePath = base_url.directory(false, false); 02358 relURL = encode( _relativePath(basePath, url.path(), dummy), 1, encoding_hint); 02359 relURL += url.query(); 02360 } 02361 02362 if ( url.hasRef() ) 02363 { 02364 relURL += "#"; 02365 relURL += url.ref(); 02366 } 02367 02368 if ( relURL.isEmpty() ) 02369 return "./"; 02370 02371 return relURL; 02372 } 02373 02374 int KURL::uriMode() const 02375 { 02376 return m_iUriMode; 02377 } 02378 02379 KURL::URIMode KURL::uriModeForProtocol(const TQString& protocol) 02380 { 02381 #ifndef KDE_QT_ONLY 02382 KURL::URIMode mode = Auto; 02383 if (protocol == fileProt) 02384 return URL; 02385 if (TDEGlobal::_instance) 02386 mode = KProtocolInfo::uriParseMode(protocol); 02387 if (mode == Auto ) { 02388 #else 02389 KURL::URIMode mode = Auto; 02390 #endif 02391 if ( protocol == "ed2k" || protocol == "sig2dat" || protocol == "slsk" || protocol == "data" ) mode = RawURI; 02392 else if ( protocol == "mailto" ) mode = Mailto; 02393 else mode = URL; 02394 #ifndef KDE_QT_ONLY 02395 } 02396 #endif 02397 return mode; 02398 }