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

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
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
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