KDECore
kurl.cpp
Go to the documentation of this file.
00001 /* 00002 Copyright (C) 1999 Torben Weis <weis@kde.org> 00003 Copyright (C) 2005-2006 David Faure <faure@kde.org> 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Library General Public 00007 License as published by the Free Software Foundation; either 00008 version 2 of the License, or (at your option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Library General Public License for more details. 00014 00015 You should have received a copy of the GNU Library General Public License 00016 along with this library; see the file COPYING.LIB. If not, write to 00017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00018 Boston, MA 02110-1301, USA. 00019 */ 00020 00022 00023 /* 00024 * The currently active RFC for URL/URIs is RFC3986 00025 * Previous (and now deprecated) RFCs are RFC1738 and RFC2396 00026 */ 00027 00028 #include "kurl.h" 00029 00030 #include <kdebug.h> 00031 #include <kglobal.h> 00032 #include <kshell.h> 00033 #include <kstringhandler.h> 00034 00035 #include <stdio.h> 00036 #include <assert.h> 00037 #include <ctype.h> 00038 #include <stdlib.h> 00039 #include <unistd.h> 00040 00041 #include <QtCore/QDir> 00042 #include <QtCore/QMutableStringListIterator> 00043 #include <QtCore/QRegExp> 00044 #include <QtCore/QMimeData> 00045 #include <QtCore/QTextCodec> 00046 00047 #ifdef DEBUG_KURL 00048 static int kurlDebugArea() { static int s_area = KDebug::registerArea("kdecore (KUrl)"); return s_area; } 00049 #endif 00050 00051 static QString cleanpath( const QString &_path, bool cleanDirSeparator, bool decodeDots ) 00052 { 00053 if (_path.isEmpty()) 00054 return QString(); 00055 00056 if (QFileInfo(_path).isRelative()) 00057 return _path; // Don't mangle mailto-style URLs 00058 00059 QString path = _path; 00060 00061 int len = path.length(); 00062 00063 if (decodeDots) 00064 { 00065 static const QString &encodedDot = KGlobal::staticQString("%2e"); 00066 if (path.indexOf(encodedDot, 0, Qt::CaseInsensitive) != -1) 00067 { 00068 static const QString &encodedDOT = KGlobal::staticQString("%2E"); // Uppercase! 00069 path.replace(encodedDot, QString(QLatin1Char('.'))); 00070 path.replace(encodedDOT, QString(QLatin1Char('.'))); 00071 len = path.length(); 00072 } 00073 } 00074 00075 const bool slash = (len && path[len-1] == QLatin1Char('/')) || 00076 (len > 1 && path[len-2] == QLatin1Char('/') && path[len-1] == QLatin1Char('.')); 00077 00078 // The following code cleans up directory path much like 00079 // QDir::cleanPath() except it can be made to ignore multiple 00080 // directory separators by setting the flag to false. That fixes 00081 // bug# 15044, mail.altavista.com and other similar brain-dead server 00082 // implementations that do not follow what has been specified in 00083 // RFC 2396!! (dA) 00084 QString result; 00085 int cdUp, orig_pos, pos; 00086 00087 cdUp = 0; 00088 pos = orig_pos = len; 00089 while ( pos && (pos = path.lastIndexOf(QLatin1Char('/'),--pos)) != -1 ) 00090 { 00091 len = orig_pos - pos - 1; 00092 if ( len == 2 && path[pos+1] == QLatin1Char('.') && path[pos+2] == QLatin1Char('.') ) 00093 cdUp++; 00094 else 00095 { 00096 // Ignore any occurrences of '.' 00097 // This includes entries that simply do not make sense like /..../ 00098 if ( (len || !cleanDirSeparator) && 00099 (len != 1 || path[pos+1] != QLatin1Char('.') ) ) 00100 { 00101 if ( !cdUp ) 00102 result.prepend(path.mid(pos, len+1)); 00103 else 00104 cdUp--; 00105 } 00106 } 00107 orig_pos = pos; 00108 } 00109 00110 #ifdef Q_WS_WIN // prepend drive letter if exists (js) 00111 if (orig_pos >= 2 && path[0].isLetter() && path[1] == QLatin1Char(':') ) { 00112 result.prepend(QString(path[0]) + QLatin1Char(':') ); 00113 } 00114 #endif 00115 00116 if ( result.isEmpty() ) 00117 result = QLatin1Char('/'); 00118 else if ( slash && result[result.length()-1] != QLatin1Char('/') ) 00119 result.append(QLatin1Char('/')); 00120 00121 return result; 00122 } 00123 00124 #ifdef Q_WS_WIN 00125 00126 // returns true if provided arguments desinate letter+colon or double slash 00127 #define IS_DRIVE_OR_DOUBLESLASH(isletter, char1, char2, colon, slash) \ 00128 ((isletter && char2 == colon) || (char1 == slash && char2 == slash)) 00129 00130 // Removes file:/// or file:// or file:/ or / prefix assuming that str 00131 // is (nonempty) Windows absolute path with a drive letter or double slash. 00132 // If there was file protocol, the path is decoded from percent encoding 00133 static QString removeSlashOrFilePrefix(const QString& str) 00134 { 00135 // FIXME this should maybe be replaced with some (faster?)/nicer logic 00136 const int len = str.length(); 00137 if (str[0]==QLatin1Char('f')) { 00138 if ( len > 10 && str.startsWith( QLatin1String( "file:///" ) ) 00139 && IS_DRIVE_OR_DOUBLESLASH(str[8].isLetter(), str[8], str[9], QLatin1Char(':'), QLatin1Char('/')) ) 00140 return QUrl::fromPercentEncoding( str.toLatin1() ).mid(8); 00141 else if ( len > 9 && str.startsWith( QLatin1String( "file://" ) ) 00142 && IS_DRIVE_OR_DOUBLESLASH(str[7].isLetter(), str[7], str[8], QLatin1Char(':'), QLatin1Char('/')) ) 00143 return QUrl::fromPercentEncoding( str.toLatin1() ).mid(7); 00144 else if ( len > 8 && str.startsWith( QLatin1String( "file:/" ) ) 00145 && IS_DRIVE_OR_DOUBLESLASH(str[6].isLetter(), str[6], str[7], QLatin1Char(':'), QLatin1Char('/')) ) 00146 return QUrl::fromPercentEncoding( str.toLatin1() ).mid(6); 00147 } 00148 /* No 'else' here since there can be "f:/" path. */ 00149 00150 /* '/' + drive letter or // */ 00151 if ( len > 2 && str[0] == QLatin1Char('/') 00152 && IS_DRIVE_OR_DOUBLESLASH(str[1].isLetter(), str[1], str[2], QLatin1Char(':'), QLatin1Char('/')) ) 00153 return str.mid(1); 00154 /* drive letter or // */ 00155 else if ( len >= 2 && IS_DRIVE_OR_DOUBLESLASH(str[0].isLetter(), str[0], str[1], QLatin1Char(':'), QLatin1Char('/')) ) 00156 return str; 00157 return QString(); 00158 } 00159 #endif 00160 00161 bool KUrl::isRelativeUrl(const QString &_url) 00162 { 00163 int len = _url.length(); 00164 if (!len) return true; // Very short relative URL. 00165 const QChar *str = _url.unicode(); 00166 00167 // Absolute URL must start with alpha-character 00168 if (!isalpha(str[0].toLatin1())) 00169 return true; // Relative URL 00170 00171 for(int i = 1; i < len; i++) 00172 { 00173 char c = str[i].toLatin1(); // Note: non-latin1 chars return 0! 00174 if (c == ':') 00175 return false; // Absolute URL 00176 00177 // Protocol part may only contain alpha, digit, + or - 00178 if (!isalpha(c) && !isdigit(c) && (c != '+') && (c != '-')) 00179 return true; // Relative URL 00180 } 00181 // URL did not contain ':' 00182 return true; // Relative URL 00183 } 00184 00185 KUrl::List::List(const KUrl &url) 00186 { 00187 append( url ); 00188 } 00189 00190 KUrl::List::List(const QList<KUrl> &list) 00191 : QList<KUrl>(list) 00192 { 00193 } 00194 00195 KUrl::List::List(const QStringList &list) 00196 { 00197 for (QStringList::ConstIterator it = list.begin(); 00198 it != list.end(); 00199 ++it) 00200 { 00201 append( KUrl(*it) ); 00202 } 00203 } 00204 00205 QStringList KUrl::List::toStringList() const 00206 { 00207 return toStringList(KUrl::LeaveTrailingSlash); 00208 } 00209 00210 QStringList KUrl::List::toStringList(KUrl::AdjustPathOption trailing) const 00211 { 00212 QStringList lst; 00213 for(KUrl::List::ConstIterator it = constBegin(); 00214 it != constEnd(); ++it) { 00215 lst.append(it->url(trailing)); 00216 } 00217 return lst; 00218 } 00219 00220 static QByteArray uriListData(const KUrl::List& urls) 00221 { 00222 QList<QByteArray> urlStringList; 00223 KUrl::List::ConstIterator uit = urls.constBegin(); 00224 const KUrl::List::ConstIterator uEnd = urls.constEnd(); 00225 for (; uit != uEnd ; ++uit) { 00226 // Get each URL encoded in utf8 - and since we get it in escaped 00227 // form on top of that, .toLatin1() is fine. 00228 urlStringList.append((*uit).toMimeDataString().toLatin1()); 00229 } 00230 00231 QByteArray uriListData; 00232 for (int i = 0, n = urlStringList.count(); i < n; ++i) { 00233 uriListData += urlStringList.at(i); 00234 if (i < n-1) 00235 uriListData += "\r\n"; 00236 } 00237 return uriListData; 00238 } 00239 00240 static const char s_kdeUriListMime[] = "application/x-kde4-urilist"; 00241 00242 void KUrl::List::populateMimeData( QMimeData* mimeData, 00243 const KUrl::MetaDataMap& metaData, 00244 MimeDataFlags flags ) const 00245 { 00246 mimeData->setData(QString::fromLatin1("text/uri-list"), uriListData(*this)); 00247 00248 if ( ( flags & KUrl::NoTextExport ) == 0 ) 00249 { 00250 QStringList prettyURLsList; 00251 KUrl::List::ConstIterator uit = constBegin(); 00252 const KUrl::List::ConstIterator uEnd = constEnd(); 00253 for ( ; uit != uEnd ; ++uit ) { 00254 QString prettyURL = (*uit).prettyUrl(); 00255 if ( (*uit).protocol() == QLatin1String("mailto") ) { 00256 prettyURL = (*uit).path(); // remove mailto: when pasting into konsole 00257 } 00258 prettyURLsList.append( prettyURL ); 00259 } 00260 00261 QByteArray plainTextData = prettyURLsList.join(QString(QLatin1Char('\n'))).toLocal8Bit(); 00262 if( count() > 1 ) // terminate last line, unless it's the only line 00263 plainTextData.append( "\n" ); 00264 mimeData->setData( QString::fromLatin1("text/plain"), plainTextData ); 00265 } 00266 00267 if ( !metaData.isEmpty() ) 00268 { 00269 QByteArray metaDataData; // :) 00270 for( KUrl::MetaDataMap::const_iterator it = metaData.begin(); it != metaData.end(); ++it ) 00271 { 00272 metaDataData += it.key().toUtf8(); 00273 metaDataData += "$@@$"; 00274 metaDataData += it.value().toUtf8(); 00275 metaDataData += "$@@$"; 00276 } 00277 mimeData->setData( QString::fromLatin1("application/x-kio-metadata"), metaDataData ); 00278 } 00279 } 00280 00281 00282 void KUrl::List::populateMimeData(const KUrl::List& mostLocalUrls, 00283 QMimeData* mimeData, 00284 const KUrl::MetaDataMap& metaData, 00285 MimeDataFlags flags) const 00286 { 00287 // Export the most local urls as text/uri-list and plain text. 00288 mostLocalUrls.populateMimeData(mimeData, metaData, flags); 00289 00290 mimeData->setData(QString::fromLatin1(s_kdeUriListMime), uriListData(*this)); 00291 } 00292 00293 bool KUrl::List::canDecode( const QMimeData *mimeData ) 00294 { 00295 return mimeData->hasFormat(QString::fromLatin1("text/uri-list")) || 00296 mimeData->hasFormat(QString::fromLatin1(s_kdeUriListMime)); 00297 } 00298 00299 QStringList KUrl::List::mimeDataTypes() 00300 { 00301 return QStringList() << QString::fromLatin1(s_kdeUriListMime) << QString::fromLatin1("text/uri-list"); 00302 } 00303 00304 00305 KUrl::List KUrl::List::fromMimeData(const QMimeData *mimeData, 00306 DecodeOptions decodeOptions, 00307 KUrl::MetaDataMap* metaData) 00308 { 00309 00310 KUrl::List uris; 00311 const char* firstMimeType = s_kdeUriListMime; 00312 const char* secondMimeType = "text/uri-list"; 00313 if (decodeOptions == PreferLocalUrls) { 00314 qSwap(firstMimeType, secondMimeType); 00315 } 00316 QByteArray payload = mimeData->data(QString::fromLatin1(firstMimeType)); 00317 if (payload.isEmpty()) 00318 payload = mimeData->data(QString::fromLatin1(secondMimeType)); 00319 if ( !payload.isEmpty() ) { 00320 int c = 0; 00321 const char* d = payload.constData(); 00322 while ( c < payload.size() && d[c] ) { 00323 int f = c; 00324 // Find line end 00325 while (c < payload.size() && d[c] && d[c]!='\r' 00326 && d[c] != '\n') 00327 c++; 00328 QByteArray s( d+f, c-f ); 00329 if ( s[0] != '#' ) // non-comment? 00330 uris.append( KUrl::fromMimeDataByteArray( s ) ); 00331 // Skip junk 00332 while ( c < payload.size() && d[c] && 00333 ( d[c] == '\n' || d[c] == '\r' ) ) 00334 ++c; 00335 } 00336 } 00337 if ( metaData ) 00338 { 00339 const QByteArray metaDataPayload = mimeData->data(QLatin1String("application/x-kio-metadata")); 00340 if ( !metaDataPayload.isEmpty() ) 00341 { 00342 QString str = QString::fromUtf8( metaDataPayload ); 00343 Q_ASSERT(str.endsWith(QLatin1String("$@@$"))); 00344 str.truncate( str.length() - 4 ); 00345 const QStringList lst = str.split(QLatin1String("$@@$")); 00346 QStringList::ConstIterator it = lst.begin(); 00347 bool readingKey = true; // true, then false, then true, etc. 00348 QString key; 00349 for ( ; it != lst.end(); ++it ) { 00350 if ( readingKey ) 00351 key = *it; 00352 else 00353 metaData->insert( key, *it ); 00354 readingKey = !readingKey; 00355 } 00356 Q_ASSERT( readingKey ); // an odd number of items would be, well, odd ;-) 00357 } 00358 } 00359 00360 return uris; 00361 } 00362 00363 KUrl::List KUrl::List::fromMimeData( const QMimeData *mimeData, KUrl::MetaDataMap* metaData ) 00364 { 00365 return fromMimeData(mimeData, PreferKdeUrls, metaData); 00366 } 00367 00368 KUrl::List::operator QVariant() const 00369 { 00370 return qVariantFromValue(*this); 00371 } 00372 00374 00375 KUrl::KUrl() 00376 : QUrl(), d(0) 00377 { 00378 } 00379 00380 KUrl::~KUrl() 00381 { 00382 } 00383 00384 00385 KUrl::KUrl( const QString &str ) 00386 : QUrl(), d(0) 00387 { 00388 if ( !str.isEmpty() ) { 00389 #ifdef Q_WS_WIN 00390 #ifdef DEBUG_KURL 00391 kDebug(kurlDebugArea()) << "KUrl::KUrl ( const QString &str = " << str.toAscii().data() << " )"; 00392 #endif 00393 QString pathToSet; 00394 // when it starts with file:// it's a url and must be valid. we don't care if the 00395 // path exist/ is valid or not 00396 if (!str.startsWith(QLatin1String("file://"))) 00397 pathToSet = removeSlashOrFilePrefix( QDir::fromNativeSeparators(str) ); 00398 if ( !pathToSet.isEmpty() ) { 00399 // we have a prefix indicating this is a local URL 00400 // remember the possible query using _setEncodedUrl(), then set up the correct path without query protocol part 00401 int index = pathToSet.lastIndexOf(QLatin1Char('?')); 00402 if (index == -1) 00403 setPath( pathToSet ); 00404 else { 00405 setPath( pathToSet.left( index ) ); 00406 _setQuery( pathToSet.mid( index + 1 ) ); 00407 } 00408 return; 00409 } 00410 #endif 00411 if ( str[0] == QLatin1Char('/') || str[0] == QLatin1Char('~') ) 00412 setPath( str ); 00413 else { 00414 _setEncodedUrl( str.toUtf8() ); 00415 } 00416 } 00417 } 00418 00419 KUrl::KUrl( const char * str ) 00420 : QUrl(), d(0) 00421 { 00422 #ifdef Q_WS_WIN 00423 // true if @a c is letter 00424 #define IS_LETTER(c) \ 00425 ((c >= QLatin1Char('A') && c <= QLatin1Char('Z')) || (c >= QLatin1Char('a') && c <= QLatin1Char('z'))) 00426 00427 // like IS_DRIVE_OR_DOUBLESLASH, but slash is prepended 00428 #define IS_SLASH_AND_DRIVE_OR_DOUBLESLASH_0 \ 00429 ( QLatin1Char(str[0]) == QLatin1Char('/') && IS_DRIVE_OR_DOUBLESLASH(IS_LETTER(QLatin1Char(str[1])), QLatin1Char(str[1]), QLatin1Char(str[2]), QLatin1Char(':'), QLatin1Char('/')) ) 00430 00431 // like IS_DRIVE_OR_DOUBLESLASH, with characters == str[0] and str[1] 00432 #define IS_DRIVE_OR_DOUBLESLASH_0 \ 00433 ( IS_DRIVE_OR_DOUBLESLASH(IS_LETTER(QLatin1Char(str[0])), QLatin1Char(str[0]), QLatin1Char(str[1]), QLatin1Char(':'), QLatin1Char('/')) ) 00434 00435 #if defined(DEBUG_KURL) 00436 kDebug(kurlDebugArea()) << "KUrl::KUrl " << " " << str; 00437 #endif 00438 if ( str && str[0] && str[1] && str[2] ) { 00439 if ( IS_SLASH_AND_DRIVE_OR_DOUBLESLASH_0 ) 00440 setPath( QString::fromUtf8( str+1 ) ); 00441 else if ( IS_DRIVE_OR_DOUBLESLASH_0 ) 00442 setPath( QString::fromUtf8( str ) ); 00443 } 00444 #endif 00445 if ( str && str[0] ) { 00446 if ( str[0] == '/' || str[0] == '~' ) 00447 setPath( QString::fromUtf8( str ) ); 00448 else 00449 _setEncodedUrl( str ); 00450 } 00451 } 00452 00453 KUrl::KUrl( const QByteArray& str ) 00454 : QUrl(), d(0) 00455 { 00456 if ( !str.isEmpty() ) { 00457 #ifdef Q_WS_WIN 00458 #ifdef DEBUG_KURL 00459 kDebug(kurlDebugArea()) << "KUrl::KUrl " << " " << str.data(); 00460 #endif 00461 if ( IS_SLASH_AND_DRIVE_OR_DOUBLESLASH_0 ) 00462 setPath( QString::fromUtf8( str.mid( 1 ) ) ); 00463 else if ( IS_DRIVE_OR_DOUBLESLASH_0 ) 00464 setPath( QString::fromUtf8( str ) ); 00465 #else 00466 if ( str[0] == '/' || str[0] == '~' ) 00467 setPath( QString::fromUtf8( str ) ); 00468 #endif 00469 else 00470 _setEncodedUrl( str ); 00471 } 00472 } 00473 00474 KUrl::KUrl( const KUrl& _u ) 00475 : QUrl( _u ), d(0) 00476 { 00477 #if defined(Q_WS_WIN) && defined(DEBUG_KURL) 00478 kDebug(kurlDebugArea()) << "KUrl::KUrl(KUrl) " << " path " << _u.path() << " toLocalFile " << _u.toLocalFile(); 00479 #endif 00480 } 00481 00482 KUrl::KUrl( const QUrl &u ) 00483 : QUrl( u ), d(0) 00484 { 00485 #if defined(Q_WS_WIN) && defined(DEBUG_KURL) 00486 kDebug(kurlDebugArea()) << "KUrl::KUrl(Qurl) " << " path " << u.path() << " toLocalFile " << u.toLocalFile(); 00487 #endif 00488 } 00489 00490 KUrl::KUrl( const KUrl& _u, const QString& _rel_url ) 00491 : QUrl(), d(0) 00492 { 00493 #if defined(Q_WS_WIN) && defined(DEBUG_KURL) 00494 kDebug(kurlDebugArea()) << "KUrl::KUrl(KUrl,QString rel_url) " << " path " << _u.path() << " toLocalFile " << _u.toLocalFile(); 00495 #endif 00496 #if 0 00497 if (_u.hasSubUrl()) // Operate on the last suburl, not the first 00498 { 00499 KUrl::List lst = split( _u ); 00500 KUrl u(lst.last(), _rel_url); 00501 lst.erase( --lst.end() ); 00502 lst.append( u ); 00503 *this = join( lst ); 00504 return; 00505 } 00506 #endif 00507 QString rUrl = _rel_url; 00508 00509 // WORKAROUND THE RFC 1606 LOOPHOLE THAT ALLOWS 00510 // http:/index.html AS A VALID SYNTAX FOR RELATIVE 00511 // URLS. ( RFC 2396 section 5.2 item # 3 ) 00512 const int len = _u.scheme().length(); 00513 if ( !_u.host().isEmpty() && !rUrl.isEmpty() && 00514 rUrl.indexOf( _u.scheme(), 0, Qt::CaseInsensitive ) == 0 && 00515 rUrl[len] == QLatin1Char(':') && (rUrl[len+1] != QLatin1Char('/') || 00516 (rUrl[len+1] == QLatin1Char('/') && rUrl[len+2] != QLatin1Char('/'))) ) 00517 { 00518 rUrl.remove( 0, rUrl.indexOf( QLatin1Char(':') ) + 1 ); 00519 } 00520 00521 00522 if ( rUrl.isEmpty() ) 00523 { 00524 *this = _u; 00525 } 00526 else if ( rUrl[0] == QLatin1Char('#') ) 00527 { 00528 *this = _u; 00529 QByteArray strRef_encoded = rUrl.mid(1).toLatin1(); 00530 if ( strRef_encoded.isNull() ) 00531 strRef_encoded = ""; // we know there was an (empty) html ref, we saw the '#' 00532 setEncodedFragment( strRef_encoded ); 00533 } 00534 else if ( isRelativeUrl( rUrl ) ) 00535 { 00536 *this = _u; 00537 setFragment( QString() ); 00538 setEncodedQuery( QByteArray() ); 00539 QString strPath = path(); 00540 if ( rUrl[0] == QLatin1Char('/') ) 00541 { 00542 if ((rUrl.length() > 1) && (rUrl[1] == QLatin1Char('/'))) 00543 { 00544 setHost( QString() ); 00545 setPort( -1 ); 00546 // File protocol returns file:/// without host, strip // from rUrl 00547 if ( _u.isLocalFile() ) 00548 rUrl.remove(0, 2); 00549 } 00550 strPath.clear(); 00551 } 00552 else if ( rUrl[0] != QLatin1Char('?') ) 00553 { 00554 const int pos = strPath.lastIndexOf( QLatin1Char('/') ); 00555 if (pos >= 0) 00556 strPath.truncate(pos); 00557 strPath += QLatin1Char('/'); 00558 } 00559 else 00560 { 00561 if ( strPath.isEmpty() ) 00562 strPath = QLatin1Char('/'); 00563 } 00564 setPath( strPath ); 00565 //kDebug(kurlDebugArea()) << "url()=" << url() << " rUrl=" << rUrl; 00566 const KUrl tmp( url() + rUrl); 00567 //kDebug(kurlDebugArea()) << "assigning tmp=" << tmp.url(); 00568 *this = tmp; 00569 cleanPath(KeepDirSeparators); 00570 } 00571 else 00572 { 00573 const KUrl tmp( rUrl ); 00574 //kDebug(kurlDebugArea()) << "not relative; assigning tmp=" << tmp.url(); 00575 *this = tmp; 00576 // Preserve userinfo if applicable. 00577 if (!_u.userInfo().isEmpty() && userInfo().isEmpty() 00578 && (_u.host() == host()) && (_u.scheme() == scheme())) 00579 { 00580 setUserInfo( _u.userInfo() ); 00581 } 00582 cleanPath(KeepDirSeparators); 00583 } 00584 } 00585 00586 KUrl& KUrl::operator=( const KUrl& _u ) 00587 { 00588 QUrl::operator=( _u ); 00589 return *this; 00590 } 00591 00592 bool KUrl::operator==( const KUrl& _u ) const 00593 { 00594 return QUrl::operator==( _u ); 00595 } 00596 00597 bool KUrl::operator==( const QString& _u ) const 00598 { 00599 KUrl u( _u ); 00600 return ( *this == u ); 00601 } 00602 00603 KUrl::operator QVariant() const 00604 { 00605 return qVariantFromValue(*this); 00606 } 00607 00608 #ifndef KDE_NO_DEPRECATED 00609 bool KUrl::cmp( const KUrl &u, bool ignore_trailing ) const 00610 { 00611 return equals( u, ignore_trailing ? CompareWithoutTrailingSlash : EqualsOptions(0) ); 00612 } 00613 #endif 00614 00615 bool KUrl::equals( const KUrl &_u, const EqualsOptions& options ) const 00616 { 00617 if ( !isValid() || !_u.isValid() ) 00618 return false; 00619 00620 if ( options & CompareWithoutTrailingSlash || options & CompareWithoutFragment ) 00621 { 00622 QString path1 = path((options & CompareWithoutTrailingSlash) ? RemoveTrailingSlash : LeaveTrailingSlash); 00623 QString path2 = _u.path((options & CompareWithoutTrailingSlash) ? RemoveTrailingSlash : LeaveTrailingSlash); 00624 00625 if (options & AllowEmptyPath) { 00626 if (path1 == QLatin1String("/")) 00627 path1.clear(); 00628 if (path2 == QLatin1String("/")) 00629 path2.clear(); 00630 } 00631 00632 #ifdef Q_WS_WIN 00633 const bool bLocal1 = isLocalFile(); 00634 const bool bLocal2 = _u.isLocalFile(); 00635 if ( !bLocal1 && bLocal2 || bLocal1 && !bLocal2 ) 00636 return false; 00637 // local files are case insensitive 00638 if ( bLocal1 && bLocal2 && 0 != QString::compare( path1, path2, Qt::CaseInsensitive ) ) 00639 return false; 00640 #endif 00641 if ( path1 != path2 ) 00642 return false; 00643 00644 if ( scheme() == _u.scheme() && 00645 authority() == _u.authority() && // user+pass+host+port 00646 encodedQuery() == _u.encodedQuery() && 00647 (fragment() == _u.fragment() || options & CompareWithoutFragment ) ) 00648 return true; 00649 00650 return false; 00651 } 00652 00653 return ( *this == _u ); 00654 } 00655 00656 QString KUrl::protocol() const 00657 { 00658 return scheme().toLower(); 00659 } 00660 00661 void KUrl::setProtocol( const QString& proto ) 00662 { 00663 setScheme( proto ); 00664 } 00665 00666 QString KUrl::user() const 00667 { 00668 return userName(); 00669 } 00670 00671 void KUrl::setUser( const QString& user ) 00672 { 00673 setUserName( user ); 00674 } 00675 00676 bool KUrl::hasUser() const 00677 { 00678 return !userName().isEmpty(); 00679 } 00680 00681 QString KUrl::pass() const 00682 { 00683 return password(); 00684 } 00685 00686 void KUrl::setPass( const QString& pass ) 00687 { 00688 setPassword( pass ); 00689 } 00690 00691 bool KUrl::hasPass() const 00692 { 00693 return !password().isEmpty(); 00694 } 00695 00696 bool KUrl::hasHost() const 00697 { 00698 return !host().isEmpty(); 00699 } 00700 00701 bool KUrl::hasPath() const 00702 { 00703 return !path().isEmpty(); 00704 } 00705 00706 KUrl KUrl::fromPath( const QString& text ) 00707 { 00708 KUrl u; 00709 u.setPath( text ); 00710 return u; 00711 } 00712 00713 void KUrl::setFileName( const QString& _txt ) 00714 { 00715 setFragment( QString() ); 00716 int i = 0; 00717 while( i < _txt.length() && _txt[i] == QLatin1Char('/') ) 00718 ++i; 00719 QString tmp = i ? _txt.mid( i ) : _txt; 00720 00721 QString path = this->path(); 00722 if ( path.isEmpty() ) 00723 #ifdef Q_OS_WIN 00724 path = isLocalFile() ? QDir::rootPath() : QLatin1String("/"); 00725 #else 00726 path = QDir::rootPath(); 00727 #endif 00728 else 00729 { 00730 int lastSlash = path.lastIndexOf( QLatin1Char('/') ); 00731 if ( lastSlash == -1) 00732 path.clear(); // there's only the file name, remove it 00733 else if ( !path.endsWith( QLatin1Char('/') ) ) 00734 path.truncate( lastSlash+1 ); // keep the "/" 00735 } 00736 00737 path += tmp; 00738 setPath( path ); 00739 00740 cleanPath(); 00741 } 00742 00743 void KUrl::cleanPath( const CleanPathOption& options ) 00744 { 00745 //if (m_iUriMode != URL) return; 00746 const QString newPath = cleanpath(path(), !(options & KeepDirSeparators), false); 00747 if ( path() != newPath ) 00748 setPath( newPath ); 00749 // WABA: Is this safe when "/../" is encoded with %? 00750 //m_strPath_encoded = cleanpath(m_strPath_encoded, cleanDirSeparator, true); 00751 } 00752 00753 static QString trailingSlash( KUrl::AdjustPathOption trailing, const QString &path ) 00754 { 00755 if ( trailing == KUrl::LeaveTrailingSlash ) { 00756 return path; 00757 } 00758 00759 QString result = path; 00760 00761 if ( trailing == KUrl::AddTrailingSlash ) 00762 { 00763 int len = result.length(); 00764 if ( (len == 0) || (result[ len - 1 ] != QLatin1Char('/')) ) 00765 result += QLatin1Char('/'); 00766 return result; 00767 } 00768 else if ( trailing == KUrl::RemoveTrailingSlash ) 00769 { 00770 if ( result == QLatin1String("/") ) 00771 return result; 00772 int len = result.length(); 00773 while (len > 1 && result[ len - 1 ] == QLatin1Char('/')) 00774 { 00775 len--; 00776 } 00777 result.truncate( len ); 00778 return result; 00779 } 00780 else { 00781 assert( 0 ); 00782 return result; 00783 } 00784 } 00785 00786 void KUrl::adjustPath( AdjustPathOption trailing ) 00787 { 00788 #if 0 00789 if (!m_strPath_encoded.isEmpty()) 00790 { 00791 m_strPath_encoded = trailingSlash( _trailing, m_strPath_encoded ); 00792 } 00793 #endif 00794 const QString newPath = trailingSlash( trailing, path() ); 00795 if ( path() != newPath ) 00796 setPath( newPath ); 00797 } 00798 00799 00800 QString KUrl::encodedPathAndQuery( AdjustPathOption trailing , const EncodedPathAndQueryOptions &options) const 00801 { 00802 QString encodedPath; 00803 #ifdef Q_OS_WIN 00804 // see KUrl::path() 00805 if (isLocalFile()) { 00806 // ### this is probably broken 00807 encodedPath = trailingSlash(trailing, QUrl::toLocalFile()); 00808 encodedPath = QString::fromLatin1(QUrl::toPercentEncoding(encodedPath, "!$&'()*+,;=:@/")); 00809 } else { 00810 encodedPath = trailingSlash(trailing, QString::fromLatin1(QUrl::encodedPath())); 00811 } 00812 #else 00813 encodedPath = trailingSlash(trailing, QString::fromLatin1(QUrl::encodedPath())); 00814 #endif 00815 00816 if ((options & AvoidEmptyPath) && encodedPath.isEmpty()) { 00817 encodedPath.append(QLatin1Char('/')); 00818 } 00819 00820 if (hasQuery()) { 00821 return encodedPath + QLatin1Char('?') + QString::fromLatin1(encodedQuery()); 00822 } else { 00823 return encodedPath; 00824 } 00825 } 00826 00827 #if 0 00828 void KUrl::setEncodedPath( const QString& _txt, int encoding_hint ) 00829 { 00830 m_strPath_encoded = _txt; 00831 00832 decode( m_strPath_encoded, m_strPath, m_strPath_encoded, encoding_hint ); 00833 // Throw away encoding for local files, makes file-operations faster. 00834 if (m_strProtocol == "file") 00835 m_strPath_encoded.clear(); 00836 00837 if ( m_iUriMode == Auto ) 00838 m_iUriMode = URL; 00839 } 00840 #endif 00841 00842 void KUrl::setEncodedPathAndQuery( const QString& _txt ) 00843 { 00844 const int pos = _txt.indexOf(QLatin1Char('?')); 00845 if ( pos == -1 ) 00846 { 00847 setPath( QUrl::fromPercentEncoding( _txt.toLatin1() ) ); 00848 setEncodedQuery( QByteArray() ); 00849 } 00850 else 00851 { 00852 setPath( QUrl::fromPercentEncoding(_txt.toLatin1().left(pos)) ); 00853 _setQuery( _txt.right( _txt.length() - pos - 1 ) ); 00854 } 00855 } 00856 00857 QString KUrl::path( AdjustPathOption trailing ) const 00858 { 00859 #ifdef Q_WS_WIN 00860 #ifdef DEBUG_KURL 00861 kWarning() << (isLocalFile() ? "converted to local file - the related call should be converted to toLocalFile()" : "") << QUrl::path(); 00862 #endif 00863 return trailingSlash( trailing, isLocalFile() ? QUrl::toLocalFile() : QUrl::path() ); 00864 #else 00865 return trailingSlash( trailing, QUrl::path() ); 00866 #endif 00867 } 00868 00869 QString KUrl::toLocalFile( AdjustPathOption trailing ) const 00870 { 00871 if (hasHost() && isLocalFile()) { 00872 KUrl urlWithoutHost(*this); 00873 urlWithoutHost.setHost(QString()); 00874 return trailingSlash(trailing, urlWithoutHost.toLocalFile()); 00875 } 00876 return trailingSlash(trailing, QUrl::toLocalFile()); 00877 } 00878 00879 inline static bool hasSubUrl( const QUrl& url ); 00880 00881 static inline bool isLocalFile( const QUrl& url ) 00882 { 00883 if ( ( url.scheme() != QLatin1String("file") ) || hasSubUrl( url ) ) 00884 return false; 00885 00886 if (url.host().isEmpty() || (url.host() == QLatin1String("localhost"))) 00887 return true; 00888 00889 char hostname[ 256 ]; 00890 hostname[ 0 ] = '\0'; 00891 if (!gethostname( hostname, 255 )) 00892 hostname[sizeof(hostname)-1] = '\0'; 00893 00894 for(char *p = hostname; *p; p++) 00895 *p = tolower(*p); 00896 00897 return (url.host() == QString::fromLatin1( hostname )); 00898 } 00899 00900 bool KUrl::isLocalFile() const 00901 { 00902 return ::isLocalFile( *this ); 00903 } 00904 00905 void KUrl::setFileEncoding(const QString &encoding) 00906 { 00907 if (!isLocalFile()) 00908 return; 00909 00910 QString q = query(); 00911 00912 if (!q.isEmpty() && q[0] == QLatin1Char('?')) 00913 q = q.mid(1); 00914 00915 QStringList args = q.split(QLatin1Char('&'), QString::SkipEmptyParts); 00916 for(QStringList::Iterator it = args.begin(); 00917 it != args.end();) 00918 { 00919 QString s = QUrl::fromPercentEncoding( (*it).toLatin1() ); 00920 if (s.startsWith(QLatin1String("charset="))) 00921 it = args.erase(it); 00922 else 00923 ++it; 00924 } 00925 if (!encoding.isEmpty()) 00926 args.append(QLatin1String("charset=") + QString::fromLatin1(QUrl::toPercentEncoding(encoding))); 00927 00928 if (args.isEmpty()) 00929 _setQuery(QString()); 00930 else 00931 _setQuery(args.join(QString(QLatin1Char('&')))); 00932 } 00933 00934 QString KUrl::fileEncoding() const 00935 { 00936 if (!isLocalFile()) 00937 return QString(); 00938 00939 QString q = query(); 00940 00941 if (q.isEmpty()) 00942 return QString(); 00943 00944 if (q[0] == QLatin1Char('?')) 00945 q = q.mid(1); 00946 00947 const QStringList args = q.split(QLatin1Char('&'), QString::SkipEmptyParts); 00948 for(QStringList::ConstIterator it = args.begin(); 00949 it != args.end(); 00950 ++it) 00951 { 00952 QString s = QUrl::fromPercentEncoding((*it).toLatin1()); 00953 if (s.startsWith(QLatin1String("charset="))) 00954 return s.mid(8); 00955 } 00956 return QString(); 00957 } 00958 00959 inline static bool hasSubUrl( const QUrl& url ) 00960 { 00961 // The isValid call triggers QUrlPrivate::validate which needs the full encoded url, 00962 // all this takes too much time for isLocalFile() 00963 const QString scheme = url.scheme(); 00964 if ( scheme.isEmpty() /*|| !isValid()*/ ) 00965 return false; 00966 const QString ref( url.fragment() ); 00967 if (ref.isEmpty()) 00968 return false; 00969 switch ( ref.at(0).unicode() ) { 00970 case 'g': 00971 if ( ref.startsWith(QLatin1String("gzip:")) ) 00972 return true; 00973 break; 00974 case 'b': 00975 if ( ref.startsWith(QLatin1String("bzip:")) || ref.startsWith(QLatin1String("bzip2:")) ) 00976 return true; 00977 break; 00978 case 'l': 00979 if ( ref.startsWith(QLatin1String("lzma:")) ) 00980 return true; 00981 break; 00982 case 'x': 00983 if ( ref.startsWith(QLatin1String("xz:")) ) 00984 return true; 00985 break; 00986 case 't': 00987 if ( ref.startsWith(QLatin1String("tar:")) ) 00988 return true; 00989 break; 00990 case 'a': 00991 if ( ref.startsWith(QLatin1String("ar:")) ) 00992 return true; 00993 break; 00994 case 'z': 00995 if ( ref.startsWith(QLatin1String("zip:")) ) 00996 return true; 00997 break; 00998 default: 00999 break; 01000 } 01001 if ( scheme == QLatin1String("error") ) // anything that starts with error: has suburls 01002 return true; 01003 return false; 01004 } 01005 01006 bool KUrl::hasSubUrl() const 01007 { 01008 return ::hasSubUrl( *this ); 01009 } 01010 01011 QString KUrl::url( AdjustPathOption trailing ) const 01012 { 01013 if (QString::compare(scheme(), QLatin1String("mailto"), Qt::CaseInsensitive) == 0) { 01014 // mailto urls should be prettified, see the url183433 testcase. 01015 return prettyUrl(trailing); 01016 } 01017 if ( trailing == AddTrailingSlash && !path().endsWith( QLatin1Char('/') ) ) { 01018 // -1 and 0 are provided by QUrl, but not +1, so that one is a bit tricky. 01019 // To avoid reimplementing toEncoded() all over again, I just use another QUrl 01020 // Let's hope this is fast, or not called often... 01021 QUrl newUrl( *this ); 01022 newUrl.setPath( path() + QLatin1Char('/') ); 01023 return QString::fromLatin1(newUrl.toEncoded()); 01024 } 01025 else if ( trailing == RemoveTrailingSlash) { 01026 const QString cleanedPath = trailingSlash(trailing, path()); 01027 if (cleanedPath == QLatin1String("/")) { 01028 if (path() != QLatin1String("/")) { 01029 QUrl fixedUrl = *this; 01030 fixedUrl.setPath(cleanedPath); 01031 return QLatin1String(fixedUrl.toEncoded(None)); 01032 } 01033 return QLatin1String(toEncoded(None)); 01034 } 01035 } 01036 return QString::fromLatin1(toEncoded(trailing == RemoveTrailingSlash ? StripTrailingSlash : None)); 01037 } 01038 01039 static QString toPrettyPercentEncoding(const QString &input, bool forFragment) 01040 { 01041 QString result; 01042 result.reserve(input.length()); 01043 for (int i = 0; i < input.length(); ++i) { 01044 const QChar c = input.at(i); 01045 register ushort u = c.unicode(); 01046 if (u < 0x20 01047 || (!forFragment && u == '?') // don't escape '?' in fragments, not needed and wrong (#173101) 01048 || u == '#' || u == '%' 01049 || (u == ' ' && (i+1 == input.length() || input.at(i+1).unicode() == ' '))) { 01050 static const char hexdigits[] = "0123456789ABCDEF"; 01051 result += QLatin1Char('%'); 01052 result += QLatin1Char(hexdigits[(u & 0xf0) >> 4]); 01053 result += QLatin1Char(hexdigits[u & 0xf]); 01054 } else { 01055 result += c; 01056 } 01057 } 01058 01059 return result; 01060 } 01061 01062 QString KUrl::prettyUrl( AdjustPathOption trailing ) const 01063 { 01064 // reconstruct the URL in a "pretty" form 01065 // a "pretty" URL is NOT suitable for data transfer. It's only for showing data to the user. 01066 // however, it must be parseable back to its original state, since 01067 // notably Konqueror displays it in the Location address. 01068 01069 // A pretty URL is the same as a normal URL, except that: 01070 // - the password is removed 01071 // - the hostname is shown in Unicode (as opposed to ACE/Punycode) 01072 // - the pathname and fragment parts are shown in Unicode (as opposed to %-encoding) 01073 QString result = scheme(); 01074 if (!result.isEmpty()) 01075 { 01076 if(!authority().isEmpty() || result == QLatin1String("file")) 01077 result += QLatin1String("://"); 01078 else 01079 result += QLatin1Char(':'); 01080 } 01081 01082 QString tmp = userName(); 01083 if (!tmp.isEmpty()) { 01084 result += QString::fromLatin1(QUrl::toPercentEncoding(tmp)); 01085 result += QLatin1Char('@'); 01086 } 01087 01088 // Check if host is an ipv6 address 01089 tmp = host(); 01090 if (tmp.contains(QLatin1Char(':'))) 01091 result += QLatin1Char('[') + tmp + QLatin1Char(']'); 01092 else 01093 result += tmp; 01094 01095 if (port() != -1) { 01096 result += QLatin1Char(':'); 01097 result += QString::number(port()); 01098 } 01099 01100 tmp = path(); 01101 #ifdef Q_WS_WIN 01102 if (isLocalFile()) 01103 tmp.prepend(QLatin1Char('/')); // KUrl::path() returns toLocalFile() on windows so we need to add the / back to create a proper url 01104 #endif 01105 result += toPrettyPercentEncoding(tmp, false); 01106 01107 // adjust the trailing slash, if necessary 01108 if (trailing == AddTrailingSlash && !tmp.endsWith(QLatin1Char('/'))) 01109 result += QLatin1Char('/'); 01110 else if (trailing == RemoveTrailingSlash && tmp.length() > 1 && tmp.endsWith(QLatin1Char('/'))) 01111 result.chop(1); 01112 01113 if (hasQuery()) { 01114 result += QLatin1Char('?'); 01115 result += QString::fromLatin1(encodedQuery()); 01116 } 01117 01118 if (hasFragment()) { 01119 result += QLatin1Char('#'); 01120 result += toPrettyPercentEncoding(fragment(), true); 01121 } 01122 01123 return result; 01124 } 01125 01126 #if 0 01127 QString KUrl::prettyUrl( int _trailing, AdjustementFlags _flags) const 01128 { 01129 QString u = prettyUrl(_trailing); 01130 if (_flags & StripFileProtocol && u.startsWith("file://")) { 01131 u.remove(0, 7); 01132 #ifdef Q_WS_WIN 01133 return QDir::convertSeparators(u); 01134 #endif 01135 } 01136 return u; 01137 } 01138 #endif 01139 01140 QString KUrl::pathOrUrl() const 01141 { 01142 return pathOrUrl(LeaveTrailingSlash); 01143 } 01144 01145 QString KUrl::pathOrUrl(AdjustPathOption trailing) const 01146 { 01147 if ( isLocalFile() && fragment().isNull() && encodedQuery().isNull() ) { 01148 return toLocalFile(trailing); 01149 } else { 01150 return prettyUrl(trailing); 01151 } 01152 } 01153 01154 // Used for text/uri-list in the mime data 01155 QString KUrl::toMimeDataString() const // don't fold this into populateMimeData, it's also needed by other code like konqdrag 01156 { 01157 if ( isLocalFile() ) 01158 { 01159 #if 1 01160 return url(); 01161 #else 01162 // According to the XDND spec, file:/ URLs for DND must have 01163 // the hostname part. But in really it just breaks many apps, 01164 // so it's disabled for now. 01165 const QString s = url( 0, KGlobal::locale()->fileEncodingMib() ); 01166 if( !s.startsWith( QLatin1String ( "file://" ) )) 01167 { 01168 char hostname[257]; 01169 if ( gethostname( hostname, 255 ) == 0 ) 01170 { 01171 hostname[256] = '\0'; 01172 return QString( "file://" ) + hostname + s.mid( 5 ); 01173 } 01174 } 01175 #endif 01176 } 01177 01178 if (hasPass()) { 01179 KUrl safeUrl(*this); 01180 safeUrl.setPassword(QString()); 01181 return safeUrl.url(); 01182 } 01183 return url(); 01184 } 01185 01186 KUrl KUrl::fromMimeDataByteArray( const QByteArray& str ) 01187 { 01188 if ( str.startsWith( "file:" ) ) 01189 return KUrl( str /*, QTextCodec::codecForLocale()->mibEnum()*/ ); 01190 01191 return KUrl( str /*, 106*/ ); // 106 is mib enum for utf8 codec; 01192 } 01193 01194 KUrl::List KUrl::split( const KUrl& _url ) 01195 { 01196 QString ref; 01197 bool hasRef; 01198 KUrl::List lst; 01199 KUrl url = _url; 01200 01201 while(true) 01202 { 01203 KUrl u = url; 01204 u.setFragment( QString() ); 01205 lst.append(u); 01206 if (url.hasSubUrl()) 01207 { 01208 url = KUrl(url.fragment()); 01209 } 01210 else 01211 { 01212 ref = url.fragment(); 01213 hasRef = url.hasFragment(); 01214 break; 01215 } 01216 } 01217 01218 if ( hasRef ) 01219 { 01220 // Set HTML ref in all URLs. 01221 KUrl::List::Iterator it; 01222 for( it = lst.begin() ; it != lst.end(); ++it ) 01223 { 01224 (*it).setFragment( ref ); 01225 } 01226 } 01227 01228 return lst; 01229 } 01230 01231 KUrl::List KUrl::split( const QString& _url ) 01232 { 01233 return split(KUrl(_url)); 01234 } 01235 01236 KUrl KUrl::join( const KUrl::List & lst ) 01237 { 01238 if (lst.isEmpty()) return KUrl(); 01239 KUrl tmp; 01240 01241 bool first = true; 01242 QListIterator<KUrl> it(lst); 01243 it.toBack(); 01244 while (it.hasPrevious()) 01245 { 01246 KUrl u(it.previous()); 01247 if (!first) { 01248 u.setEncodedFragment(tmp.url().toLatin1() /* TODO double check encoding */); 01249 } 01250 tmp = u; 01251 01252 first = false; 01253 } 01254 01255 return tmp; 01256 } 01257 01258 QString KUrl::fileName( const DirectoryOptions& options ) const 01259 { 01260 Q_ASSERT( options != 0 ); //Disallow options == false 01261 QString fname; 01262 if (hasSubUrl()) { // If we have a suburl, then return the filename from there 01263 const KUrl::List list = KUrl::split(*this); 01264 return list.last().fileName(options); 01265 } 01266 const QString path = this->path(); 01267 01268 int len = path.length(); 01269 if ( len == 0 ) 01270 return fname; 01271 01272 if (!(options & ObeyTrailingSlash) ) 01273 { 01274 while ( len >= 1 && path[ len - 1 ] == QLatin1Char('/') ) 01275 len--; 01276 } 01277 else if ( path[ len - 1 ] == QLatin1Char('/') ) 01278 return fname; 01279 01280 // Does the path only consist of '/' characters ? 01281 if ( len == 1 && path[ 0 ] == QLatin1Char('/') ) 01282 return fname; 01283 01284 // Skip last n slashes 01285 int n = 1; 01286 #if 0 01287 if (!m_strPath_encoded.isEmpty()) 01288 { 01289 // This is hairy, we need the last unencoded slash. 01290 // Count in the encoded string how many encoded slashes follow the last 01291 // unencoded one. 01292 int i = m_strPath_encoded.lastIndexOf( QLatin1Char('/'), len - 1 ); 01293 QString fileName_encoded = m_strPath_encoded.mid(i+1); 01294 n += fileName_encoded.count("%2f", Qt::CaseInsensitive); 01295 } 01296 #endif 01297 int i = len; 01298 do { 01299 i = path.lastIndexOf( QLatin1Char('/'), i - 1 ); 01300 } 01301 while (--n && (i > 0)); 01302 01303 // If ( i == -1 ) => the first character is not a '/' 01304 // So it's some URL like file:blah.tgz, return the whole path 01305 if ( i == -1 ) { 01306 if ( len == (int)path.length() ) 01307 fname = path; 01308 else 01309 // Might get here if _strip_trailing_slash is true 01310 fname = path.left( len ); 01311 } 01312 else 01313 { 01314 fname = path.mid( i + 1, len - i - 1 ); // TO CHECK 01315 } 01316 return fname; 01317 } 01318 01319 void KUrl::addPath( const QString& _txt ) 01320 { 01321 if (hasSubUrl()) 01322 { 01323 KUrl::List lst = split( *this ); 01324 KUrl &u = lst.last(); 01325 u.addPath(_txt); 01326 *this = join( lst ); 01327 return; 01328 } 01329 01330 //m_strPath_encoded.clear(); 01331 01332 if ( _txt.isEmpty() ) 01333 return; 01334 01335 QString strPath = path(); 01336 int i = 0; 01337 int len = strPath.length(); 01338 // Add the trailing '/' if it is missing 01339 if ( _txt[0] != QLatin1Char('/') && ( len == 0 || strPath[ len - 1 ] != QLatin1Char('/') ) ) 01340 strPath += QLatin1Char('/'); 01341 01342 // No double '/' characters 01343 i = 0; 01344 const int _txtlen = _txt.length(); 01345 if ( strPath.endsWith( QLatin1Char('/') ) ) 01346 { 01347 while ( ( i < _txtlen ) && ( _txt[i] == QLatin1Char('/') ) ) 01348 ++i; 01349 } 01350 01351 setPath( strPath + _txt.mid( i ) ); 01352 //kDebug(kurlDebugArea())<<"addPath: resultpath="<<path(); 01353 } 01354 01355 QString KUrl::directory( const DirectoryOptions& options ) const 01356 { 01357 Q_ASSERT( options != 0 ); //Disallow options == false 01358 QString result = path(); //m_strPath_encoded.isEmpty() ? m_strPath : m_strPath_encoded; 01359 if ( !(options & ObeyTrailingSlash) ) 01360 result = trailingSlash( RemoveTrailingSlash, result ); 01361 01362 if ( result.isEmpty() || result == QLatin1String ( "/" ) ) 01363 return result; 01364 01365 int i = result.lastIndexOf( QLatin1Char('/') ); 01366 // If ( i == -1 ) => the first character is not a '/' 01367 // So it's some URL like file:blah.tgz, with no path 01368 if ( i == -1 ) 01369 return QString(); 01370 01371 if ( i == 0 ) 01372 { 01373 return QString(QLatin1Char('/')); 01374 } 01375 01376 #ifdef Q_WS_WIN 01377 if ( i == 2 && result[1] == QLatin1Char(':') ) 01378 { 01379 return result.left(3); 01380 } 01381 #endif 01382 01383 if ( options & AppendTrailingSlash ) 01384 result = result.left( i + 1 ); 01385 else 01386 result = result.left( i ); 01387 01388 //if (!m_strPath_encoded.isEmpty()) 01389 // result = decode(result); 01390 01391 return result; 01392 } 01393 01394 01395 bool KUrl::cd( const QString& _dir ) 01396 { 01397 if ( _dir.isEmpty() || !isValid() ) 01398 return false; 01399 01400 if (hasSubUrl()) 01401 { 01402 KUrl::List lst = split( *this ); 01403 KUrl &u = lst.last(); 01404 u.cd(_dir); 01405 *this = join( lst ); 01406 return true; 01407 } 01408 01409 // absolute path ? 01410 #ifdef Q_OS_WIN 01411 if ( !QFileInfo(_dir).isRelative() ) 01412 #else 01413 if ( _dir[0] == QLatin1Char('/') ) 01414 #endif 01415 { 01416 //m_strPath_encoded.clear(); 01417 setPath( _dir ); 01418 setHTMLRef( QString() ); 01419 setEncodedQuery( QByteArray() ); 01420 return true; 01421 } 01422 01423 // Users home directory on the local disk ? 01424 if (_dir[0] == QLatin1Char('~') && scheme() == QLatin1String ("file")) 01425 { 01426 //m_strPath_encoded.clear(); 01427 QString strPath = QDir::homePath(); 01428 strPath += QLatin1Char('/'); 01429 strPath += _dir.right( strPath.length() - 1 ); 01430 setPath( strPath ); 01431 setHTMLRef( QString() ); 01432 setEncodedQuery( QByteArray() ); 01433 return true; 01434 } 01435 01436 // relative path 01437 // we always work on the past of the first url. 01438 // Sub URLs are not touched. 01439 01440 // append '/' if necessary 01441 QString p = path(AddTrailingSlash); 01442 p += _dir; 01443 p = cleanpath( p, true, false ); 01444 setPath( p ); 01445 01446 setHTMLRef( QString() ); 01447 setEncodedQuery( QByteArray() ); 01448 01449 return true; 01450 } 01451 01452 KUrl KUrl::upUrl( ) const 01453 { 01454 if (!isValid() || isRelative()) 01455 return KUrl(); 01456 01457 if (!encodedQuery().isEmpty()) 01458 { 01459 KUrl u(*this); 01460 u.setEncodedQuery(QByteArray()); 01461 return u; 01462 } 01463 01464 if (!hasSubUrl()) 01465 { 01466 KUrl u(*this); 01467 u.cd(QLatin1String("../")); 01468 return u; 01469 } 01470 01471 // We have a subURL. 01472 KUrl::List lst = split( *this ); 01473 if (lst.isEmpty()) 01474 return KUrl(); // Huh? 01475 while (true) 01476 { 01477 KUrl &u = lst.last(); 01478 const QString old = u.path(); 01479 u.cd(QLatin1String("../")); 01480 if (u.path() != old) 01481 break; // Finished. 01482 if (lst.count() == 1) 01483 break; // Finished. 01484 lst.removeLast(); 01485 } 01486 return join( lst ); 01487 } 01488 01489 QString KUrl::htmlRef() const 01490 { 01491 if ( !hasSubUrl() ) 01492 { 01493 return QUrl::fromPercentEncoding( ref().toLatin1() ); 01494 } 01495 01496 const List lst = split( *this ); 01497 return QUrl::fromPercentEncoding( (*lst.begin()).ref().toLatin1() ); 01498 } 01499 01500 QString KUrl::encodedHtmlRef() const 01501 { 01502 if ( !hasSubUrl() ) 01503 { 01504 return ref(); 01505 } 01506 01507 const List lst = split( *this ); 01508 return (*lst.begin()).ref(); 01509 } 01510 01511 void KUrl::setHTMLRef( const QString& _ref ) 01512 { 01513 if ( !hasSubUrl() ) 01514 { 01515 setFragment( _ref ); 01516 return; 01517 } 01518 01519 List lst = split( *this ); 01520 01521 (*lst.begin()).setFragment( _ref ); 01522 01523 *this = join( lst ); 01524 } 01525 01526 bool KUrl::hasHTMLRef() const 01527 { 01528 if ( !hasSubUrl() ) 01529 { 01530 return hasRef(); 01531 } 01532 01533 const List lst = split( *this ); 01534 return (*lst.begin()).hasRef(); 01535 } 01536 01537 void KUrl::setDirectory( const QString &dir) 01538 { 01539 if ( dir.endsWith(QLatin1Char('/'))) 01540 setPath(dir); 01541 else 01542 setPath(dir + QLatin1Char('/')); 01543 } 01544 01545 void KUrl::setQuery( const QString &_txt ) 01546 { 01547 if (!_txt.isEmpty() && _txt[0] == QLatin1Char('?')) 01548 _setQuery( _txt.length() > 1 ? _txt.mid(1) : QString::fromLatin1("") /*empty, not null*/ ); 01549 else 01550 _setQuery( _txt ); 01551 } 01552 01553 void KUrl::_setQuery( const QString& query ) 01554 { 01555 if ( query.isNull() ) { 01556 setEncodedQuery( QByteArray() ); 01557 } else if ( query.isEmpty() ) { 01558 setEncodedQuery(""); 01559 } else { 01560 setEncodedQuery( query.toLatin1() ); // already percent-escaped, so toLatin1 is ok 01561 } 01562 } 01563 01564 QString KUrl::query() const 01565 { 01566 if (!hasQuery()) { 01567 return QString(); 01568 } 01569 return QString(QLatin1Char('?')) + QString::fromLatin1(encodedQuery()); 01570 } 01571 01572 void KUrl::_setEncodedUrl(const QByteArray& url) 01573 { 01574 setEncodedUrl(url, QUrl::TolerantMode); 01575 if (!isValid()) // see unit tests referring to N183630/task 183874 01576 setUrl(QString::fromUtf8(url), QUrl::TolerantMode); 01577 } 01578 01579 #ifndef KDE_NO_DEPRECATED 01580 bool urlcmp( const QString& _url1, const QString& _url2 ) 01581 { 01582 return QUrl( _url1, QUrl::TolerantMode ) == QUrl( _url2, QUrl::TolerantMode ); 01583 #if 0 01584 // Both empty ? 01585 if ( _url1.isEmpty() && _url2.isEmpty() ) 01586 return true; 01587 // Only one empty ? 01588 if ( _url1.isEmpty() || _url2.isEmpty() ) 01589 return false; 01590 01591 KUrl::List list1 = KUrl::split( _url1 ); 01592 KUrl::List list2 = KUrl::split( _url2 ); 01593 01594 // Malformed ? 01595 if ( list1.isEmpty() || list2.isEmpty() ) 01596 return false; 01597 01598 return ( list1 == list2 ); 01599 #endif 01600 } 01601 #endif 01602 01603 #ifndef KDE_NO_DEPRECATED 01604 bool urlcmp( const QString& _url1, const QString& _url2, const KUrl::EqualsOptions& _options ) 01605 { 01606 // Both empty ? 01607 if (_url1.isEmpty() && _url2.isEmpty()) 01608 return true; 01609 // Only one empty ? 01610 if (_url1.isEmpty() || _url2.isEmpty()) 01611 return false; 01612 01613 KUrl u1(_url1); 01614 KUrl u2(_url2); 01615 return u1.equals(u2, _options); 01616 01617 #if 0 // kde3 code that supported nested urls 01618 01619 KUrl::List list1 = KUrl::split( _url1 ); 01620 KUrl::List list2 = KUrl::split( _url2 ); 01621 01622 // Malformed ? 01623 if ( list1.isEmpty() || list2.isEmpty() ) 01624 return false; 01625 01626 int size = list1.count(); 01627 if ( list2.count() != size ) 01628 return false; 01629 01630 if ( _ignore_ref ) 01631 { 01632 (*list1.begin()).setRef(QString()); 01633 (*list2.begin()).setRef(QString()); 01634 } 01635 01636 KUrl::List::Iterator it1 = list1.begin(); 01637 KUrl::List::Iterator it2 = list2.begin(); 01638 for( ; it1 != list1.end() ; ++it1, ++it2 ) 01639 if ( !(*it1).equals( *it2, _ignore_trailing ) ) 01640 return false; 01641 return true; 01642 #endif 01643 } 01644 #endif 01645 01646 // static 01647 #ifndef KDE_NO_DEPRECATED 01648 KUrl KUrl::fromPathOrUrl( const QString& text ) 01649 { 01650 KUrl url; 01651 if ( !text.isEmpty() ) 01652 { 01653 if (!QDir::isRelativePath(text) || text[0] == QLatin1Char('~')) 01654 url.setPath( text ); 01655 else 01656 url = KUrl( text ); 01657 } 01658 01659 return url; 01660 } 01661 #endif 01662 01663 static QString _relativePath(const QString &base_dir, const QString &path, bool &isParent) 01664 { 01665 QString _base_dir(QDir::cleanPath(base_dir)); 01666 QString _path(QDir::cleanPath(path.isEmpty() || (path[0] != QLatin1Char('/')) ? _base_dir+QLatin1Char('/')+path : path)); 01667 01668 if (_base_dir.isEmpty()) 01669 return _path; 01670 01671 if (_base_dir[_base_dir.length()-1] != QLatin1Char('/')) 01672 _base_dir.append(QLatin1Char('/') ); 01673 01674 const QStringList list1 = _base_dir.split(QLatin1Char('/'), QString::SkipEmptyParts); 01675 const QStringList list2 = _path.split(QLatin1Char('/'), QString::SkipEmptyParts); 01676 01677 // Find where they meet 01678 int level = 0; 01679 int maxLevel = qMin(list1.count(), list2.count()); 01680 while((level < maxLevel) && (list1[level] == list2[level])) level++; 01681 01682 QString result; 01683 // Need to go down out of the first path to the common branch. 01684 for(int i = level; i < list1.count(); i++) 01685 result.append(QLatin1String("../")); 01686 01687 // Now up up from the common branch to the second path. 01688 for(int i = level; i < list2.count(); i++) 01689 result.append(list2[i]).append(QLatin1Char('/')); 01690 01691 if ((level < list2.count()) && (path[path.length()-1] != QLatin1Char('/'))) 01692 result.truncate(result.length()-1); 01693 01694 isParent = (level == list1.count()); 01695 01696 return result; 01697 } 01698 01699 QString KUrl::relativePath(const QString &base_dir, const QString &path, bool *isParent) 01700 { 01701 bool parent = false; 01702 QString result = _relativePath(base_dir, path, parent); 01703 if (parent) 01704 result.prepend(QLatin1String("./")); 01705 01706 if (isParent) 01707 *isParent = parent; 01708 01709 return result; 01710 } 01711 01712 01713 QString KUrl::relativeUrl(const KUrl &base_url, const KUrl &url) 01714 { 01715 if ((url.protocol() != base_url.protocol()) || 01716 (url.host() != base_url.host()) || 01717 (url.port() && url.port() != base_url.port()) || 01718 (url.hasUser() && url.user() != base_url.user()) || 01719 (url.hasPass() && url.pass() != base_url.pass())) 01720 { 01721 return url.url(); 01722 } 01723 01724 QString relURL; 01725 01726 if ((base_url.path() != url.path()) || (base_url.query() != url.query())) 01727 { 01728 bool dummy; 01729 QString basePath = base_url.directory(KUrl::ObeyTrailingSlash); 01730 relURL = _relativePath(basePath, url.path(), dummy); // was QUrl::toPercentEncoding() but why? 01731 relURL += url.query(); 01732 } 01733 01734 if ( url.hasRef() ) 01735 { 01736 relURL += QLatin1Char('#'); 01737 relURL += url.ref(); 01738 } 01739 01740 if ( relURL.isEmpty() ) 01741 return QLatin1String("./"); 01742 01743 return relURL; 01744 } 01745 01746 void KUrl::setPath( const QString& _path ) 01747 { 01748 #if defined(Q_WS_WIN) && defined(DEBUG_KURL) 01749 kDebug(kurlDebugArea()) << "KUrl::setPath " << " " << _path.toAscii().data(); 01750 #endif 01751 if ( scheme().isEmpty() ) 01752 setScheme( QLatin1String( "file" ) ); 01753 QString path = KShell::tildeExpand( _path ); 01754 if (path.isEmpty()) 01755 path = _path; 01756 #ifdef Q_WS_WIN 01757 const int len = path.length(); 01758 if( len == 2 && IS_LETTER(path[0]) && path[1] == QLatin1Char(':') ) 01759 path += QLatin1Char('/'); 01760 //This is necessary because QUrl has the "path" part including the first slash 01761 //Without this QUrl doesn't understand that this is a path, and some operations fail 01762 //e.g. C:/blah needs to become /C:/blah 01763 else 01764 if( len > 0 && path[0] != QLatin1Char('/') && scheme() == QLatin1String( "file" ) ) 01765 path = QLatin1Char('/') + path; 01766 #endif 01767 QUrl::setPath( path ); 01768 } 01769 01770 #if 0 // this would be if we didn't decode '+' into ' ' 01771 QMap< QString, QString > KUrl::queryItems( int options ) const { 01772 QMap< QString, QString > result; 01773 const QList<QPair<QString, QString> > items = QUrl::queryItems(); 01774 QPair<QString, QString> item; 01775 Q_FOREACH( item, items ) { 01776 result.insert( options & CaseInsensitiveKeys ? item.first.toLower() : item.first, item.second ); 01777 } 01778 return result; 01779 } 01780 #endif 01781 01782 QMap< QString, QString > KUrl::queryItems( const QueryItemsOptions &options ) const 01783 { 01784 const QString strQueryEncoded = QString::fromLatin1(encodedQuery()); 01785 if ( strQueryEncoded.isEmpty() ) 01786 return QMap<QString,QString>(); 01787 01788 QMap< QString, QString > result; 01789 const QStringList items = strQueryEncoded.split( QLatin1Char('&'), QString::SkipEmptyParts ); 01790 for ( QStringList::const_iterator it = items.begin() ; it != items.end() ; ++it ) { 01791 const int equal_pos = (*it).indexOf(QLatin1Char('=')); 01792 if ( equal_pos > 0 ) { // = is not the first char... 01793 QString name = (*it).left( equal_pos ); 01794 if ( options & CaseInsensitiveKeys ) 01795 name = name.toLower(); 01796 QString value = (*it).mid( equal_pos + 1 ); 01797 if ( value.isEmpty() ) 01798 result.insert( name, QString::fromLatin1("") ); 01799 else { 01800 // ### why is decoding name not necessary? 01801 value.replace( QLatin1Char('+'), QLatin1Char(' ') ); // + in queries means space 01802 result.insert( name, QUrl::fromPercentEncoding( value.toLatin1() ) ); 01803 } 01804 } else if ( equal_pos < 0 ) { // no = 01805 QString name = (*it); 01806 if ( options & CaseInsensitiveKeys ) 01807 name = name.toLower(); 01808 result.insert( name, QString() ); 01809 } 01810 } 01811 01812 return result; 01813 } 01814 01815 QString KUrl::queryItem( const QString& _item ) const 01816 { 01817 const QString strQueryEncoded = QString::fromLatin1(encodedQuery()); 01818 const QString item = _item + QLatin1Char('='); 01819 if ( strQueryEncoded.length() <= 1 ) 01820 return QString(); 01821 01822 const QStringList items = strQueryEncoded.split( QString(QLatin1Char('&')), QString::SkipEmptyParts ); 01823 const int _len = item.length(); 01824 for ( QStringList::ConstIterator it = items.begin(); it != items.end(); ++it ) 01825 { 01826 if ( (*it).startsWith( item ) ) 01827 { 01828 if ( (*it).length() > _len ) 01829 { 01830 QString str = (*it).mid( _len ); 01831 str.replace( QLatin1Char('+'), QLatin1Char(' ') ); // + in queries means space. 01832 return QUrl::fromPercentEncoding( str.toLatin1() ); 01833 } 01834 else // empty value 01835 return QString::fromLatin1(""); 01836 } 01837 } 01838 01839 return QString(); 01840 } 01841 01842 void KUrl::addQueryItem( const QString& _item, const QString& _value ) 01843 { 01844 QString item = _item + QLatin1Char('='); 01845 QString value = QString::fromLatin1(QUrl::toPercentEncoding(_value)); 01846 01847 QString strQueryEncoded = QString::fromLatin1(encodedQuery()); 01848 if (!strQueryEncoded.isEmpty()) 01849 strQueryEncoded += QLatin1Char('&'); 01850 strQueryEncoded += item + value; 01851 setEncodedQuery( strQueryEncoded.toLatin1() ); 01852 } 01853 01854 void KUrl::populateMimeData( QMimeData* mimeData, 01855 const MetaDataMap& metaData, 01856 MimeDataFlags flags ) const 01857 { 01858 KUrl::List lst( *this ); 01859 lst.populateMimeData( mimeData, metaData, flags ); 01860 } 01861 01862 bool KUrl::hasRef() const 01863 { 01864 return hasFragment(); 01865 } 01866 01867 void KUrl::setRef( const QString& fragment ) 01868 { 01869 if ( fragment.isNull() ) 01870 setFragment( fragment ); // pass null, not empty 01871 else 01872 setFragment( QUrl::fromPercentEncoding( fragment.toLatin1() ) ); 01873 } 01874 01875 QString KUrl::ref() const 01876 { 01877 if ( fragment().isNull() ) 01878 return QString(); 01879 else 01880 return QString::fromLatin1( QUrl::toPercentEncoding( fragment() ) ); 01881 } 01882 01883 bool KUrl::isParentOf( const KUrl& u ) const 01884 { 01885 return QUrl::isParentOf( u ) || equals( u, CompareWithoutTrailingSlash ); 01886 } 01887 01888 uint qHash(const KUrl& kurl) 01889 { 01890 // qHash(kurl.url()) was the worse implementation possible, since QUrl::toEncoded() 01891 // had to concatenate the bits of the url into the full url every time. 01892 01893 return qHash(kurl.protocol()) ^ qHash(kurl.path()) ^ qHash(kurl.fragment()) ^ qHash(kurl.query()); 01894 }
KDE 4.6 API Reference