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

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
  • KUtils
  • Nepomuk
  • Plasma
  •     Sodep
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.9-20090814
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