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