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 <kglobal.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++ ] = (QChar)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] == (QChar)' '))) // 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 = KGlobal::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 = KGlobal::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 bool KURL::isRelativeURL(const TQString &_url) 00404 { 00405 int len = _url.length(); 00406 if (!len) return true; // Very short relative URL. 00407 const TQChar *str = _url.unicode(); 00408 00409 // Absolute URL must start with alpha-character 00410 if (!isalpha(str[0].latin1())) 00411 return true; // Relative URL 00412 00413 for(int i = 1; i < len; i++) 00414 { 00415 char c = str[i].latin1(); // Note: non-latin1 chars return 0! 00416 if (c == ':') 00417 return false; // Absolute URL 00418 00419 // Protocol part may only contain alpha, digit, + or - 00420 if (!isalpha(c) && !isdigit(c) && (c != '+') && (c != '-')) 00421 return true; // Relative URL 00422 } 00423 // URL did not contain ':' 00424 return true; // Relative URL 00425 } 00426 00427 KURL::List::List(const KURL &url) 00428 { 00429 append( url ); 00430 } 00431 00432 KURL::List::List(const TQStringList &list) 00433 { 00434 for (TQStringList::ConstIterator it = list.begin(); 00435 it != list.end(); 00436 it++) 00437 { 00438 append( KURL(*it) ); 00439 } 00440 } 00441 00442 TQStringList KURL::List::toStringList() const 00443 { 00444 TQStringList lst; 00445 for( KURL::List::ConstIterator it = begin(); 00446 it != end(); 00447 it++) 00448 { 00449 lst.append( (*it).url() ); 00450 } 00451 return lst; 00452 } 00453 00454 00455 KURL::KURL() 00456 { 00457 reset(); 00458 } 00459 00460 KURL::~KURL() 00461 { 00462 } 00463 00464 00465 KURL::KURL( const TQString &url, int encoding_hint ) 00466 { 00467 reset(); 00468 parse( url, encoding_hint ); 00469 } 00470 00471 KURL::KURL( const char * url, int encoding_hint ) 00472 { 00473 reset(); 00474 parse( TQString::fromLatin1(url), encoding_hint ); 00475 } 00476 00477 KURL::KURL( const TQCString& url, int encoding_hint ) 00478 { 00479 reset(); 00480 parse( TQString::fromLatin1(url), encoding_hint ); 00481 } 00482 00483 KURL::KURL( const KURL& _u ) 00484 { 00485 *this = _u; 00486 } 00487 00488 TQDataStream & operator<< (TQDataStream & s, const KURL & a) 00489 { 00490 TQString QueryForWire=a.m_strQuery_encoded; 00491 if (!a.m_strQuery_encoded.isNull()) 00492 QueryForWire.prepend("?"); 00493 00494 s << a.m_strProtocol << a.m_strUser << a.m_strPass << a.m_strHost 00495 << a.m_strPath << a.m_strPath_encoded << QueryForWire << a.m_strRef_encoded 00496 << TQ_INT8(a.m_bIsMalformed ? 1 : 0) << a.m_iPort; 00497 return s; 00498 } 00499 00500 TQDataStream & operator>> (TQDataStream & s, KURL & a) 00501 { 00502 TQ_INT8 malf; 00503 TQString QueryFromWire; 00504 s >> a.m_strProtocol >> a.m_strUser >> a.m_strPass >> a.m_strHost 00505 >> a.m_strPath >> a.m_strPath_encoded >> QueryFromWire >> a.m_strRef_encoded 00506 >> malf >> a.m_iPort; 00507 a.m_bIsMalformed = (malf != 0); 00508 00509 if ( QueryFromWire.isNull() ) 00510 a.m_strQuery_encoded = TQString::null; 00511 else if ( QueryFromWire.length() == 1 ) // empty query 00512 a.m_strQuery_encoded = ""; 00513 else 00514 a.m_strQuery_encoded = QueryFromWire.mid(1); 00515 00516 a.m_iUriMode = KURL::uriModeForProtocol( a.m_strProtocol ); 00517 00518 return s; 00519 } 00520 00521 #ifndef QT_NO_NETWORKPROTOCOL 00522 KURL::KURL( const TQUrl &u ) 00523 { 00524 *this = u; 00525 } 00526 #endif 00527 00528 KURL::KURL( const KURL& _u, const TQString& _rel_url, int encoding_hint ) 00529 { 00530 if (_u.hasSubURL()) // Operate on the last suburl, not the first 00531 { 00532 KURL::List lst = split( _u ); 00533 KURL u(lst.last(), _rel_url, encoding_hint); 00534 lst.remove( lst.last() ); 00535 lst.append( u ); 00536 *this = join( lst ); 00537 return; 00538 } 00539 // WORKAROUND THE RFC 1606 LOOPHOLE THAT ALLOWS 00540 // http:/index.html AS A VALID SYNTAX FOR RELATIVE 00541 // URLS. ( RFC 2396 section 5.2 item # 3 ) 00542 TQString rUrl = _rel_url; 00543 int len = _u.m_strProtocol.length(); 00544 if ( !_u.m_strHost.isEmpty() && !rUrl.isEmpty() && 00545 rUrl.find( _u.m_strProtocol, 0, false ) == 0 && 00546 rUrl[len] == ':' && (rUrl[len+1] != '/' || 00547 (rUrl[len+1] == '/' && rUrl[len+2] != '/')) ) 00548 { 00549 rUrl.remove( 0, rUrl.find( ':' ) + 1 ); 00550 } 00551 00552 if ( rUrl.isEmpty() ) 00553 { 00554 *this = _u; 00555 } 00556 else if ( rUrl[0] == '#' ) 00557 { 00558 *this = _u; 00559 m_strRef_encoded = rUrl.mid(1); 00560 if ( m_strRef_encoded.isNull() ) 00561 m_strRef_encoded = ""; // we know there was an (empty) html ref, we saw the '#' 00562 } 00563 else if ( isRelativeURL( rUrl) ) 00564 { 00565 *this = _u; 00566 m_strQuery_encoded = TQString::null; 00567 m_strRef_encoded = TQString::null; 00568 if ( rUrl[0] == '/') 00569 { 00570 if ((rUrl.length() > 1) && (rUrl[1] == '/')) 00571 { 00572 m_strHost = TQString::null; 00573 // File protocol returns file:/// without host, strip // from rUrl 00574 if (_u.m_strProtocol == fileProt) 00575 rUrl.remove(0, 2); 00576 } 00577 m_strPath = TQString::null; 00578 m_strPath_encoded = TQString::null; 00579 } 00580 else if ( rUrl[0] != '?' ) 00581 { 00582 int pos = m_strPath.findRev( '/' ); 00583 if (pos >= 0) 00584 m_strPath.truncate(pos); 00585 m_strPath += '/'; 00586 if (!m_strPath_encoded.isEmpty()) 00587 { 00588 pos = m_strPath_encoded.findRev( '/' ); 00589 if (pos >= 0) 00590 m_strPath_encoded.truncate(pos); 00591 m_strPath_encoded += '/'; 00592 } 00593 } 00594 else 00595 { 00596 if ( m_strPath.isEmpty() ) 00597 m_strPath = '/'; 00598 } 00599 KURL tmp( url() + rUrl, encoding_hint); 00600 *this = tmp; 00601 cleanPath(false); 00602 } 00603 else 00604 { 00605 KURL tmp( rUrl, encoding_hint); 00606 *this = tmp; 00607 // Preserve userinfo if applicable. 00608 if (!_u.m_strUser.isEmpty() && m_strUser.isEmpty() && (_u.m_strHost == m_strHost) && (_u.m_strProtocol == m_strProtocol)) 00609 { 00610 m_strUser = _u.m_strUser; 00611 m_strPass = _u.m_strPass; 00612 } 00613 cleanPath(false); 00614 } 00615 } 00616 00617 void KURL::reset() 00618 { 00619 m_strProtocol = TQString::null; 00620 m_strUser = TQString::null; 00621 m_strPass = TQString::null; 00622 m_strHost = TQString::null; 00623 m_strPath = TQString::null; 00624 m_strPath_encoded = TQString::null; 00625 m_strQuery_encoded = TQString::null; 00626 m_strRef_encoded = TQString::null; 00627 m_bIsMalformed = true; 00628 m_iPort = 0; 00629 m_iUriMode = Auto; 00630 } 00631 00632 bool KURL::isEmpty() const 00633 { 00634 return (m_strPath.isEmpty() && m_strProtocol.isEmpty()); 00635 } 00636 00637 void KURL::parse( const TQString& _url, int encoding_hint ) 00638 { 00639 if ( _url.isEmpty() || m_iUriMode == Invalid ) 00640 { 00641 m_strProtocol = _url; 00642 m_iUriMode = Invalid; 00643 return; 00644 } 00645 00646 const TQChar* buf = _url.unicode(); 00647 const TQChar* orig = buf; 00648 uint len = _url.length(); 00649 uint pos = 0; 00650 00651 // Node 1: Accept alpha or slash 00652 TQChar x = buf[pos++]; 00653 #ifdef Q_WS_WIN 00654 /* win32: accept <letter>: or <letter>:/ or <letter>:\ */ 00655 const bool alpha = isalpha((int)x); 00656 if (alpha && len<2) 00657 goto NodeErr; 00658 if (alpha && buf[pos]==':' && (len==2 || (len>2 && (buf[pos+1]=='/' || buf[pos+1]=='\\')))) 00659 #else 00660 if ( x == (QChar)'/' ) 00661 #endif 00662 { 00663 // A slash means we immediately proceed to parse it as a file URL. 00664 m_iUriMode = URL; 00665 m_strProtocol = fileProt; 00666 parseURL( _url, encoding_hint ); 00667 return; 00668 } 00669 if ( !isalpha( (int)x ) ) 00670 goto NodeErr; 00671 00672 // Node 2: Accept any amount of (alpha|digit|'+'|'-') 00673 // '.' is not currently accepted, because current KURL may be confused. 00674 // Proceed with :// :/ or : 00675 while( pos < len && (isalpha((int)buf[pos]) || isdigit((int)buf[pos]) || 00676 buf[pos] == (QChar)'+' || buf[pos] == (QChar)'-')) pos++; 00677 00678 if (pos < len && buf[pos] == (QChar)':' ) 00679 { 00680 m_strProtocol = TQString( orig, pos ).lower(); 00681 if ( m_iUriMode == Auto ) 00682 m_iUriMode = uriModeForProtocol( m_strProtocol ); 00683 // Proceed to correct parse function. 00684 switch ( m_iUriMode ) 00685 { 00686 case RawURI: 00687 parseRawURI( _url ); 00688 return; 00689 case Mailto: 00690 parseMailto( _url ); 00691 return; 00692 case URL: 00693 parseURL( _url, encoding_hint ); 00694 return; 00695 default: 00696 // Unknown URI mode results in an invalid URI. 00697 break; 00698 } 00699 } 00700 00701 NodeErr: 00702 reset(); 00703 m_strProtocol = _url; 00704 m_iUriMode = Invalid; 00705 } 00706 00707 void KURL::parseRawURI( const TQString& _url, int encoding_hint ) 00708 { 00709 uint len = _url.length(); 00710 const TQChar* buf = _url.unicode(); 00711 00712 uint pos = 0; 00713 00714 // Accept any amount of (alpha|digit|'+'|'-') 00715 // '.' is not currently accepted, because current KURL may be confused. 00716 // Proceed with : 00717 while( pos < len && (isalpha((int)buf[pos]) || isdigit((int)buf[pos]) || 00718 buf[pos] == (QChar)'+' || buf[pos] == (QChar)'-')) pos++; 00719 00720 // Note that m_strProtocol is already set here, so we just skip over the protocol. 00721 if (pos < len && buf[pos] == (QChar)':' ) 00722 pos++; 00723 else { // can't happen, the caller checked all this already 00724 reset(); 00725 m_strProtocol = _url; 00726 m_iUriMode = Invalid; 00727 return; 00728 } 00729 00730 if ( pos == len ) // can't happen, the caller checked this already 00731 m_strPath = TQString::null; 00732 else 00733 m_strPath = decode( TQString( buf + pos, len - pos ), encoding_hint, true ); 00734 00735 m_bIsMalformed = false; 00736 00737 return; 00738 } 00739 00740 void KURL::parseMailto( const TQString& _url, int encoding_hint ) 00741 { 00742 parseURL( _url, encoding_hint); 00743 if ( m_bIsMalformed ) 00744 return; 00745 TQRegExp mailre("(.+@)(.+)"); 00746 if ( mailre.exactMatch( m_strPath ) ) 00747 { 00748 #ifndef KDE_QT_ONLY 00749 TQString host = KIDNA::toUnicode( mailre.cap( 2 ) ); 00750 if (host.isEmpty()) 00751 host = TQString(mailre.cap( 2 )).lower(); 00752 #else 00753 TQString host = TQString(mailre.cap( 2 )).lower(); 00754 #endif 00755 m_strPath = mailre.cap( 1 ) + host; 00756 } 00757 } 00758 00759 void KURL::parseURL( const TQString& _url, int encoding_hint ) 00760 { 00761 TQString port; 00762 bool badHostName = false; 00763 int start = 0; 00764 uint len = _url.length(); 00765 const TQChar* buf = _url.unicode(); 00766 00767 TQChar delim; 00768 TQString tmp; 00769 00770 uint pos = 0; 00771 00772 // Node 1: Accept alpha or slash 00773 TQChar x = buf[pos++]; 00774 #ifdef Q_WS_WIN 00775 /* win32: accept <letter>: or <letter>:/ or <letter>:\ */ 00776 const bool alpha = isalpha((int)x); 00777 if (alpha && len<2) 00778 goto NodeErr; 00779 if (alpha && buf[pos]==(QChar)':' && (len==2 || (len>2 && (buf[pos+1]==(QChar)'/' || buf[pos+1]==(QChar)'\\')))) 00780 #else 00781 if ( x == (QChar)'/' ) 00782 #endif 00783 goto Node9; 00784 if ( !isalpha( (int)x ) ) 00785 goto NodeErr; 00786 00787 // Node 2: Accept any amount of (alpha|digit|'+'|'-') 00788 // '.' is not currently accepted, because current KURL may be confused. 00789 // Proceed with :// :/ or : 00790 while( pos < len && (isalpha((int)buf[pos]) || isdigit((int)buf[pos]) || 00791 buf[pos] == (QChar)'+' || buf[pos] == (QChar)'-')) pos++; 00792 00793 // Note that m_strProtocol is already set here, so we just skip over the protocol. 00794 if ( pos+2 < len && buf[pos] == (QChar)':' && buf[pos+1] == (QChar)'/' && buf[pos+2] == (QChar)'/' ) 00795 { 00796 pos += 3; 00797 } 00798 else if (pos+1 < len && buf[pos] == (QChar)':' ) // Need to always compare length()-1 otherwise KURL passes "http:" as legal!! 00799 { 00800 pos++; 00801 start = pos; 00802 goto Node9; 00803 } 00804 else 00805 goto NodeErr; 00806 00807 //Node 3: We need at least one character here 00808 if ( pos == len ) 00809 goto NodeErr; 00810 start = pos; 00811 00812 // Node 4: Accept any amount of characters. 00813 if (buf[pos] == (QChar)'[') // An IPv6 host follows. 00814 goto Node8; 00815 // Terminate on / or @ or ? or # or " or ; or < 00816 x = buf[pos]; 00817 while( (x != (QChar)':') && (x != (QChar)'@') && (x != (QChar)'/') && (x != (QChar)'?') && (x != (QChar)'#') ) 00818 { 00819 if ((x == (QChar)'\"') || (x == (QChar)';') || (x == (QChar)'<')) 00820 badHostName = true; 00821 if (++pos == len) 00822 break; 00823 x = buf[pos]; 00824 } 00825 if ( pos == len ) 00826 { 00827 if (badHostName) 00828 goto NodeErr; 00829 00830 setHost(decode(TQString( buf + start, pos - start ), encoding_hint)); 00831 goto NodeOk; 00832 } 00833 if ( x == (QChar)'@' ) 00834 { 00835 m_strUser = decode(TQString( buf + start, pos - start ), encoding_hint); 00836 pos++; 00837 goto Node7; 00838 } 00839 else if ( (x == (QChar)'/') || (x == (QChar)'?') || (x == (QChar)'#')) 00840 { 00841 if (badHostName) 00842 goto NodeErr; 00843 00844 setHost(decode(TQString( buf + start, pos - start ), encoding_hint)); 00845 start = pos; 00846 goto Node9; 00847 } 00848 else if ( x != (QChar)':' ) 00849 goto NodeErr; 00850 m_strUser = decode(TQString( buf + start, pos - start ), encoding_hint); 00851 pos++; 00852 00853 // Node 5: We need at least one character 00854 if ( pos == len ) 00855 goto NodeErr; 00856 start = pos++; 00857 00858 // Node 6: Read everything until @, /, ? or # 00859 while( (pos < len) && 00860 (buf[pos] != (QChar)'@') && 00861 (buf[pos] != (QChar)'/') && 00862 (buf[pos] != (QChar)'?') && 00863 (buf[pos] != (QChar)'#')) pos++; 00864 // If we now have a '@' the ':' seperates user and password. 00865 // Otherwise it seperates host and port. 00866 if ( (pos == len) || (buf[pos] != (QChar)'@') ) 00867 { 00868 // Ok the : was used to separate host and port 00869 if (badHostName) 00870 goto NodeErr; 00871 setHost(m_strUser); 00872 m_strUser = TQString::null; 00873 TQString tmp( buf + start, pos - start ); 00874 char *endptr; 00875 m_iPort = (unsigned short int)strtol(tmp.ascii(), &endptr, 10); 00876 if ((pos == len) && (strlen(endptr) == 0)) 00877 goto NodeOk; 00878 // there is more after the digits 00879 pos -= strlen(endptr); 00880 if ((buf[pos] != (QChar)'@') && 00881 (buf[pos] != (QChar)'/') && 00882 (buf[pos] != (QChar)'?') && 00883 (buf[pos] != (QChar)'#')) 00884 goto NodeErr; 00885 00886 start = pos; 00887 goto Node9; 00888 } 00889 m_strPass = decode(TQString( buf + start, pos - start), encoding_hint); 00890 pos++; 00891 00892 // Node 7: We need at least one character 00893 Node7: 00894 if ( pos == len ) 00895 goto NodeErr; 00896 00897 Node8: 00898 if (buf[pos] == (QChar)'[') 00899 { 00900 // IPv6 address 00901 start = ++pos; // Skip '[' 00902 00903 if (pos == len) 00904 { 00905 badHostName = true; 00906 goto NodeErr; 00907 } 00908 // Node 8a: Read everything until ] or terminate 00909 badHostName = false; 00910 x = buf[pos]; 00911 while( (x != (QChar)']') ) 00912 { 00913 if ((x == (QChar)'\"') || (x == (QChar)';') || (x == (QChar)'<')) 00914 badHostName = true; 00915 if (++pos == len) 00916 { 00917 badHostName = true; 00918 break; 00919 } 00920 x = buf[pos]; 00921 } 00922 if (badHostName) 00923 goto NodeErr; 00924 setHost(decode(TQString( buf + start, pos - start ), encoding_hint)); 00925 if (pos < len) pos++; // Skip ']' 00926 if (pos == len) 00927 goto NodeOk; 00928 } 00929 else 00930 { 00931 // Non IPv6 address, with a user 00932 start = pos; 00933 00934 // Node 8b: Read everything until / : or terminate 00935 badHostName = false; 00936 x = buf[pos]; 00937 while( (x != (QChar)':') && (x != (QChar)'@') && (x != (QChar)'/') && (x != (QChar)'?') && (x != (QChar)'#') ) 00938 { 00939 if ((x == (QChar)'\"') || (x == (QChar)';') || (x == (QChar)'<')) 00940 badHostName = true; 00941 if (++pos == len) 00942 break; 00943 x = buf[pos]; 00944 } 00945 if (badHostName) 00946 goto NodeErr; 00947 if ( pos == len ) 00948 { 00949 setHost(decode(TQString( buf + start, pos - start ), encoding_hint)); 00950 goto NodeOk; 00951 } 00952 setHost(decode(TQString( buf + start, pos - start ), encoding_hint)); 00953 } 00954 x = buf[pos]; 00955 if ( x == (QChar)'/' || x == (QChar)'#' || x == (QChar)'?' ) 00956 { 00957 start = pos; 00958 goto Node9; 00959 } 00960 else if ( x != (QChar)':' ) 00961 goto NodeErr; 00962 pos++; 00963 00964 // Node 8c: Accept at least one digit 00965 if ( pos == len ) 00966 goto NodeErr; 00967 start = pos; 00968 if ( !isdigit( buf[pos++] ) ) 00969 goto NodeErr; 00970 00971 // Node 8d: Accept any amount of digits 00972 while( pos < len && isdigit( buf[pos] ) ) pos++; 00973 port = TQString( buf + start, pos - start ); 00974 m_iPort = port.toUShort(); 00975 if ( pos == len ) 00976 goto NodeOk; 00977 start = pos; 00978 00979 Node9: // parse path until query or reference reached 00980 00981 while( pos < len && buf[pos] != (QChar)'#' && buf[pos]!=(QChar)'?' ) pos++; 00982 00983 tmp = TQString( buf + start, pos - start ); 00984 //kdDebug(126)<<" setting encoded path to:"<<tmp<<endl; 00985 setEncodedPath( tmp, encoding_hint ); 00986 00987 if ( pos == len ) 00988 goto NodeOk; 00989 00990 //Node10: // parse query or reference depending on what comes first 00991 delim = (buf[pos++]==(QChar)'#'?(QChar)'?':(QChar)'#'); 00992 00993 start = pos; 00994 00995 while(pos < len && buf[pos]!=delim ) pos++; 00996 00997 tmp = TQString(buf + start, pos - start); 00998 if (delim==(QChar)'#') 00999 _setQuery(tmp, encoding_hint); 01000 else 01001 m_strRef_encoded = tmp; 01002 01003 if (pos == len) 01004 goto NodeOk; 01005 01006 //Node11: // feed the rest into the remaining variable 01007 tmp = TQString( buf + pos + 1, len - pos - 1); 01008 if (delim == (QChar)'#') 01009 m_strRef_encoded = tmp; 01010 else 01011 _setQuery(tmp, encoding_hint); 01012 01013 NodeOk: 01014 //kdDebug(126)<<"parsing finished. m_strProtocol="<<m_strProtocol<<" m_strHost="<<m_strHost<<" m_strPath="<<m_strPath<<endl; 01015 m_bIsMalformed = false; // Valid URL 01016 01017 //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; 01018 if (m_strProtocol.isEmpty()) 01019 { 01020 m_iUriMode = URL; 01021 m_strProtocol = fileProt; 01022 } 01023 return; 01024 01025 NodeErr: 01026 // kdDebug(126) << "KURL couldn't parse URL \"" << _url << "\"" << endl; 01027 reset(); 01028 m_strProtocol = _url; 01029 m_iUriMode = Invalid; 01030 } 01031 01032 KURL& KURL::operator=( const TQString& _url ) 01033 { 01034 reset(); 01035 parse( _url ); 01036 01037 return *this; 01038 } 01039 01040 KURL& KURL::operator=( const char * _url ) 01041 { 01042 reset(); 01043 parse( TQString::fromLatin1(_url) ); 01044 01045 return *this; 01046 } 01047 01048 #ifndef QT_NO_NETWORKPROTOCOL 01049 KURL& KURL::operator=( const TQUrl & u ) 01050 { 01051 m_strProtocol = u.protocol(); 01052 m_iUriMode = Auto; 01053 m_strUser = u.user(); 01054 m_strPass = u.password(); 01055 m_strHost = u.host(); 01056 m_strPath = u.path( false ); 01057 m_strPath_encoded = TQString::null; 01058 m_strQuery_encoded = u.query(); 01059 m_strRef_encoded = u.ref(); 01060 m_bIsMalformed = !u.isValid(); 01061 m_iPort = u.port(); 01062 01063 return *this; 01064 } 01065 #endif 01066 01067 KURL& KURL::operator=( const KURL& _u ) 01068 { 01069 m_strProtocol = _u.m_strProtocol; 01070 m_strUser = _u.m_strUser; 01071 m_strPass = _u.m_strPass; 01072 m_strHost = _u.m_strHost; 01073 m_strPath = _u.m_strPath; 01074 m_strPath_encoded = _u.m_strPath_encoded; 01075 m_strQuery_encoded = _u.m_strQuery_encoded; 01076 m_strRef_encoded = _u.m_strRef_encoded; 01077 m_bIsMalformed = _u.m_bIsMalformed; 01078 m_iPort = _u.m_iPort; 01079 m_iUriMode = _u.m_iUriMode; 01080 01081 return *this; 01082 } 01083 01084 bool KURL::operator<( const KURL& _u) const 01085 { 01086 int i; 01087 if (!_u.isValid()) 01088 { 01089 if (!isValid()) 01090 { 01091 i = m_strProtocol.compare(_u.m_strProtocol); 01092 return (i < 0); 01093 } 01094 return false; 01095 } 01096 if (!isValid()) 01097 return true; 01098 01099 i = m_strProtocol.compare(_u.m_strProtocol); 01100 if (i) return (i < 0); 01101 01102 i = m_strHost.compare(_u.m_strHost); 01103 if (i) return (i < 0); 01104 01105 if (m_iPort != _u.m_iPort) return (m_iPort < _u.m_iPort); 01106 01107 i = m_strPath.compare(_u.m_strPath); 01108 if (i) return (i < 0); 01109 01110 i = m_strQuery_encoded.compare(_u.m_strQuery_encoded); 01111 if (i) return (i < 0); 01112 01113 i = m_strRef_encoded.compare(_u.m_strRef_encoded); 01114 if (i) return (i < 0); 01115 01116 i = m_strUser.compare(_u.m_strUser); 01117 if (i) return (i < 0); 01118 01119 i = m_strPass.compare(_u.m_strPass); 01120 if (i) return (i < 0); 01121 01122 return false; 01123 } 01124 01125 bool KURL::operator==( const KURL& _u ) const 01126 { 01127 if ( !isValid() || !_u.isValid() ) 01128 return false; 01129 01130 if ( m_strProtocol == _u.m_strProtocol && 01131 m_strUser == _u.m_strUser && 01132 m_strPass == _u.m_strPass && 01133 m_strHost == _u.m_strHost && 01134 m_strPath == _u.m_strPath && 01135 // The encoded path may be null, but the URLs are still equal (David) 01136 ( m_strPath_encoded.isNull() || _u.m_strPath_encoded.isNull() || 01137 m_strPath_encoded == _u.m_strPath_encoded ) && 01138 m_strQuery_encoded == _u.m_strQuery_encoded && 01139 m_strRef_encoded == _u.m_strRef_encoded && 01140 m_iPort == _u.m_iPort ) 01141 { 01142 return true; 01143 } 01144 01145 return false; 01146 } 01147 01148 bool KURL::operator==( const TQString& _u ) const 01149 { 01150 KURL u( _u ); 01151 return ( *this == u ); 01152 } 01153 01154 bool KURL::cmp( const KURL &u, bool ignore_trailing ) const 01155 { 01156 return equals( u, ignore_trailing ); 01157 } 01158 01159 bool KURL::equals( const KURL &_u, bool ignore_trailing ) const 01160 { 01161 if ( !isValid() || !_u.isValid() ) 01162 return false; 01163 01164 if ( ignore_trailing ) 01165 { 01166 TQString path1 = path(1); 01167 TQString path2 = _u.path(1); 01168 if ( path1 != path2 ) 01169 return false; 01170 01171 if ( m_strProtocol == _u.m_strProtocol && 01172 m_strUser == _u.m_strUser && 01173 m_strPass == _u.m_strPass && 01174 m_strHost == _u.m_strHost && 01175 m_strQuery_encoded == _u.m_strQuery_encoded && 01176 m_strRef_encoded == _u.m_strRef_encoded && 01177 m_iPort == _u.m_iPort ) 01178 return true; 01179 01180 return false; 01181 } 01182 01183 return ( *this == _u ); 01184 } 01185 01186 bool KURL::isParentOf( const KURL& _u ) const 01187 { 01188 if ( !isValid() || !_u.isValid() ) 01189 return false; 01190 01191 if ( m_strProtocol == _u.m_strProtocol && 01192 m_strUser == _u.m_strUser && 01193 m_strPass == _u.m_strPass && 01194 m_strHost == _u.m_strHost && 01195 m_strQuery_encoded == _u.m_strQuery_encoded && 01196 m_strRef_encoded == _u.m_strRef_encoded && 01197 m_iPort == _u.m_iPort ) 01198 { 01199 if ( path().isEmpty() || _u.path().isEmpty() ) 01200 return false; // can't work with implicit paths 01201 01202 TQString p1( cleanpath( path(), true, false ) ); 01203 if ( p1[p1.length()-1] != '/' ) 01204 p1 += '/'; 01205 TQString p2( cleanpath( _u.path(), true, false ) ); 01206 if ( p2[p2.length()-1] != '/' ) 01207 p2 += '/'; 01208 01209 //kdDebug(126) << "p1=" << p1 << endl; 01210 //kdDebug(126) << "p2=" << p2 << endl; 01211 //kdDebug(126) << "p1.length()=" << p1.length() << endl; 01212 //kdDebug(126) << "p2.left(!$)=" << p2.left( p1.length() ) << endl; 01213 return p2.startsWith( p1 ); 01214 } 01215 return false; 01216 } 01217 01218 void KURL::setFileName( const TQString& _txt ) 01219 { 01220 m_strRef_encoded = TQString::null; 01221 int i = 0; 01222 while( _txt[i] == (QChar)'/' ) ++i; 01223 TQString tmp; 01224 if ( i ) 01225 tmp = _txt.mid( i ); 01226 else 01227 tmp = _txt; 01228 01229 TQString path = m_strPath_encoded.isEmpty() ? m_strPath : m_strPath_encoded; 01230 if ( path.isEmpty() ) 01231 path = "/"; 01232 else 01233 { 01234 int lastSlash = path.findRev( '/' ); 01235 if ( lastSlash == -1) 01236 { 01237 // The first character is not a '/' ??? 01238 // This looks strange ... 01239 path = "/"; 01240 } 01241 else if ( path.right(1) != "/" ) 01242 path.truncate( lastSlash+1 ); // keep the "/" 01243 } 01244 if (m_strPath_encoded.isEmpty()) 01245 { 01246 path += tmp; 01247 setPath( path ); 01248 } 01249 else 01250 { 01251 path += encode_string(tmp); 01252 setEncodedPath( path ); 01253 } 01254 cleanPath(); 01255 } 01256 01257 void KURL::cleanPath( bool cleanDirSeparator ) // taken from the old KURL 01258 { 01259 if (m_iUriMode != URL) return; 01260 m_strPath = cleanpath(m_strPath, cleanDirSeparator, false); 01261 // WABA: Is this safe when "/../" is encoded with %? 01262 m_strPath_encoded = cleanpath(m_strPath_encoded, cleanDirSeparator, true); 01263 } 01264 01265 static TQString trailingSlash( int _trailing, const TQString &path ) 01266 { 01267 TQString result = path; 01268 01269 if ( _trailing == 0 ) 01270 return result; 01271 else if ( _trailing == 1 ) 01272 { 01273 int len = result.length(); 01274 if ( (len == 0) || (result[ len - 1 ] != (QChar)'/') ) 01275 result += "/"; 01276 return result; 01277 } 01278 else if ( _trailing == -1 ) 01279 { 01280 if ( result == "/" ) 01281 return result; 01282 int len = result.length(); 01283 while (len > 1 && result[ len - 1 ] == (QChar)'/') 01284 { 01285 len--; 01286 } 01287 result.truncate( len ); 01288 return result; 01289 } 01290 else { 01291 assert( 0 ); 01292 return TQString::null; 01293 } 01294 } 01295 01296 void KURL::adjustPath( int _trailing ) 01297 { 01298 if (!m_strPath_encoded.isEmpty()) 01299 { 01300 m_strPath_encoded = trailingSlash( _trailing, m_strPath_encoded ); 01301 } 01302 m_strPath = trailingSlash( _trailing, m_strPath ); 01303 } 01304 01305 01306 TQString KURL::encodedPathAndQuery( int _trailing, bool _no_empty_path, int encoding_hint ) const 01307 { 01308 TQString tmp; 01309 if (!m_strPath_encoded.isEmpty() && encoding_hint == 0) 01310 { 01311 tmp = trailingSlash( _trailing, m_strPath_encoded ); 01312 } 01313 else 01314 { 01315 tmp = path( _trailing ); 01316 if ( _no_empty_path && tmp.isEmpty() ) 01317 tmp = "/"; 01318 if (m_iUriMode == Mailto) 01319 { 01320 tmp = encode( tmp, 2, encoding_hint ); 01321 } 01322 else 01323 { 01324 tmp = encode( tmp, 1, encoding_hint ); 01325 } 01326 } 01327 01328 // TODO apply encoding_hint to the query 01329 if (!m_strQuery_encoded.isNull()) 01330 tmp += '?' + m_strQuery_encoded; 01331 return tmp; 01332 } 01333 01334 void KURL::setEncodedPath( const TQString& _txt, int encoding_hint ) 01335 { 01336 m_strPath_encoded = _txt; 01337 01338 decode( m_strPath_encoded, m_strPath, m_strPath_encoded, encoding_hint ); 01339 // Throw away encoding for local files, makes file-operations faster. 01340 if (m_strProtocol == fileProt) 01341 m_strPath_encoded = TQString::null; 01342 01343 if ( m_iUriMode == Auto ) 01344 m_iUriMode = URL; 01345 } 01346 01347 01348 void KURL::setEncodedPathAndQuery( const TQString& _txt, int encoding_hint ) 01349 { 01350 int pos = _txt.find( '?' ); 01351 if ( pos == -1 ) 01352 { 01353 setEncodedPath(_txt, encoding_hint); 01354 m_strQuery_encoded = TQString::null; 01355 } 01356 else 01357 { 01358 setEncodedPath(_txt.left( pos ), encoding_hint); 01359 _setQuery(_txt.right(_txt.length() - pos - 1), encoding_hint); 01360 } 01361 } 01362 01363 TQString KURL::path( int _trailing ) const 01364 { 01365 return trailingSlash( _trailing, path() ); 01366 } 01367 01368 bool KURL::isLocalFile() const 01369 { 01370 if ( (m_strProtocol != fileProt ) || hasSubURL() ) 01371 return false; 01372 01373 if (m_strHost.isEmpty() || (m_strHost == "localhost")) 01374 return true; 01375 01376 char hostname[ 256 ]; 01377 hostname[ 0 ] = '\0'; 01378 if (!gethostname( hostname, 255 )) 01379 hostname[sizeof(hostname)-1] = '\0'; 01380 01381 for(char *p = hostname; *p; p++) 01382 *p = tolower(*p); 01383 01384 return (m_strHost == hostname); 01385 } 01386 01387 void KURL::setFileEncoding(const TQString &encoding) 01388 { 01389 if (!isLocalFile()) 01390 return; 01391 01392 TQString q = query(); 01393 01394 if (!q.isEmpty() && (q[0] == '?')) 01395 q = q.mid(1); 01396 01397 TQStringList args = TQStringList::split('&', q); 01398 for(TQStringList::Iterator it = args.begin(); 01399 it != args.end();) 01400 { 01401 TQString s = decode_string(*it); 01402 if (s.startsWith("charset=")) 01403 it = args.erase(it); 01404 else 01405 ++it; 01406 } 01407 if (!encoding.isEmpty()) 01408 args.append("charset="+encode_string(encoding)); 01409 01410 if (args.isEmpty()) 01411 _setQuery(TQString::null); 01412 else 01413 _setQuery(args.join("&")); 01414 } 01415 01416 TQString KURL::fileEncoding() const 01417 { 01418 if (!isLocalFile()) 01419 return TQString::null; 01420 01421 TQString q = query(); 01422 01423 if (q.isEmpty()) 01424 return TQString::null; 01425 01426 if (q[0] == '?') 01427 q = q.mid(1); 01428 01429 TQStringList args = TQStringList::split('&', q); 01430 for(TQStringList::ConstIterator it = args.begin(); 01431 it != args.end(); 01432 ++it) 01433 { 01434 TQString s = decode_string(*it); 01435 if (s.startsWith("charset=")) 01436 return s.mid(8); 01437 } 01438 return TQString::null; 01439 } 01440 01441 bool KURL::hasSubURL() const 01442 { 01443 if ( m_strProtocol.isEmpty() || m_bIsMalformed ) 01444 return false; 01445 if (m_strRef_encoded.isEmpty()) 01446 return false; 01447 if (m_strRef_encoded.startsWith("gzip:")) 01448 return true; 01449 if (m_strRef_encoded.startsWith("bzip:")) 01450 return true; 01451 if (m_strRef_encoded.startsWith("bzip2:")) 01452 return true; 01453 if (m_strRef_encoded.startsWith("tar:")) 01454 return true; 01455 if (m_strRef_encoded.startsWith("ar:")) 01456 return true; 01457 if (m_strRef_encoded.startsWith("zip:")) 01458 return true; 01459 if (m_strRef_encoded.startsWith("lzma:")) 01460 return true; 01461 if (m_strRef_encoded.startsWith("xz:")) 01462 return true; 01463 if ( m_strProtocol == "error" ) // anything that starts with error: has suburls 01464 return true; 01465 return false; 01466 } 01467 01468 TQString KURL::url( int _trailing, int encoding_hint ) const 01469 { 01470 if( m_bIsMalformed ) 01471 { 01472 // Return the whole url even when the url is 01473 // malformed. Under such conditions the url 01474 // is stored in m_strProtocol. 01475 return m_strProtocol; 01476 } 01477 01478 TQString u = m_strProtocol; 01479 if (!u.isEmpty()) 01480 u += ":"; 01481 01482 if ( hasHost() || (m_strProtocol == fileProt) ) 01483 { 01484 u += "//"; 01485 if ( hasUser() ) 01486 { 01487 u += encode(m_strUser, 0, encoding_hint); 01488 if ( hasPass() ) 01489 { 01490 u += ":"; 01491 u += encode(m_strPass, 0, encoding_hint); 01492 } 01493 u += "@"; 01494 } 01495 if ( m_iUriMode == URL ) 01496 { 01497 bool IPv6 = (m_strHost.find(':') != -1); 01498 if (IPv6) 01499 u += '[' + m_strHost + ']'; 01500 else 01501 u += encodeHost(m_strHost, true, encoding_hint); 01502 if ( m_iPort != 0 ) { 01503 TQString buffer; 01504 buffer.sprintf( ":%u", m_iPort ); 01505 u += buffer; 01506 } 01507 } 01508 else 01509 { 01510 u += m_strHost; 01511 } 01512 } 01513 01514 if ( m_iUriMode == URL || m_iUriMode == Mailto ) 01515 u += encodedPathAndQuery( _trailing, false, encoding_hint ); 01516 else 01517 u += encode( m_strPath, 21, encoding_hint, true ); 01518 01519 if ( hasRef() ) 01520 { 01521 u += "#"; 01522 u += m_strRef_encoded; 01523 } 01524 01525 return u; 01526 } 01527 01528 TQString KURL::prettyURL( int _trailing ) const 01529 { 01530 if( m_bIsMalformed ) 01531 { 01532 // Return the whole url even when the url is 01533 // malformed. Under such conditions the url 01534 // is stored in m_strProtocol. 01535 return m_strProtocol; 01536 } 01537 01538 TQString u = m_strProtocol; 01539 if (!u.isEmpty()) 01540 u += ":"; 01541 01542 if ( hasHost() || (m_strProtocol == fileProt) ) 01543 { 01544 u += "//"; 01545 if ( hasUser() ) 01546 { 01547 u += encode(m_strUser, 0, 0); 01548 // Don't show password! 01549 u += "@"; 01550 } 01551 if ( m_iUriMode == URL ) 01552 { 01553 bool IPv6 = (m_strHost.find(':') != -1); 01554 if (IPv6) 01555 { 01556 u += '[' + m_strHost + ']'; 01557 } 01558 else 01559 { 01560 u += lazy_encode(m_strHost); 01561 } 01562 } 01563 else 01564 { 01565 u += lazy_encode(m_strHost); 01566 } 01567 if ( m_iPort != 0 ) { 01568 TQString buffer; 01569 buffer.sprintf( ":%u", m_iPort ); 01570 u += buffer; 01571 } 01572 } 01573 01574 if (m_iUriMode == Mailto) 01575 { 01576 u += lazy_encode( m_strPath, false ); 01577 } 01578 else 01579 { 01580 u += trailingSlash( _trailing, lazy_encode( m_strPath ) ); 01581 } 01582 01583 if (!m_strQuery_encoded.isNull()) 01584 u += '?' + m_strQuery_encoded; 01585 01586 if ( hasRef() ) 01587 { 01588 u += "#"; 01589 u += m_strRef_encoded; 01590 } 01591 01592 return u; 01593 } 01594 01595 TQString KURL::prettyURL( int _trailing, AdjustementFlags _flags) const 01596 { 01597 TQString u = prettyURL(_trailing); 01598 if (_flags & StripFileProtocol && u.startsWith("file://")) { 01599 u.remove(0, 7); 01600 #ifdef Q_WS_WIN 01601 return TQDir::convertSeparators(u); 01602 #endif 01603 } 01604 return u; 01605 } 01606 01607 TQString KURL::pathOrURL() const 01608 { 01609 if ( isLocalFile() && m_strRef_encoded.isNull() && m_strQuery_encoded.isNull() ) { 01610 return path(); 01611 } else { 01612 return prettyURL(); 01613 } 01614 } 01615 01616 TQString KURL::htmlURL() const 01617 { 01618 return TQStyleSheet::escape(prettyURL()); 01619 } 01620 01621 KURL::List KURL::split( const KURL& _url ) 01622 { 01623 TQString ref; 01624 KURL::List lst; 01625 KURL url = _url; 01626 01627 while(true) 01628 { 01629 KURL u = url; 01630 u.m_strRef_encoded = TQString::null; 01631 lst.append(u); 01632 if (url.hasSubURL()) 01633 { 01634 url = KURL(url.m_strRef_encoded); 01635 } 01636 else 01637 { 01638 ref = url.m_strRef_encoded; 01639 break; 01640 } 01641 } 01642 01643 // Set HTML ref in all URLs. 01644 KURL::List::Iterator it; 01645 for( it = lst.begin() ; it != lst.end(); ++it ) 01646 { 01647 (*it).m_strRef_encoded = ref; 01648 } 01649 01650 return lst; 01651 } 01652 01653 KURL::List KURL::split( const TQString& _url ) 01654 { 01655 return split(KURL(_url)); 01656 } 01657 01658 KURL KURL::join( const KURL::List & lst ) 01659 { 01660 if (lst.isEmpty()) return KURL(); 01661 KURL tmp; 01662 01663 KURL::List::ConstIterator first = lst.fromLast(); 01664 for( KURL::List::ConstIterator it = first; it != lst.end(); --it ) 01665 { 01666 KURL u(*it); 01667 if (it != first) 01668 { 01669 if (!u.m_strRef_encoded) u.m_strRef_encoded = tmp.url(); 01670 else u.m_strRef_encoded += "#" + tmp.url(); // Support more than one suburl thingy 01671 } 01672 tmp = u; 01673 } 01674 01675 return tmp; 01676 } 01677 01678 TQString KURL::fileName( bool _strip_trailing_slash ) const 01679 { 01680 TQString fname; 01681 if (hasSubURL()) { // If we have a suburl, then return the filename from there 01682 KURL::List list = KURL::split(*this); 01683 KURL::List::Iterator it = list.fromLast(); 01684 return (*it).fileName(_strip_trailing_slash); 01685 } 01686 const TQString &path = m_strPath; 01687 01688 int len = path.length(); 01689 if ( len == 0 ) 01690 return fname; 01691 01692 if ( _strip_trailing_slash ) 01693 { 01694 while ( len >= 1 && path[ len - 1 ] == TQChar('/') ) 01695 len--; 01696 } 01697 else if ( path[ len - 1 ] == TQChar('/') ) 01698 return fname; 01699 01700 // Does the path only consist of '/' characters ? 01701 if ( len == 1 && path[ 0 ] == TQChar('/') ) 01702 return fname; 01703 01704 // Skip last n slashes 01705 int n = 1; 01706 if (!m_strPath_encoded.isEmpty()) 01707 { 01708 // This is hairy, we need the last unencoded slash. 01709 // Count in the encoded string how many encoded slashes follow the last 01710 // unencoded one. 01711 int i = m_strPath_encoded.findRev( TQChar('/'), len - 1 ); 01712 TQString fileName_encoded = m_strPath_encoded.mid(i+1); 01713 n += fileName_encoded.contains("%2f", false); 01714 } 01715 int i = len; 01716 do { 01717 i = path.findRev( TQChar('/'), i - 1 ); 01718 } 01719 while (--n && (i > 0)); 01720 01721 // If ( i == -1 ) => the first character is not a '/' 01722 // So it's some URL like file:blah.tgz, return the whole path 01723 if ( i == -1 ) { 01724 if ( len == (int)path.length() ) 01725 fname = path; 01726 else 01727 // Might get here if _strip_trailing_slash is true 01728 fname = path.left( len ); 01729 } 01730 else 01731 { 01732 fname = path.mid( i + 1, len - i - 1 ); // TO CHECK 01733 } 01734 return fname; 01735 } 01736 01737 void KURL::addPath( const TQString& _txt ) 01738 { 01739 if (hasSubURL()) 01740 { 01741 KURL::List lst = split( *this ); 01742 KURL &u = lst.last(); 01743 u.addPath(_txt); 01744 *this = join( lst ); 01745 return; 01746 } 01747 01748 m_strPath_encoded = TQString::null; 01749 01750 if ( _txt.isEmpty() ) 01751 return; 01752 01753 int i = 0; 01754 int len = m_strPath.length(); 01755 // Add the trailing '/' if it is missing 01756 if ( _txt[0] != (QChar)'/' && ( len == 0 || m_strPath[ len - 1 ] != (QChar)'/' ) ) 01757 m_strPath += "/"; 01758 01759 // No double '/' characters 01760 i = 0; 01761 if ( len != 0 && m_strPath[ len - 1 ] == (QChar)'/' ) 01762 { 01763 while( _txt[i] == (QChar)'/' ) 01764 ++i; 01765 } 01766 01767 m_strPath += _txt.mid( i ); 01768 } 01769 01770 TQString KURL::directory( bool _strip_trailing_slash_from_result, 01771 bool _ignore_trailing_slash_in_path ) const 01772 { 01773 TQString result = m_strPath_encoded.isEmpty() ? m_strPath : m_strPath_encoded; 01774 if ( _ignore_trailing_slash_in_path ) 01775 result = trailingSlash( -1, result ); 01776 01777 if ( result.isEmpty() || result == "/" ) 01778 return result; 01779 01780 int i = result.findRev( "/" ); 01781 // If ( i == -1 ) => the first character is not a '/' 01782 // So it's some URL like file:blah.tgz, with no path 01783 if ( i == -1 ) 01784 return TQString::null; 01785 01786 if ( i == 0 ) 01787 { 01788 result = "/"; 01789 return result; 01790 } 01791 01792 if ( _strip_trailing_slash_from_result ) 01793 result = result.left( i ); 01794 else 01795 result = result.left( i + 1 ); 01796 01797 if (!m_strPath_encoded.isEmpty()) 01798 result = decode(result); 01799 01800 return result; 01801 } 01802 01803 01804 bool KURL::cd( const TQString& _dir ) 01805 { 01806 if ( _dir.isEmpty() || m_bIsMalformed ) 01807 return false; 01808 01809 if (hasSubURL()) 01810 { 01811 KURL::List lst = split( *this ); 01812 KURL &u = lst.last(); 01813 u.cd(_dir); 01814 *this = join( lst ); 01815 return true; 01816 } 01817 01818 // absolute path ? 01819 if ( _dir[0] == (QChar)'/' ) 01820 { 01821 m_strPath_encoded = TQString::null; 01822 m_strPath = _dir; 01823 setHTMLRef( TQString::null ); 01824 m_strQuery_encoded = TQString::null; 01825 return true; 01826 } 01827 01828 // Users home directory on the local disk ? 01829 if ( ( _dir[0] == (QChar)'~' ) && ( m_strProtocol == fileProt )) 01830 { 01831 m_strPath_encoded = TQString::null; 01832 m_strPath = TQDir::homeDirPath(); 01833 m_strPath += "/"; 01834 m_strPath += _dir.right(m_strPath.length() - 1); 01835 setHTMLRef( TQString::null ); 01836 m_strQuery_encoded = TQString::null; 01837 return true; 01838 } 01839 01840 // relative path 01841 // we always work on the past of the first url. 01842 // Sub URLs are not touched. 01843 01844 // append '/' if necessary 01845 TQString p = path(1); 01846 p += _dir; 01847 p = cleanpath( p, true, false ); 01848 setPath( p ); 01849 01850 setHTMLRef( TQString::null ); 01851 m_strQuery_encoded = TQString::null; 01852 01853 return true; 01854 } 01855 01856 KURL KURL::upURL( ) const 01857 { 01858 if (!query().isEmpty()) 01859 { 01860 KURL u(*this); 01861 u._setQuery(TQString::null); 01862 return u; 01863 }; 01864 01865 if (!hasSubURL()) 01866 { 01867 KURL u(*this); 01868 01869 u.cd("../"); 01870 01871 return u; 01872 } 01873 01874 // We have a subURL. 01875 KURL::List lst = split( *this ); 01876 if (lst.isEmpty()) 01877 return KURL(); // Huh? 01878 while (true) 01879 { 01880 KURL &u = lst.last(); 01881 TQString old = u.path(); 01882 u.cd("../"); 01883 if (u.path() != old) 01884 break; // Finshed. 01885 if (lst.count() == 1) 01886 break; // Finished. 01887 lst.remove(lst.fromLast()); 01888 } 01889 return join( lst ); 01890 } 01891 01892 TQString KURL::htmlRef() const 01893 { 01894 if ( !hasSubURL() ) 01895 { 01896 return decode( ref() ); 01897 } 01898 01899 List lst = split( *this ); 01900 return decode( (*lst.begin()).ref() ); 01901 } 01902 01903 TQString KURL::encodedHtmlRef() const 01904 { 01905 if ( !hasSubURL() ) 01906 { 01907 return ref(); 01908 } 01909 01910 List lst = split( *this ); 01911 return (*lst.begin()).ref(); 01912 } 01913 01914 void KURL::setHTMLRef( const TQString& _ref ) 01915 { 01916 if ( !hasSubURL() ) 01917 { 01918 m_strRef_encoded = encode( _ref, 0, 0 /*?*/); 01919 return; 01920 } 01921 01922 List lst = split( *this ); 01923 01924 (*lst.begin()).setRef( encode( _ref, 0, 0 /*?*/) ); 01925 01926 *this = join( lst ); 01927 } 01928 01929 bool KURL::hasHTMLRef() const 01930 { 01931 if ( !hasSubURL() ) 01932 { 01933 return hasRef(); 01934 } 01935 01936 List lst = split( *this ); 01937 return (*lst.begin()).hasRef(); 01938 } 01939 01940 void 01941 KURL::setProtocol( const TQString& _txt ) 01942 { 01943 m_strProtocol = _txt; 01944 if ( m_iUriMode == Auto ) m_iUriMode = uriModeForProtocol( m_strProtocol ); 01945 m_bIsMalformed = false; 01946 } 01947 01948 void 01949 KURL::setUser( const TQString& _txt ) 01950 { 01951 if ( _txt.isEmpty() ) 01952 m_strUser = TQString::null; 01953 else 01954 m_strUser = _txt; 01955 } 01956 01957 void 01958 KURL::setPass( const TQString& _txt ) 01959 { 01960 if ( _txt.isEmpty() ) 01961 m_strPass = TQString::null; 01962 else 01963 m_strPass = _txt; 01964 } 01965 01966 void 01967 KURL::setHost( const TQString& _txt ) 01968 { 01969 if ( m_iUriMode == Auto ) 01970 m_iUriMode = URL; 01971 switch ( m_iUriMode ) 01972 { 01973 case URL: 01974 #ifndef KDE_QT_ONLY 01975 m_strHost = KIDNA::toUnicode(_txt); 01976 if (m_strHost.isEmpty()) 01977 m_strHost = _txt.lower(); // Probably an invalid hostname, but... 01978 #else 01979 m_strHost = _txt.lower(); 01980 #endif 01981 break; 01982 default: 01983 m_strHost = _txt; 01984 break; 01985 } 01986 } 01987 01988 void 01989 KURL::setPort( unsigned short int _p ) 01990 { 01991 m_iPort = _p; 01992 } 01993 01994 void KURL::setPath( const TQString & path ) 01995 { 01996 if (isEmpty()) 01997 m_bIsMalformed = false; 01998 if (m_strProtocol.isEmpty()) 01999 { 02000 m_strProtocol = fileProt; 02001 } 02002 m_strPath = path; 02003 m_strPath_encoded = TQString::null; 02004 if ( m_iUriMode == Auto ) 02005 m_iUriMode = URL; 02006 } 02007 02008 void KURL::setDirectory( const TQString &dir) 02009 { 02010 if ( dir.endsWith("/")) 02011 setPath(dir); 02012 else 02013 setPath(dir+"/"); 02014 } 02015 02016 void KURL::setQuery( const TQString &_txt, int encoding_hint) 02017 { 02018 if (_txt[0] == (QChar)'?') 02019 _setQuery( _txt.length() > 1 ? _txt.mid(1) : "" /*empty, not null*/, encoding_hint ); 02020 else 02021 _setQuery( _txt, encoding_hint ); 02022 } 02023 02024 // This is a private function that expects a query without '?' 02025 void KURL::_setQuery( const TQString &_txt, int encoding_hint) 02026 { 02027 m_strQuery_encoded = _txt; 02028 if (!_txt.length()) 02029 return; 02030 02031 int l = m_strQuery_encoded.length(); 02032 int i = 0; 02033 TQString result; 02034 while (i < l) 02035 { 02036 int s = i; 02037 // Re-encode. Break encoded string up according to the reserved 02038 // characters '&:;=/?' and re-encode part by part. 02039 while(i < l) 02040 { 02041 char c = m_strQuery_encoded[i].latin1(); 02042 if ((c == '&') || (c == ':') || (c == ';') || 02043 (c == '=') || (c == '/') || (c == '?')) 02044 break; 02045 i++; 02046 } 02047 if (i > s) 02048 { 02049 TQString tmp = m_strQuery_encoded.mid(s, i-s); 02050 TQString newTmp; 02051 decode( tmp, newTmp, tmp, encoding_hint, false ); 02052 result += tmp; 02053 } 02054 if (i < l) 02055 { 02056 result += m_strQuery_encoded[i]; 02057 i++; 02058 } 02059 } 02060 m_strQuery_encoded = result; 02061 } 02062 02063 TQString KURL::query() const 02064 { 02065 if (m_strQuery_encoded.isNull()) 02066 return TQString::null; 02067 return '?'+m_strQuery_encoded; 02068 } 02069 02070 TQString KURL::decode_string(const TQString &str, int encoding_hint) 02071 { 02072 return decode(str, encoding_hint); 02073 } 02074 02075 TQString KURL::encode_string(const TQString &str, int encoding_hint) 02076 { 02077 return encode(str, 1, encoding_hint); 02078 } 02079 02080 TQString KURL::encode_string_no_slash(const TQString &str, int encoding_hint) 02081 { 02082 return encode(str, 0, encoding_hint); 02083 } 02084 02085 bool urlcmp( const TQString& _url1, const TQString& _url2 ) 02086 { 02087 // Both empty ? 02088 if ( _url1.isEmpty() && _url2.isEmpty() ) 02089 return true; 02090 // Only one empty ? 02091 if ( _url1.isEmpty() || _url2.isEmpty() ) 02092 return false; 02093 02094 KURL::List list1 = KURL::split( _url1 ); 02095 KURL::List list2 = KURL::split( _url2 ); 02096 02097 // Malformed ? 02098 if ( list1.isEmpty() || list2.isEmpty() ) 02099 return false; 02100 02101 return ( list1 == list2 ); 02102 } 02103 02104 bool urlcmp( const TQString& _url1, const TQString& _url2, bool _ignore_trailing, bool _ignore_ref ) 02105 { 02106 // Both empty ? 02107 if ( _url1.isEmpty() && _url2.isEmpty() ) 02108 return true; 02109 // Only one empty ? 02110 if ( _url1.isEmpty() || _url2.isEmpty() ) 02111 return false; 02112 02113 KURL::List list1 = KURL::split( _url1 ); 02114 KURL::List list2 = KURL::split( _url2 ); 02115 02116 // Malformed ? 02117 if ( list1.isEmpty() || list2.isEmpty() ) 02118 return false; 02119 02120 unsigned int size = list1.count(); 02121 if ( list2.count() != size ) 02122 return false; 02123 02124 if ( _ignore_ref ) 02125 { 02126 (*list1.begin()).setRef(TQString::null); 02127 (*list2.begin()).setRef(TQString::null); 02128 } 02129 02130 KURL::List::Iterator it1 = list1.begin(); 02131 KURL::List::Iterator it2 = list2.begin(); 02132 for( ; it1 != list1.end() ; ++it1, ++it2 ) 02133 if ( !(*it1).equals( *it2, _ignore_trailing ) ) 02134 return false; 02135 02136 return true; 02137 } 02138 02139 TQMap< TQString, TQString > KURL::queryItems( int options ) const { 02140 return queryItems(options, 0); 02141 } 02142 02143 TQMap< TQString, TQString > KURL::queryItems( int options, int encoding_hint ) const { 02144 if ( m_strQuery_encoded.isEmpty() ) 02145 return TQMap<TQString,TQString>(); 02146 02147 TQMap< TQString, TQString > result; 02148 TQStringList items = TQStringList::split( '&', m_strQuery_encoded ); 02149 for ( TQStringList::const_iterator it = items.begin() ; it != items.end() ; ++it ) { 02150 int equal_pos = (*it).find( '=' ); 02151 if ( equal_pos > 0 ) { // = is not the first char... 02152 TQString name = (*it).left( equal_pos ); 02153 if ( options & CaseInsensitiveKeys ) 02154 name = name.lower(); 02155 TQString value = (*it).mid( equal_pos + 1 ); 02156 if ( value.isEmpty() ) 02157 result.insert( name, TQString::fromLatin1("") ); 02158 else { 02159 // ### why is decoding name not necessary? 02160 value.replace( '+', ' ' ); // + in queries means space 02161 result.insert( name, decode_string( value, encoding_hint ) ); 02162 } 02163 } else if ( equal_pos < 0 ) { // no = 02164 TQString name = (*it); 02165 if ( options & CaseInsensitiveKeys ) 02166 name = name.lower(); 02167 result.insert( name, TQString::null ); 02168 } 02169 } 02170 02171 return result; 02172 } 02173 02174 TQString KURL::queryItem( const TQString& _item ) const 02175 { 02176 return queryItem( _item, 0 ); 02177 } 02178 02179 TQString KURL::queryItem( const TQString& _item, int encoding_hint ) const 02180 { 02181 TQString item = _item + '='; 02182 if ( m_strQuery_encoded.length() <= 1 ) 02183 return TQString::null; 02184 02185 TQStringList items = TQStringList::split( '&', m_strQuery_encoded ); 02186 unsigned int _len = item.length(); 02187 for ( TQStringList::ConstIterator it = items.begin(); it != items.end(); ++it ) 02188 { 02189 if ( (*it).startsWith( item ) ) 02190 { 02191 if ( (*it).length() > _len ) 02192 { 02193 TQString str = (*it).mid( _len ); 02194 str.replace( '+', ' ' ); // + in queries means space. 02195 return decode_string( str, encoding_hint ); 02196 } 02197 else // empty value 02198 return TQString::fromLatin1(""); 02199 } 02200 } 02201 02202 return TQString::null; 02203 } 02204 02205 void KURL::removeQueryItem( const TQString& _item ) 02206 { 02207 TQString item = _item + '='; 02208 if ( m_strQuery_encoded.length() <= 1 ) 02209 return; 02210 02211 TQStringList items = TQStringList::split( '&', m_strQuery_encoded ); 02212 for ( TQStringList::Iterator it = items.begin(); it != items.end(); ) 02213 { 02214 if ( (*it).startsWith( item ) || (*it == _item) ) 02215 { 02216 TQStringList::Iterator deleteIt = it; 02217 ++it; 02218 items.remove(deleteIt); 02219 } 02220 else 02221 { 02222 ++it; 02223 } 02224 } 02225 m_strQuery_encoded = items.join( "&" ); 02226 } 02227 02228 void KURL::addQueryItem( const TQString& _item, const TQString& _value, int encoding_hint ) 02229 { 02230 TQString item = _item + '='; 02231 TQString value = encode( _value, 0, encoding_hint ); 02232 02233 if (!m_strQuery_encoded.isEmpty()) 02234 m_strQuery_encoded += '&'; 02235 m_strQuery_encoded += item + value; 02236 } 02237 02238 // static 02239 KURL KURL::fromPathOrURL( const TQString& text ) 02240 { 02241 if ( text.isEmpty() ) 02242 return KURL(); 02243 02244 KURL url; 02245 if (!TQDir::isRelativePath(text)) 02246 url.setPath( text ); 02247 else 02248 url = text; 02249 02250 return url; 02251 } 02252 02253 static TQString _relativePath(const TQString &base_dir, const TQString &path, bool &isParent) 02254 { 02255 TQString _base_dir(TQDir::cleanDirPath(base_dir)); 02256 TQString _path(TQDir::cleanDirPath(path.isEmpty() || (path[0] != (QChar)'/') ? _base_dir+"/"+path : path)); 02257 02258 if (_base_dir.isEmpty()) 02259 return _path; 02260 02261 if (_base_dir[_base_dir.length()-1] != '/') 02262 _base_dir.append('/'); 02263 02264 TQStringList list1 = TQStringList::split('/', _base_dir); 02265 TQStringList list2 = TQStringList::split('/', _path); 02266 02267 // Find where they meet 02268 uint level = 0; 02269 uint maxLevel = QMIN(list1.count(), list2.count()); 02270 while((level < maxLevel) && (list1[level] == list2[level])) level++; 02271 02272 TQString result; 02273 // Need to go down out of the first path to the common branch. 02274 for(uint i = level; i < list1.count(); i++) 02275 result.append("../"); 02276 02277 // Now up up from the common branch to the second path. 02278 for(uint i = level; i < list2.count(); i++) 02279 result.append(list2[i]).append("/"); 02280 02281 if ((level < list2.count()) && (path[path.length()-1] != (QChar)'/')) 02282 result.truncate(result.length()-1); 02283 02284 isParent = (level == list1.count()); 02285 02286 return result; 02287 } 02288 02289 TQString KURL::relativePath(const TQString &base_dir, const TQString &path, bool *isParent) 02290 { 02291 bool parent = false; 02292 TQString result = _relativePath(base_dir, path, parent); 02293 if (parent) 02294 result.prepend("./"); 02295 02296 if (isParent) 02297 *isParent = parent; 02298 02299 return result; 02300 } 02301 02302 02303 TQString KURL::relativeURL(const KURL &base_url, const KURL &url, int encoding_hint) 02304 { 02305 if ((url.protocol() != base_url.protocol()) || 02306 (url.host() != base_url.host()) || 02307 (url.port() && url.port() != base_url.port()) || 02308 (url.hasUser() && url.user() != base_url.user()) || 02309 (url.hasPass() && url.pass() != base_url.pass())) 02310 { 02311 return url.url(0, encoding_hint); 02312 } 02313 02314 TQString relURL; 02315 02316 if ((base_url.path() != url.path()) || (base_url.query() != url.query())) 02317 { 02318 bool dummy; 02319 TQString basePath = base_url.directory(false, false); 02320 relURL = encode( _relativePath(basePath, url.path(), dummy), 1, encoding_hint); 02321 relURL += url.query(); 02322 } 02323 02324 if ( url.hasRef() ) 02325 { 02326 relURL += "#"; 02327 relURL += url.ref(); 02328 } 02329 02330 if ( relURL.isEmpty() ) 02331 return "./"; 02332 02333 return relURL; 02334 } 02335 02336 int KURL::uriMode() const 02337 { 02338 return m_iUriMode; 02339 } 02340 02341 KURL::URIMode KURL::uriModeForProtocol(const TQString& protocol) 02342 { 02343 #ifndef KDE_QT_ONLY 02344 KURL::URIMode mode = Auto; 02345 if (protocol == fileProt) 02346 return URL; 02347 if (KGlobal::_instance) 02348 mode = KProtocolInfo::uriParseMode(protocol); 02349 if (mode == Auto ) { 02350 #else 02351 KURL::URIMode mode = Auto; 02352 #endif 02353 if ( protocol == "ed2k" || protocol == "sig2dat" || protocol == "slsk" || protocol == "data" ) mode = RawURI; 02354 else if ( protocol == "mailto" ) mode = Mailto; 02355 else mode = URL; 02356 #ifndef KDE_QT_ONLY 02357 } 02358 #endif 02359 return mode; 02360 }