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

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
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
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