• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KDECore

kurl.cpp

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

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
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
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