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 <qurl.h> 00042 #include <qdir.h> 00043 #include <qstringlist.h> 00044 #include <qregexp.h> 00045 #include <qstylesheet.h> 00046 #include <qmap.h> 00047 #include <qtextcodec.h> 00048 #include <qmutex.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 QString fileProt = "file"; 00057 00058 static QTextCodec * codecForHint( int encoding_hint /* not 0 ! */ ) 00059 { 00060 return QTextCodec::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 QString encode( const QString& segment, int encoding_offset, int encoding_hint, bool isRawURI = false ) 00068 { 00069 const char *encode_string = "/@<>#\"&?={}|^~[]\'`\\:+%"; 00070 encode_string += encoding_offset; 00071 00072 QCString local; 00073 if (encoding_hint==0) 00074 local = segment.local8Bit(); 00075 else 00076 { 00077 QTextCodec * 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 ) 00087 return segment.isNull() ? QString::null : QString(""); // differentiate null and empty 00088 00089 // a worst case approximation 00090 QChar *new_segment = new QChar[ 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++ ] = local[i]; 00116 } 00117 00118 QString result = QString(new_segment, new_length); 00119 delete [] new_segment; 00120 return result; 00121 } 00122 00123 static QString encodeHost( const QString& 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 QString 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 QString lazy_encode( const QString& segment, bool encodeAt=true ) 00163 { 00164 int old_length = segment.length(); 00165 00166 if ( !old_length ) 00167 return QString::null; 00168 00169 // a worst case approximation 00170 QChar *new_segment = new QChar[ 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] == ' '))) // 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 QString result = QString(new_segment, new_length); 00203 delete [] new_segment; 00204 return result; 00205 } 00206 00207 static void decode( const QString& segment, QString &decoded, QString &encoded, int encoding_hint=0, bool updateDecoded = true, bool isRawURI = false ) 00208 { 00209 decoded = QString::null; 00210 encoded = segment; 00211 00212 int old_length = segment.length(); 00213 if ( !old_length ) 00214 return; 00215 00216 QTextCodec *textCodec = 0; 00217 if (encoding_hint) 00218 textCodec = codecForHint( encoding_hint ); 00219 00220 if (!textCodec) 00221 textCodec = QTextCodec::codecForLocale(); 00222 00223 QCString 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 QChar *new_usegment = new QChar[ 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 = QString( new_usegment, new_length2); 00288 00289 // Encoding specified 00290 if (updateDecoded) 00291 { 00292 decoded = textCodec->toUnicode( new_segment ); 00293 if ( isRawURI ) { 00294 int length = qstrlen( new_segment ); 00295 while ( length < new_length ) { 00296 decoded += QChar::null; 00297 length += 1; 00298 decoded += textCodec->toUnicode( new_segment + length ); 00299 length += qstrlen( new_segment + length ); 00300 } 00301 } 00302 00303 QCString validate = textCodec->fromUnicode(decoded); 00304 00305 if (strcmp(validate.data(), new_segment) != 0) 00306 { 00307 decoded = QString::fromLocal8Bit(new_segment, new_length); 00308 } 00309 } 00310 00311 delete [] new_segment; 00312 delete [] new_usegment; 00313 } 00314 00315 static QString decode(const QString &segment, int encoding_hint = 0, bool isRawURI = false) 00316 { 00317 QString result; 00318 QString tmp; 00319 decode(segment, result, tmp, encoding_hint, true, isRawURI); 00320 return result; 00321 } 00322 00323 static QString cleanpath(const QString &_path, bool cleanDirSeparator, bool decodeDots) 00324 { 00325 if (_path.isEmpty()) return QString::null; 00326 00327 if (QDir::isRelativePath(_path)) 00328 return _path; // Don't mangle mailto-style URLs 00329 00330 QString path = _path; 00331 00332 int len = path.length(); 00333 00334 if (decodeDots) 00335 { 00336 #ifndef KDE_QT_ONLY 00337 static const QString &encodedDot = KGlobal::staticQString("%2e"); 00338 #else 00339 QString encodedDot("%2e"); 00340 #endif 00341 if (path.find(encodedDot, 0, false) != -1) 00342 { 00343 #ifndef KDE_QT_ONLY 00344 static const QString &encodedDOT = KGlobal::staticQString("%2E"); // Uppercase! 00345 #else 00346 QString 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 // QDir::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 QString 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(QString(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 QString &_url) 00404 { 00405 int len = _url.length(); 00406 if (!len) return true; // Very short relative URL. 00407 const QChar *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 QStringList &list) 00433 { 00434 for (QStringList::ConstIterator it = list.begin(); 00435 it != list.end(); 00436 it++) 00437 { 00438 append( KURL(*it) ); 00439 } 00440 } 00441 00442 QStringList KURL::List::toStringList() const 00443 { 00444 QStringList 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 QString &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( QString::fromLatin1(url), encoding_hint ); 00475 } 00476 00477 KURL::KURL( const QCString& url, int encoding_hint ) 00478 { 00479 reset(); 00480 parse( QString::fromLatin1(url), encoding_hint ); 00481 } 00482 00483 KURL::KURL( const KURL& _u ) 00484 { 00485 *this = _u; 00486 } 00487 00488 QDataStream & operator<< (QDataStream & s, const KURL & a) 00489 { 00490 QString 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 << Q_INT8(a.m_bIsMalformed ? 1 : 0) << a.m_iPort; 00497 return s; 00498 } 00499 00500 QDataStream & operator>> (QDataStream & s, KURL & a) 00501 { 00502 Q_INT8 malf; 00503 QString 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 = QString::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 QUrl &u ) 00523 { 00524 *this = u; 00525 } 00526 #endif 00527 00528 KURL::KURL( const KURL& _u, const QString& _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 QString 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 = QString::null; 00567 m_strRef_encoded = QString::null; 00568 if ( rUrl[0] == '/') 00569 { 00570 if ((rUrl.length() > 1) && (rUrl[1] == '/')) 00571 { 00572 m_strHost = QString::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 = QString::null; 00578 m_strPath_encoded = QString::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 = QString::null; 00620 m_strUser = QString::null; 00621 m_strPass = QString::null; 00622 m_strHost = QString::null; 00623 m_strPath = QString::null; 00624 m_strPath_encoded = QString::null; 00625 m_strQuery_encoded = QString::null; 00626 m_strRef_encoded = QString::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 QString& _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 QChar* buf = _url.unicode(); 00647 const QChar* orig = buf; 00648 uint len = _url.length(); 00649 uint pos = 0; 00650 00651 // Node 1: Accept alpha or slash 00652 QChar 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 == '/' ) 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] == '+' || buf[pos] == '-')) pos++; 00677 00678 if (pos < len && buf[pos] == ':' ) 00679 { 00680 m_strProtocol = QString( 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 QString& _url, int encoding_hint ) 00708 { 00709 uint len = _url.length(); 00710 const QChar* 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] == '+' || buf[pos] == '-')) pos++; 00719 00720 // Note that m_strProtocol is already set here, so we just skip over the protocol. 00721 if (pos < len && buf[pos] == ':' ) 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 = QString::null; 00732 else 00733 m_strPath = decode( QString( buf + pos, len - pos ), encoding_hint, true ); 00734 00735 m_bIsMalformed = false; 00736 00737 return; 00738 } 00739 00740 void KURL::parseMailto( const QString& _url, int encoding_hint ) 00741 { 00742 parseURL( _url, encoding_hint); 00743 if ( m_bIsMalformed ) 00744 return; 00745 QRegExp mailre("(.+@)(.+)"); 00746 if ( mailre.exactMatch( m_strPath ) ) 00747 { 00748 #ifndef KDE_QT_ONLY 00749 QString host = KIDNA::toUnicode( mailre.cap( 2 ) ); 00750 if (host.isEmpty()) 00751 host = mailre.cap( 2 ).lower(); 00752 #else 00753 QString host = mailre.cap( 2 ).lower(); 00754 #endif 00755 m_strPath = mailre.cap( 1 ) + host; 00756 } 00757 } 00758 00759 void KURL::parseURL( const QString& _url, int encoding_hint ) 00760 { 00761 QString port; 00762 bool badHostName = false; 00763 int start = 0; 00764 uint len = _url.length(); 00765 const QChar* buf = _url.unicode(); 00766 00767 QChar delim; 00768 QString tmp; 00769 00770 uint pos = 0; 00771 00772 // Node 1: Accept alpha or slash 00773 QChar 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]==':' && (len==2 || (len>2 && (buf[pos+1]=='/' || buf[pos+1]=='\\')))) 00780 #else 00781 if ( x == '/' ) 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] == '+' || buf[pos] == '-')) 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] == ':' && buf[pos+1] == '/' && buf[pos+2] == '/' ) 00795 { 00796 pos += 3; 00797 } 00798 else if (pos+1 < len && buf[pos] == ':' ) // 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] == '[') // An IPv6 host follows. 00814 goto Node8; 00815 // Terminate on / or @ or ? or # or " or ; or < 00816 x = buf[pos]; 00817 while( (x != ':') && (x != '@') && (x != '/') && (x != '?') && (x != '#') ) 00818 { 00819 if ((x == '\"') || (x == ';') || (x == '<')) 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(QString( buf + start, pos - start ), encoding_hint)); 00831 goto NodeOk; 00832 } 00833 if ( x == '@' ) 00834 { 00835 m_strUser = decode(QString( buf + start, pos - start ), encoding_hint); 00836 pos++; 00837 goto Node7; 00838 } 00839 else if ( (x == '/') || (x == '?') || (x == '#')) 00840 { 00841 if (badHostName) 00842 goto NodeErr; 00843 00844 setHost(decode(QString( buf + start, pos - start ), encoding_hint)); 00845 start = pos; 00846 goto Node9; 00847 } 00848 else if ( x != ':' ) 00849 goto NodeErr; 00850 m_strUser = decode(QString( 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] != '@') && 00861 (buf[pos] != '/') && 00862 (buf[pos] != '?') && 00863 (buf[pos] != '#')) 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] != '@') ) 00867 { 00868 // Ok the : was used to separate host and port 00869 if (badHostName) 00870 goto NodeErr; 00871 setHost(m_strUser); 00872 m_strUser = QString::null; 00873 QString 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] != '@') && 00881 (buf[pos] != '/') && 00882 (buf[pos] != '?') && 00883 (buf[pos] != '#')) 00884 goto NodeErr; 00885 00886 start = pos; 00887 goto Node9; 00888 } 00889 m_strPass = decode(QString( 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] == '[') 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 != ']') ) 00912 { 00913 if ((x == '\"') || (x == ';') || (x == '<')) 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(QString( 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 != ':') && (x != '@') && (x != '/') && (x != '?') && (x != '#') ) 00938 { 00939 if ((x == '\"') || (x == ';') || (x == '<')) 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(QString( buf + start, pos - start ), encoding_hint)); 00950 goto NodeOk; 00951 } 00952 setHost(decode(QString( buf + start, pos - start ), encoding_hint)); 00953 } 00954 x = buf[pos]; 00955 if ( x == '/' || x == '#' || x == '?' ) 00956 { 00957 start = pos; 00958 goto Node9; 00959 } 00960 else if ( x != ':' ) 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 = QString( 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] != '#' && buf[pos]!='?' ) pos++; 00982 00983 tmp = QString( 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++]=='#'?'?':'#'); 00992 00993 start = pos; 00994 00995 while(pos < len && buf[pos]!=delim ) pos++; 00996 00997 tmp = QString(buf + start, pos - start); 00998 if (delim=='#') 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 = QString( buf + pos + 1, len - pos - 1); 01008 if (delim == '#') 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 QString& _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( QString::fromLatin1(_url) ); 01044 01045 return *this; 01046 } 01047 01048 #ifndef QT_NO_NETWORKPROTOCOL 01049 KURL& KURL::operator=( const QUrl & 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 = QString::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 QString& _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 QString path1 = path(1); 01167 QString 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 QString p1( cleanpath( path(), true, false ) ); 01203 if ( p1[p1.length()-1] != '/' ) 01204 p1 += '/'; 01205 QString 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 QString& _txt ) 01219 { 01220 m_strRef_encoded = QString::null; 01221 int i = 0; 01222 while( _txt[i] == '/' ) ++i; 01223 QString tmp; 01224 if ( i ) 01225 tmp = _txt.mid( i ); 01226 else 01227 tmp = _txt; 01228 01229 QString 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 QString trailingSlash( int _trailing, const QString &path ) 01266 { 01267 QString 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 ] != '/') ) 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 ] == '/') 01284 { 01285 len--; 01286 } 01287 result.truncate( len ); 01288 return result; 01289 } 01290 else { 01291 assert( 0 ); 01292 return QString::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 QString KURL::encodedPathAndQuery( int _trailing, bool _no_empty_path, int encoding_hint ) const 01307 { 01308 QString 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 QString& _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 = QString::null; 01342 01343 if ( m_iUriMode == Auto ) 01344 m_iUriMode = URL; 01345 } 01346 01347 01348 void KURL::setEncodedPathAndQuery( const QString& _txt, int encoding_hint ) 01349 { 01350 int pos = _txt.find( '?' ); 01351 if ( pos == -1 ) 01352 { 01353 setEncodedPath(_txt, encoding_hint); 01354 m_strQuery_encoded = QString::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 QString 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 QString &encoding) 01388 { 01389 if (!isLocalFile()) 01390 return; 01391 01392 QString q = query(); 01393 01394 if (!q.isEmpty() && (q[0] == '?')) 01395 q = q.mid(1); 01396 01397 QStringList args = QStringList::split('&', q); 01398 for(QStringList::Iterator it = args.begin(); 01399 it != args.end();) 01400 { 01401 QString 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(QString::null); 01412 else 01413 _setQuery(args.join("&")); 01414 } 01415 01416 QString KURL::fileEncoding() const 01417 { 01418 if (!isLocalFile()) 01419 return QString::null; 01420 01421 QString q = query(); 01422 01423 if (q.isEmpty()) 01424 return QString::null; 01425 01426 if (q[0] == '?') 01427 q = q.mid(1); 01428 01429 QStringList args = QStringList::split('&', q); 01430 for(QStringList::ConstIterator it = args.begin(); 01431 it != args.end(); 01432 ++it) 01433 { 01434 QString s = decode_string(*it); 01435 if (s.startsWith("charset=")) 01436 return s.mid(8); 01437 } 01438 return QString::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_strProtocol == "error" ) // anything that starts with error: has suburls 01460 return true; 01461 return false; 01462 } 01463 01464 QString KURL::url( int _trailing, int encoding_hint ) const 01465 { 01466 if( m_bIsMalformed ) 01467 { 01468 // Return the whole url even when the url is 01469 // malformed. Under such conditions the url 01470 // is stored in m_strProtocol. 01471 return m_strProtocol; 01472 } 01473 01474 QString u = m_strProtocol; 01475 if (!u.isEmpty()) 01476 u += ":"; 01477 01478 if ( hasHost() || (m_strProtocol == fileProt) ) 01479 { 01480 u += "//"; 01481 if ( hasUser() ) 01482 { 01483 u += encode(m_strUser, 0, encoding_hint); 01484 if ( hasPass() ) 01485 { 01486 u += ":"; 01487 u += encode(m_strPass, 0, encoding_hint); 01488 } 01489 u += "@"; 01490 } 01491 if ( m_iUriMode == URL ) 01492 { 01493 bool IPv6 = (m_strHost.find(':') != -1); 01494 if (IPv6) 01495 u += '[' + m_strHost + ']'; 01496 else 01497 u += encodeHost(m_strHost, true, encoding_hint); 01498 if ( m_iPort != 0 ) { 01499 QString buffer; 01500 buffer.sprintf( ":%u", m_iPort ); 01501 u += buffer; 01502 } 01503 } 01504 else 01505 { 01506 u += m_strHost; 01507 } 01508 } 01509 01510 if ( m_iUriMode == URL || m_iUriMode == Mailto ) 01511 u += encodedPathAndQuery( _trailing, false, encoding_hint ); 01512 else 01513 u += encode( m_strPath, 21, encoding_hint, true ); 01514 01515 if ( hasRef() ) 01516 { 01517 u += "#"; 01518 u += m_strRef_encoded; 01519 } 01520 01521 return u; 01522 } 01523 01524 QString KURL::prettyURL( int _trailing ) const 01525 { 01526 if( m_bIsMalformed ) 01527 { 01528 // Return the whole url even when the url is 01529 // malformed. Under such conditions the url 01530 // is stored in m_strProtocol. 01531 return m_strProtocol; 01532 } 01533 01534 QString u = m_strProtocol; 01535 if (!u.isEmpty()) 01536 u += ":"; 01537 01538 if ( hasHost() || (m_strProtocol == fileProt) ) 01539 { 01540 u += "//"; 01541 if ( hasUser() ) 01542 { 01543 u += encode(m_strUser, 0, 0); 01544 // Don't show password! 01545 u += "@"; 01546 } 01547 if ( m_iUriMode == URL ) 01548 { 01549 bool IPv6 = (m_strHost.find(':') != -1); 01550 if (IPv6) 01551 { 01552 u += '[' + m_strHost + ']'; 01553 } 01554 else 01555 { 01556 u += lazy_encode(m_strHost); 01557 } 01558 } 01559 else 01560 { 01561 u += lazy_encode(m_strHost); 01562 } 01563 if ( m_iPort != 0 ) { 01564 QString buffer; 01565 buffer.sprintf( ":%u", m_iPort ); 01566 u += buffer; 01567 } 01568 } 01569 01570 if (m_iUriMode == Mailto) 01571 { 01572 u += lazy_encode( m_strPath, false ); 01573 } 01574 else 01575 { 01576 u += trailingSlash( _trailing, lazy_encode( m_strPath ) ); 01577 } 01578 01579 if (!m_strQuery_encoded.isNull()) 01580 u += '?' + m_strQuery_encoded; 01581 01582 if ( hasRef() ) 01583 { 01584 u += "#"; 01585 u += m_strRef_encoded; 01586 } 01587 01588 return u; 01589 } 01590 01591 QString KURL::prettyURL( int _trailing, AdjustementFlags _flags) const 01592 { 01593 QString u = prettyURL(_trailing); 01594 if (_flags & StripFileProtocol && u.startsWith("file://")) { 01595 u.remove(0, 7); 01596 #ifdef Q_WS_WIN 01597 return QDir::convertSeparators(u); 01598 #endif 01599 } 01600 return u; 01601 } 01602 01603 QString KURL::pathOrURL() const 01604 { 01605 if ( isLocalFile() && m_strRef_encoded.isNull() && m_strQuery_encoded.isNull() ) { 01606 return path(); 01607 } else { 01608 return prettyURL(); 01609 } 01610 } 01611 01612 QString KURL::htmlURL() const 01613 { 01614 return QStyleSheet::escape(prettyURL()); 01615 } 01616 01617 KURL::List KURL::split( const KURL& _url ) 01618 { 01619 QString ref; 01620 KURL::List lst; 01621 KURL url = _url; 01622 01623 while(true) 01624 { 01625 KURL u = url; 01626 u.m_strRef_encoded = QString::null; 01627 lst.append(u); 01628 if (url.hasSubURL()) 01629 { 01630 url = KURL(url.m_strRef_encoded); 01631 } 01632 else 01633 { 01634 ref = url.m_strRef_encoded; 01635 break; 01636 } 01637 } 01638 01639 // Set HTML ref in all URLs. 01640 KURL::List::Iterator it; 01641 for( it = lst.begin() ; it != lst.end(); ++it ) 01642 { 01643 (*it).m_strRef_encoded = ref; 01644 } 01645 01646 return lst; 01647 } 01648 01649 KURL::List KURL::split( const QString& _url ) 01650 { 01651 return split(KURL(_url)); 01652 } 01653 01654 KURL KURL::join( const KURL::List & lst ) 01655 { 01656 if (lst.isEmpty()) return KURL(); 01657 KURL tmp; 01658 01659 KURL::List::ConstIterator first = lst.fromLast(); 01660 for( KURL::List::ConstIterator it = first; it != lst.end(); --it ) 01661 { 01662 KURL u(*it); 01663 if (it != first) 01664 { 01665 if (!u.m_strRef_encoded) u.m_strRef_encoded = tmp.url(); 01666 else u.m_strRef_encoded += "#" + tmp.url(); // Support more than one suburl thingy 01667 } 01668 tmp = u; 01669 } 01670 01671 return tmp; 01672 } 01673 01674 QString KURL::fileName( bool _strip_trailing_slash ) const 01675 { 01676 QString fname; 01677 if (hasSubURL()) { // If we have a suburl, then return the filename from there 01678 KURL::List list = KURL::split(*this); 01679 KURL::List::Iterator it = list.fromLast(); 01680 return (*it).fileName(_strip_trailing_slash); 01681 } 01682 const QString &path = m_strPath; 01683 01684 int len = path.length(); 01685 if ( len == 0 ) 01686 return fname; 01687 01688 if ( _strip_trailing_slash ) 01689 { 01690 while ( len >= 1 && path[ len - 1 ] == '/' ) 01691 len--; 01692 } 01693 else if ( path[ len - 1 ] == '/' ) 01694 return fname; 01695 01696 // Does the path only consist of '/' characters ? 01697 if ( len == 1 && path[ 0 ] == '/' ) 01698 return fname; 01699 01700 // Skip last n slashes 01701 int n = 1; 01702 if (!m_strPath_encoded.isEmpty()) 01703 { 01704 // This is hairy, we need the last unencoded slash. 01705 // Count in the encoded string how many encoded slashes follow the last 01706 // unencoded one. 01707 int i = m_strPath_encoded.findRev( '/', len - 1 ); 01708 QString fileName_encoded = m_strPath_encoded.mid(i+1); 01709 n += fileName_encoded.contains("%2f", false); 01710 } 01711 int i = len; 01712 do { 01713 i = path.findRev( '/', i - 1 ); 01714 } 01715 while (--n && (i > 0)); 01716 01717 // If ( i == -1 ) => the first character is not a '/' 01718 // So it's some URL like file:blah.tgz, return the whole path 01719 if ( i == -1 ) { 01720 if ( len == (int)path.length() ) 01721 fname = path; 01722 else 01723 // Might get here if _strip_trailing_slash is true 01724 fname = path.left( len ); 01725 } 01726 else 01727 { 01728 fname = path.mid( i + 1, len - i - 1 ); // TO CHECK 01729 } 01730 return fname; 01731 } 01732 01733 void KURL::addPath( const QString& _txt ) 01734 { 01735 if (hasSubURL()) 01736 { 01737 KURL::List lst = split( *this ); 01738 KURL &u = lst.last(); 01739 u.addPath(_txt); 01740 *this = join( lst ); 01741 return; 01742 } 01743 01744 m_strPath_encoded = QString::null; 01745 01746 if ( _txt.isEmpty() ) 01747 return; 01748 01749 int i = 0; 01750 int len = m_strPath.length(); 01751 // Add the trailing '/' if it is missing 01752 if ( _txt[0] != '/' && ( len == 0 || m_strPath[ len - 1 ] != '/' ) ) 01753 m_strPath += "/"; 01754 01755 // No double '/' characters 01756 i = 0; 01757 if ( len != 0 && m_strPath[ len - 1 ] == '/' ) 01758 { 01759 while( _txt[i] == '/' ) 01760 ++i; 01761 } 01762 01763 m_strPath += _txt.mid( i ); 01764 } 01765 01766 QString KURL::directory( bool _strip_trailing_slash_from_result, 01767 bool _ignore_trailing_slash_in_path ) const 01768 { 01769 QString result = m_strPath_encoded.isEmpty() ? m_strPath : m_strPath_encoded; 01770 if ( _ignore_trailing_slash_in_path ) 01771 result = trailingSlash( -1, result ); 01772 01773 if ( result.isEmpty() || result == "/" ) 01774 return result; 01775 01776 int i = result.findRev( "/" ); 01777 // If ( i == -1 ) => the first character is not a '/' 01778 // So it's some URL like file:blah.tgz, with no path 01779 if ( i == -1 ) 01780 return QString::null; 01781 01782 if ( i == 0 ) 01783 { 01784 result = "/"; 01785 return result; 01786 } 01787 01788 if ( _strip_trailing_slash_from_result ) 01789 result = result.left( i ); 01790 else 01791 result = result.left( i + 1 ); 01792 01793 if (!m_strPath_encoded.isEmpty()) 01794 result = decode(result); 01795 01796 return result; 01797 } 01798 01799 01800 bool KURL::cd( const QString& _dir ) 01801 { 01802 if ( _dir.isEmpty() || m_bIsMalformed ) 01803 return false; 01804 01805 if (hasSubURL()) 01806 { 01807 KURL::List lst = split( *this ); 01808 KURL &u = lst.last(); 01809 u.cd(_dir); 01810 *this = join( lst ); 01811 return true; 01812 } 01813 01814 // absolute path ? 01815 if ( _dir[0] == '/' ) 01816 { 01817 m_strPath_encoded = QString::null; 01818 m_strPath = _dir; 01819 setHTMLRef( QString::null ); 01820 m_strQuery_encoded = QString::null; 01821 return true; 01822 } 01823 01824 // Users home directory on the local disk ? 01825 if ( ( _dir[0] == '~' ) && ( m_strProtocol == fileProt )) 01826 { 01827 m_strPath_encoded = QString::null; 01828 m_strPath = QDir::homeDirPath(); 01829 m_strPath += "/"; 01830 m_strPath += _dir.right(m_strPath.length() - 1); 01831 setHTMLRef( QString::null ); 01832 m_strQuery_encoded = QString::null; 01833 return true; 01834 } 01835 01836 // relative path 01837 // we always work on the past of the first url. 01838 // Sub URLs are not touched. 01839 01840 // append '/' if necessary 01841 QString p = path(1); 01842 p += _dir; 01843 p = cleanpath( p, true, false ); 01844 setPath( p ); 01845 01846 setHTMLRef( QString::null ); 01847 m_strQuery_encoded = QString::null; 01848 01849 return true; 01850 } 01851 01852 KURL KURL::upURL( ) const 01853 { 01854 if (!query().isEmpty()) 01855 { 01856 KURL u(*this); 01857 u._setQuery(QString::null); 01858 return u; 01859 }; 01860 01861 if (!hasSubURL()) 01862 { 01863 KURL u(*this); 01864 01865 u.cd("../"); 01866 01867 return u; 01868 } 01869 01870 // We have a subURL. 01871 KURL::List lst = split( *this ); 01872 if (lst.isEmpty()) 01873 return KURL(); // Huh? 01874 while (true) 01875 { 01876 KURL &u = lst.last(); 01877 QString old = u.path(); 01878 u.cd("../"); 01879 if (u.path() != old) 01880 break; // Finshed. 01881 if (lst.count() == 1) 01882 break; // Finished. 01883 lst.remove(lst.fromLast()); 01884 } 01885 return join( lst ); 01886 } 01887 01888 QString KURL::htmlRef() const 01889 { 01890 if ( !hasSubURL() ) 01891 { 01892 return decode( ref() ); 01893 } 01894 01895 List lst = split( *this ); 01896 return decode( (*lst.begin()).ref() ); 01897 } 01898 01899 QString KURL::encodedHtmlRef() const 01900 { 01901 if ( !hasSubURL() ) 01902 { 01903 return ref(); 01904 } 01905 01906 List lst = split( *this ); 01907 return (*lst.begin()).ref(); 01908 } 01909 01910 void KURL::setHTMLRef( const QString& _ref ) 01911 { 01912 if ( !hasSubURL() ) 01913 { 01914 m_strRef_encoded = encode( _ref, 0, 0 /*?*/); 01915 return; 01916 } 01917 01918 List lst = split( *this ); 01919 01920 (*lst.begin()).setRef( encode( _ref, 0, 0 /*?*/) ); 01921 01922 *this = join( lst ); 01923 } 01924 01925 bool KURL::hasHTMLRef() const 01926 { 01927 if ( !hasSubURL() ) 01928 { 01929 return hasRef(); 01930 } 01931 01932 List lst = split( *this ); 01933 return (*lst.begin()).hasRef(); 01934 } 01935 01936 void 01937 KURL::setProtocol( const QString& _txt ) 01938 { 01939 m_strProtocol = _txt; 01940 if ( m_iUriMode == Auto ) m_iUriMode = uriModeForProtocol( m_strProtocol ); 01941 m_bIsMalformed = false; 01942 } 01943 01944 void 01945 KURL::setUser( const QString& _txt ) 01946 { 01947 if ( _txt.isEmpty() ) 01948 m_strUser = QString::null; 01949 else 01950 m_strUser = _txt; 01951 } 01952 01953 void 01954 KURL::setPass( const QString& _txt ) 01955 { 01956 if ( _txt.isEmpty() ) 01957 m_strPass = QString::null; 01958 else 01959 m_strPass = _txt; 01960 } 01961 01962 void 01963 KURL::setHost( const QString& _txt ) 01964 { 01965 if ( m_iUriMode == Auto ) 01966 m_iUriMode = URL; 01967 switch ( m_iUriMode ) 01968 { 01969 case URL: 01970 #ifndef KDE_QT_ONLY 01971 m_strHost = KIDNA::toUnicode(_txt); 01972 if (m_strHost.isEmpty()) 01973 m_strHost = _txt.lower(); // Probably an invalid hostname, but... 01974 #else 01975 m_strHost = _txt.lower(); 01976 #endif 01977 break; 01978 default: 01979 m_strHost = _txt; 01980 break; 01981 } 01982 } 01983 01984 void 01985 KURL::setPort( unsigned short int _p ) 01986 { 01987 m_iPort = _p; 01988 } 01989 01990 void KURL::setPath( const QString & path ) 01991 { 01992 if (isEmpty()) 01993 m_bIsMalformed = false; 01994 if (m_strProtocol.isEmpty()) 01995 { 01996 m_strProtocol = fileProt; 01997 } 01998 m_strPath = path; 01999 m_strPath_encoded = QString::null; 02000 if ( m_iUriMode == Auto ) 02001 m_iUriMode = URL; 02002 } 02003 02004 void KURL::setDirectory( const QString &dir) 02005 { 02006 if ( dir.endsWith("/")) 02007 setPath(dir); 02008 else 02009 setPath(dir+"/"); 02010 } 02011 02012 void KURL::setQuery( const QString &_txt, int encoding_hint) 02013 { 02014 if (_txt[0] == '?') 02015 _setQuery( _txt.length() > 1 ? _txt.mid(1) : "" /*empty, not null*/, encoding_hint ); 02016 else 02017 _setQuery( _txt, encoding_hint ); 02018 } 02019 02020 // This is a private function that expects a query without '?' 02021 void KURL::_setQuery( const QString &_txt, int encoding_hint) 02022 { 02023 m_strQuery_encoded = _txt; 02024 if (!_txt.length()) 02025 return; 02026 02027 int l = m_strQuery_encoded.length(); 02028 int i = 0; 02029 QString result; 02030 while (i < l) 02031 { 02032 int s = i; 02033 // Re-encode. Break encoded string up according to the reserved 02034 // characters '&:;=/?' and re-encode part by part. 02035 while(i < l) 02036 { 02037 char c = m_strQuery_encoded[i].latin1(); 02038 if ((c == '&') || (c == ':') || (c == ';') || 02039 (c == '=') || (c == '/') || (c == '?')) 02040 break; 02041 i++; 02042 } 02043 if (i > s) 02044 { 02045 QString tmp = m_strQuery_encoded.mid(s, i-s); 02046 QString newTmp; 02047 decode( tmp, newTmp, tmp, encoding_hint, false ); 02048 result += tmp; 02049 } 02050 if (i < l) 02051 { 02052 result += m_strQuery_encoded[i]; 02053 i++; 02054 } 02055 } 02056 m_strQuery_encoded = result; 02057 } 02058 02059 QString KURL::query() const 02060 { 02061 if (m_strQuery_encoded.isNull()) 02062 return QString::null; 02063 return '?'+m_strQuery_encoded; 02064 } 02065 02066 QString KURL::decode_string(const QString &str, int encoding_hint) 02067 { 02068 return decode(str, encoding_hint); 02069 } 02070 02071 QString KURL::encode_string(const QString &str, int encoding_hint) 02072 { 02073 return encode(str, 1, encoding_hint); 02074 } 02075 02076 QString KURL::encode_string_no_slash(const QString &str, int encoding_hint) 02077 { 02078 return encode(str, 0, encoding_hint); 02079 } 02080 02081 bool urlcmp( const QString& _url1, const QString& _url2 ) 02082 { 02083 // Both empty ? 02084 if ( _url1.isEmpty() && _url2.isEmpty() ) 02085 return true; 02086 // Only one empty ? 02087 if ( _url1.isEmpty() || _url2.isEmpty() ) 02088 return false; 02089 02090 KURL::List list1 = KURL::split( _url1 ); 02091 KURL::List list2 = KURL::split( _url2 ); 02092 02093 // Malformed ? 02094 if ( list1.isEmpty() || list2.isEmpty() ) 02095 return false; 02096 02097 return ( list1 == list2 ); 02098 } 02099 02100 bool urlcmp( const QString& _url1, const QString& _url2, bool _ignore_trailing, bool _ignore_ref ) 02101 { 02102 // Both empty ? 02103 if ( _url1.isEmpty() && _url2.isEmpty() ) 02104 return true; 02105 // Only one empty ? 02106 if ( _url1.isEmpty() || _url2.isEmpty() ) 02107 return false; 02108 02109 KURL::List list1 = KURL::split( _url1 ); 02110 KURL::List list2 = KURL::split( _url2 ); 02111 02112 // Malformed ? 02113 if ( list1.isEmpty() || list2.isEmpty() ) 02114 return false; 02115 02116 unsigned int size = list1.count(); 02117 if ( list2.count() != size ) 02118 return false; 02119 02120 if ( _ignore_ref ) 02121 { 02122 (*list1.begin()).setRef(QString::null); 02123 (*list2.begin()).setRef(QString::null); 02124 } 02125 02126 KURL::List::Iterator it1 = list1.begin(); 02127 KURL::List::Iterator it2 = list2.begin(); 02128 for( ; it1 != list1.end() ; ++it1, ++it2 ) 02129 if ( !(*it1).equals( *it2, _ignore_trailing ) ) 02130 return false; 02131 02132 return true; 02133 } 02134 02135 QMap< QString, QString > KURL::queryItems( int options ) const { 02136 return queryItems(options, 0); 02137 } 02138 02139 QMap< QString, QString > KURL::queryItems( int options, int encoding_hint ) const { 02140 if ( m_strQuery_encoded.isEmpty() ) 02141 return QMap<QString,QString>(); 02142 02143 QMap< QString, QString > result; 02144 QStringList items = QStringList::split( '&', m_strQuery_encoded ); 02145 for ( QStringList::const_iterator it = items.begin() ; it != items.end() ; ++it ) { 02146 int equal_pos = (*it).find( '=' ); 02147 if ( equal_pos > 0 ) { // = is not the first char... 02148 QString name = (*it).left( equal_pos ); 02149 if ( options & CaseInsensitiveKeys ) 02150 name = name.lower(); 02151 QString value = (*it).mid( equal_pos + 1 ); 02152 if ( value.isEmpty() ) 02153 result.insert( name, QString::fromLatin1("") ); 02154 else { 02155 // ### why is decoding name not necessary? 02156 value.replace( '+', ' ' ); // + in queries means space 02157 result.insert( name, decode_string( value, encoding_hint ) ); 02158 } 02159 } else if ( equal_pos < 0 ) { // no = 02160 QString name = (*it); 02161 if ( options & CaseInsensitiveKeys ) 02162 name = name.lower(); 02163 result.insert( name, QString::null ); 02164 } 02165 } 02166 02167 return result; 02168 } 02169 02170 QString KURL::queryItem( const QString& _item ) const 02171 { 02172 return queryItem( _item, 0 ); 02173 } 02174 02175 QString KURL::queryItem( const QString& _item, int encoding_hint ) const 02176 { 02177 QString item = _item + '='; 02178 if ( m_strQuery_encoded.length() <= 1 ) 02179 return QString::null; 02180 02181 QStringList items = QStringList::split( '&', m_strQuery_encoded ); 02182 unsigned int _len = item.length(); 02183 for ( QStringList::ConstIterator it = items.begin(); it != items.end(); ++it ) 02184 { 02185 if ( (*it).startsWith( item ) ) 02186 { 02187 if ( (*it).length() > _len ) 02188 { 02189 QString str = (*it).mid( _len ); 02190 str.replace( '+', ' ' ); // + in queries means space. 02191 return decode_string( str, encoding_hint ); 02192 } 02193 else // empty value 02194 return QString::fromLatin1(""); 02195 } 02196 } 02197 02198 return QString::null; 02199 } 02200 02201 void KURL::removeQueryItem( const QString& _item ) 02202 { 02203 QString item = _item + '='; 02204 if ( m_strQuery_encoded.length() <= 1 ) 02205 return; 02206 02207 QStringList items = QStringList::split( '&', m_strQuery_encoded ); 02208 for ( QStringList::Iterator it = items.begin(); it != items.end(); ) 02209 { 02210 if ( (*it).startsWith( item ) || (*it == _item) ) 02211 { 02212 QStringList::Iterator deleteIt = it; 02213 ++it; 02214 items.remove(deleteIt); 02215 } 02216 else 02217 { 02218 ++it; 02219 } 02220 } 02221 m_strQuery_encoded = items.join( "&" ); 02222 } 02223 02224 void KURL::addQueryItem( const QString& _item, const QString& _value, int encoding_hint ) 02225 { 02226 QString item = _item + '='; 02227 QString value = encode( _value, 0, encoding_hint ); 02228 02229 if (!m_strQuery_encoded.isEmpty()) 02230 m_strQuery_encoded += '&'; 02231 m_strQuery_encoded += item + value; 02232 } 02233 02234 // static 02235 KURL KURL::fromPathOrURL( const QString& text ) 02236 { 02237 if ( text.isEmpty() ) 02238 return KURL(); 02239 02240 KURL url; 02241 if (!QDir::isRelativePath(text)) 02242 url.setPath( text ); 02243 else 02244 url = text; 02245 02246 return url; 02247 } 02248 02249 static QString _relativePath(const QString &base_dir, const QString &path, bool &isParent) 02250 { 02251 QString _base_dir(QDir::cleanDirPath(base_dir)); 02252 QString _path(QDir::cleanDirPath(path.isEmpty() || (path[0] != '/') ? _base_dir+"/"+path : path)); 02253 02254 if (_base_dir.isEmpty()) 02255 return _path; 02256 02257 if (_base_dir[_base_dir.length()-1] != '/') 02258 _base_dir.append('/'); 02259 02260 QStringList list1 = QStringList::split('/', _base_dir); 02261 QStringList list2 = QStringList::split('/', _path); 02262 02263 // Find where they meet 02264 uint level = 0; 02265 uint maxLevel = QMIN(list1.count(), list2.count()); 02266 while((level < maxLevel) && (list1[level] == list2[level])) level++; 02267 02268 QString result; 02269 // Need to go down out of the first path to the common branch. 02270 for(uint i = level; i < list1.count(); i++) 02271 result.append("../"); 02272 02273 // Now up up from the common branch to the second path. 02274 for(uint i = level; i < list2.count(); i++) 02275 result.append(list2[i]).append("/"); 02276 02277 if ((level < list2.count()) && (path[path.length()-1] != '/')) 02278 result.truncate(result.length()-1); 02279 02280 isParent = (level == list1.count()); 02281 02282 return result; 02283 } 02284 02285 QString KURL::relativePath(const QString &base_dir, const QString &path, bool *isParent) 02286 { 02287 bool parent = false; 02288 QString result = _relativePath(base_dir, path, parent); 02289 if (parent) 02290 result.prepend("./"); 02291 02292 if (isParent) 02293 *isParent = parent; 02294 02295 return result; 02296 } 02297 02298 02299 QString KURL::relativeURL(const KURL &base_url, const KURL &url, int encoding_hint) 02300 { 02301 if ((url.protocol() != base_url.protocol()) || 02302 (url.host() != base_url.host()) || 02303 (url.port() && url.port() != base_url.port()) || 02304 (url.hasUser() && url.user() != base_url.user()) || 02305 (url.hasPass() && url.pass() != base_url.pass())) 02306 { 02307 return url.url(0, encoding_hint); 02308 } 02309 02310 QString relURL; 02311 02312 if ((base_url.path() != url.path()) || (base_url.query() != url.query())) 02313 { 02314 bool dummy; 02315 QString basePath = base_url.directory(false, false); 02316 relURL = encode( _relativePath(basePath, url.path(), dummy), 1, encoding_hint); 02317 relURL += url.query(); 02318 } 02319 02320 if ( url.hasRef() ) 02321 { 02322 relURL += "#"; 02323 relURL += url.ref(); 02324 } 02325 02326 if ( relURL.isEmpty() ) 02327 return "./"; 02328 02329 return relURL; 02330 } 02331 02332 int KURL::uriMode() const 02333 { 02334 return m_iUriMode; 02335 } 02336 02337 KURL::URIMode KURL::uriModeForProtocol(const QString& protocol) 02338 { 02339 #ifndef KDE_QT_ONLY 02340 KURL::URIMode mode = Auto; 02341 if (protocol == fileProt) 02342 return URL; 02343 if (KGlobal::_instance) 02344 mode = KProtocolInfo::uriParseMode(protocol); 02345 if (mode == Auto ) { 02346 #else 02347 KURL::URIMode mode = Auto; 02348 #endif 02349 if ( protocol == "ed2k" || protocol == "sig2dat" || protocol == "slsk" || protocol == "data" ) mode = RawURI; 02350 else if ( protocol == "mailto" ) mode = Mailto; 02351 else mode = URL; 02352 #ifndef KDE_QT_ONLY 02353 } 02354 #endif 02355 return mode; 02356 }