• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

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 }

KDECore

Skip menu "KDECore"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal