kdecore Library API Documentation

kurl.cpp

00001 /*
00002     Copyright (C) 1999 Torben Weis <weis@kde.org>
00003 
00004     This library is free software; you can redistribute it and/or
00005     modify it under the terms of the GNU Library General Public
00006     License as published by the Free Software Foundation; either
00007     version 2 of the License, or (at your option) any later version.
00008 
00009     This library is distributed in the hope that it will be useful,
00010     but WITHOUT ANY WARRANTY; without even the implied warranty of
00011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012     Library General Public License for more details.
00013 
00014     You should have received a copy of the GNU Library General Public License
00015     along with this library; see the file COPYING.LIB.  If not, write to
00016     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00017     Boston, MA 02111-1307, USA.
00018 */
00019 
00020 /*
00021  * The currently active RFC for URL/URIs is RFC3986
00022  * Previous (and now deprecated) RFCs are RFC1738 and RFC2396
00023  */
00024 
00025 #include "kurl.h"
00026 
00027 #ifndef KDE_QT_ONLY
00028 #include <kdebug.h>
00029 #include <kglobal.h>
00030 #include <kidna.h>
00031 #include <kprotocolinfo.h>
00032 #endif
00033 
00034 #include <stdio.h>
00035 #include <assert.h>
00036 #include <ctype.h>
00037 #include <stdlib.h>
00038 #include <unistd.h>
00039 
00040 #include <qurl.h>
00041 #include <qdir.h>
00042 #include <qstringlist.h>
00043 #include <qregexp.h>
00044 #include <qstylesheet.h>
00045 #include <qmap.h>
00046 #include <qtextcodec.h>
00047 #include <qmutex.h>
00048 
00049 #ifdef Q_WS_WIN
00050 # define KURL_ROOTDIR_PATH "C:/"
00051 #else
00052 # define KURL_ROOTDIR_PATH "/"
00053 #endif
00054 
00055 static const QString fileProt = "file";
00056 
00057 static QTextCodec * codecForHint( int encoding_hint /* not 0 ! */ )
00058 {
00059     return QTextCodec::codecForMib( encoding_hint );
00060 }
00061 
00062 // encoding_offset:
00063 // 0 encode both @ and /
00064 // 1 encode @ but not /
00065 // 2 encode neither @ or /
00066 static QString encode( const QString& segment, int encoding_offset, int encoding_hint )
00067 {
00068   const char *encode_string = "/@<>#\"&%?={}|^~[]\'`\\:+";
00069   encode_string += encoding_offset;
00070 
00071   QCString local;
00072   if (encoding_hint==0)
00073     local = segment.local8Bit();
00074   else
00075   {
00076       QTextCodec * textCodec = codecForHint( encoding_hint );
00077       if (!textCodec)
00078           local = segment.local8Bit();
00079       else
00080           local = textCodec->fromUnicode( segment );
00081   }
00082 
00083   int old_length = local.length();
00084 
00085   if ( !old_length )
00086     return segment.isNull() ? QString::null : QString(""); // differentiate null and empty
00087 
00088   // a worst case approximation
00089   QChar *new_segment = new QChar[ old_length * 3 + 1 ];
00090   int new_length = 0;
00091 
00092   for ( int i = 0; i < old_length; i++ )
00093   {
00094     // 'unsave' and 'reserved' characters
00095     // according to RFC 1738,
00096     // 2.2. URL Character Encoding Issues (pp. 3-4)
00097     // WABA: Added non-ascii
00098     unsigned char character = local[i];
00099     if ( (character <= 32) || (character >= 127) ||
00100          strchr(encode_string, character) )
00101     {
00102       new_segment[ new_length++ ] = '%';
00103 
00104       unsigned int c = character / 16;
00105       c += (c > 9) ? ('A' - 10) : '0';
00106       new_segment[ new_length++ ] = c;
00107 
00108       c = character % 16;
00109       c += (c > 9) ? ('A' - 10) : '0';
00110       new_segment[ new_length++ ] = c;
00111 
00112     }
00113     else
00114       new_segment[ new_length++ ] = local[i];
00115   }
00116 
00117   QString result = QString(new_segment, new_length);
00118   delete [] new_segment;
00119   return result;
00120 }
00121 
00122 static QString encodeHost( const QString& segment, bool encode_slash, int encoding_hint )
00123 {
00124   // Hostnames are encoded differently
00125   // we use the IDNA transformation instead
00126 
00127   // Note: when merging qt-addon, use QResolver::domainToAscii here
00128 #ifndef KDE_QT_ONLY
00129   Q_UNUSED( encode_slash );
00130   Q_UNUSED( encoding_hint );
00131   QString host = KIDNA::toAscii(segment);
00132   if (host.isEmpty())
00133      return segment;
00134   return host;
00135 #else
00136   return encode(segment, encode_slash ? 0 : 1, encoding_hint);
00137 #endif
00138 }
00139 
00140 static int hex2int( unsigned int _char )
00141 {
00142   if ( _char >= 'A' && _char <='F')
00143     return _char - 'A' + 10;
00144   if ( _char >= 'a' && _char <='f')
00145     return _char - 'a' + 10;
00146   if ( _char >= '0' && _char <='9')
00147     return _char - '0';
00148   return -1;
00149 }
00150 
00151 // WABA: The result of lazy_encode isn't usable for a URL which
00152 // needs to satisfies RFC requirements. However, the following
00153 // operation will make it usable again:
00154 //      encode(decode(...))
00155 //
00156 // As a result one can see that url.prettyURL() does not result in
00157 // a RFC compliant URL but that the following sequence does:
00158 //      KURL(url.prettyURL()).url()
00159 
00160 
00161 static QString lazy_encode( const QString& segment, bool encodeAt=true )
00162 {
00163   int old_length = segment.length();
00164 
00165   if ( !old_length )
00166     return QString::null;
00167 
00168   // a worst case approximation
00169   QChar *new_segment = new QChar[ old_length * 3 + 1 ];
00170   int new_length = 0;
00171 
00172   for ( int i = 0; i < old_length; i++ )
00173   {
00174     unsigned int character = segment[i].unicode(); // Don't use latin1()
00175                                                    // It returns 0 for non-latin1 values
00176     // Small set of really ambiguous chars
00177     if ((character < 32) ||  // Low ASCII
00178         ((character == '%') && // The escape character itself
00179            (i+2 < old_length) && // But only if part of a valid escape sequence!
00180           (hex2int(segment[i+1].unicode())!= -1) &&
00181           (hex2int(segment[i+2].unicode())!= -1)) ||
00182         (character == '?') || // Start of query delimiter
00183         ((character == '@') && encodeAt) || // Username delimiter
00184         (character == '#') || // Start of reference delimiter
00185         ((character == 32) && (i+1 == old_length))) // A trailing space
00186     {
00187       new_segment[ new_length++ ] = '%';
00188 
00189       unsigned int c = character / 16;
00190       c += (c > 9) ? ('A' - 10) : '0';
00191       new_segment[ new_length++ ] = c;
00192 
00193       c = character % 16;
00194       c += (c > 9) ? ('A' - 10) : '0';
00195       new_segment[ new_length++ ] = c;
00196     }
00197     else
00198     new_segment[ new_length++ ] = segment[i];
00199   }
00200 
00201   QString result = QString(new_segment, new_length);
00202   delete [] new_segment;
00203   return result;
00204 }
00205 
00206 static void decode( const QString& segment, QString &decoded, QString &encoded, int encoding_hint=0, bool updateDecoded = true )
00207 {
00208   decoded = QString::null;
00209   encoded = segment;
00210 
00211   int old_length = segment.length();
00212   if ( !old_length )
00213     return;
00214 
00215   QTextCodec *textCodec = 0;
00216   if (encoding_hint)
00217       textCodec = codecForHint( encoding_hint );
00218 
00219   if (!textCodec)
00220       textCodec = QTextCodec::codecForLocale();
00221 
00222   QCString csegment = textCodec->fromUnicode(segment);
00223   // Check if everything went ok
00224   if (textCodec->toUnicode(csegment) != segment)
00225   {
00226       // Uh oh
00227       textCodec = codecForHint( 106 ); // Fall back to utf-8
00228       csegment = textCodec->fromUnicode(segment);
00229   }
00230   old_length = csegment.length();
00231 
00232   int new_length = 0;
00233   int new_length2 = 0;
00234 
00235   // make a copy of the old one
00236   char *new_segment = new char[ old_length + 1 ];
00237   QChar *new_usegment = new QChar[ old_length * 3 + 1 ];
00238 
00239   int i = 0;
00240   while( i < old_length )
00241   {
00242     bool bReencode = false;
00243     unsigned char character = csegment[ i++ ];
00244     if ((character <= ' ') || (character > 127))
00245        bReencode = true;
00246 
00247     new_usegment [ new_length2++ ] = character;
00248     if (character == '%' )
00249     {
00250       int a = i+1 < old_length ? hex2int( csegment[i] ) : -1;
00251       int b = i+1 < old_length ? hex2int( csegment[i+1] ) : -1;
00252       if ((a == -1) || (b == -1)) // Only replace if sequence is valid
00253       {
00254          // Contains stray %, make sure to re-encode!
00255          bReencode = true;
00256       }
00257       else
00258       {
00259          // Valid %xx sequence
00260          character = a * 16 + b; // Replace with value of %dd
00261          if (!character && updateDecoded)
00262             break; // Stop at %00
00263 
00264          new_usegment [ new_length2++ ] = (unsigned char) csegment[i++];
00265          new_usegment [ new_length2++ ] = (unsigned char) csegment[i++];
00266       }
00267     }
00268     if (bReencode)
00269     {
00270       new_length2--;
00271       new_usegment [ new_length2++ ] = '%';
00272 
00273       unsigned int c = character / 16;
00274       c += (c > 9) ? ('A' - 10) : '0';
00275       new_usegment[ new_length2++ ] = c;
00276 
00277       c = character % 16;
00278       c += (c > 9) ? ('A' - 10) : '0';
00279       new_usegment[ new_length2++ ] = c;
00280     }
00281 
00282     new_segment [ new_length++ ] = character;
00283   }
00284   new_segment [ new_length ] = 0;
00285 
00286   encoded = QString( new_usegment, new_length2);
00287 
00288   // Encoding specified
00289   if (updateDecoded)
00290   {
00291      QByteArray array;
00292      array.setRawData(new_segment, new_length);
00293      decoded = textCodec->toUnicode( array, new_length );
00294      array.resetRawData(new_segment, new_length);
00295      QCString validate = textCodec->fromUnicode(decoded);
00296 
00297      if (strcmp(validate.data(), new_segment) != 0)
00298      {
00299         decoded = QString::fromLocal8Bit(new_segment, new_length);
00300      }
00301   }
00302 
00303   delete [] new_segment;
00304   delete [] new_usegment;
00305 }
00306 
00307 static QString decode(const QString &segment, int encoding_hint = 0)
00308 {
00309   QString result;
00310   QString tmp;
00311   decode(segment, result, tmp, encoding_hint);
00312   return result;
00313 }
00314 
00315 static QString cleanpath(const QString &_path, bool cleanDirSeparator, bool decodeDots)
00316 {
00317   if (_path.isEmpty()) return QString::null;
00318 
00319   if (QDir::isRelativePath(_path))
00320      return _path; // Don't mangle mailto-style URLs
00321 
00322   QString path = _path;
00323 
00324   int len = path.length();
00325 
00326   if (decodeDots)
00327   {
00328 #ifndef KDE_QT_ONLY
00329      static const QString &encodedDot = KGlobal::staticQString("%2e");
00330 #else
00331      QString encodedDot("%2e");
00332 #endif
00333      if (path.find(encodedDot, 0, false) != -1)
00334      {
00335 #ifndef KDE_QT_ONLY
00336         static const QString &encodedDOT = KGlobal::staticQString("%2E"); // Uppercase!
00337 #else
00338         QString encodedDOT("%2E");
00339 #endif
00340         path.replace(encodedDot, ".");
00341         path.replace(encodedDOT, ".");
00342         len = path.length();
00343      }
00344   }
00345 
00346   bool slash = (len && path[len-1] == '/') ||
00347                (len > 1 && path[len-2] == '/' && path[len-1] == '.');
00348 
00349   // The following code cleans up directory path much like
00350   // QDir::cleanDirPath() except it can be made to ignore multiple
00351   // directory separators by setting the flag to false.  That fixes
00352   // bug# 15044, mail.altavista.com and other similar brain-dead server
00353   // implementations that do not follow what has been specified in
00354   // RFC 2396!! (dA)
00355   QString result;
00356   int cdUp, orig_pos, pos;
00357 
00358   cdUp = 0;
00359   pos = orig_pos = len;
00360   while ( pos && (pos = path.findRev('/',--pos)) != -1 )
00361   {
00362     len = orig_pos - pos - 1;
00363     if ( len == 2 && path[pos+1] == '.' && path[pos+2] == '.' )
00364       cdUp++;
00365     else
00366     {
00367       // Ignore any occurrences of '.'
00368       // This includes entries that simply do not make sense like /..../
00369       if ( (len || !cleanDirSeparator) &&
00370            (len != 1 || path[pos+1] != '.' ) )
00371       {
00372           if ( !cdUp )
00373               result.prepend(path.mid(pos, len+1));
00374           else
00375               cdUp--;
00376       }
00377     }
00378     orig_pos = pos;
00379   }
00380 
00381 #ifdef Q_WS_WIN // prepend drive letter if exists (js)
00382   if (orig_pos >= 2 && isalpha(path[0].latin1()) && path[1]==':') {
00383     result.prepend(QString(path[0])+":");
00384   }
00385 #endif
00386 
00387   if ( result.isEmpty() )
00388     result = KURL_ROOTDIR_PATH;
00389   else if ( slash && result[result.length()-1] != '/' )
00390        result.append('/');
00391 
00392   return result;
00393 }
00394 
00395 bool KURL::isRelativeURL(const QString &_url)
00396 {
00397   int len = _url.length();
00398   if (!len) return true; // Very short relative URL.
00399   const QChar *str = _url.unicode();
00400 
00401   // Absolute URL must start with alpha-character
00402   if (!isalpha(str[0].latin1()))
00403      return true; // Relative URL
00404 
00405   for(int i = 1; i < len; i++)
00406   {
00407      char c = str[i].latin1(); // Note: non-latin1 chars return 0!
00408      if (c == ':')
00409         return false; // Absolute URL
00410 
00411      // Protocol part may only contain alpha, digit, + or -
00412      if (!isalpha(c) && !isdigit(c) && (c != '+') && (c != '-'))
00413         return true; // Relative URL
00414   }
00415   // URL did not contain ':'
00416   return true; // Relative URL
00417 }
00418 
00419 KURL::List::List(const KURL &url)
00420 {
00421     append( url );
00422 }
00423 
00424 KURL::List::List(const QStringList &list)
00425 {
00426   for (QStringList::ConstIterator it = list.begin();
00427        it != list.end();
00428        it++)
00429     {
00430       append( KURL(*it) );
00431     }
00432 }
00433 
00434 QStringList KURL::List::toStringList() const
00435 {
00436   QStringList lst;
00437    for( KURL::List::ConstIterator it = begin();
00438         it != end();
00439         it++)
00440    {
00441       lst.append( (*it).url() );
00442    }
00443    return lst;
00444 }
00445 
00446 
00447 KURL::KURL()
00448 {
00449   reset();
00450 }
00451 
00452 KURL::~KURL()
00453 {
00454 }
00455 
00456 
00457 KURL::KURL( const QString &url, int encoding_hint )
00458 {
00459   reset();
00460   parse( url, encoding_hint );
00461 }
00462 
00463 KURL::KURL( const char * url, int encoding_hint )
00464 {
00465   reset();
00466   parse( QString::fromLatin1(url), encoding_hint );
00467 }
00468 
00469 KURL::KURL( const QCString& url, int encoding_hint )
00470 {
00471   reset();
00472   parse( QString::fromLatin1(url), encoding_hint );
00473 }
00474 
00475 KURL::KURL( const KURL& _u )
00476 {
00477   *this = _u;
00478 }
00479 
00480 QDataStream & operator<< (QDataStream & s, const KURL & a)
00481 {
00482   QString QueryForWire=a.m_strQuery_encoded;
00483   if (!a.m_strQuery_encoded.isNull())
00484     QueryForWire.prepend("?");
00485 
00486     s << a.m_strProtocol << a.m_strUser << a.m_strPass << a.m_strHost
00487       << a.m_strPath << a.m_strPath_encoded << QueryForWire << a.m_strRef_encoded
00488       << Q_INT8(a.m_bIsMalformed ? 1 : 0) << a.m_iPort;
00489     return s;
00490 }
00491 
00492 QDataStream & operator>> (QDataStream & s, KURL & a)
00493 {
00494     Q_INT8 malf;
00495     QString QueryFromWire;
00496     s >> a.m_strProtocol >> a.m_strUser >> a.m_strPass >> a.m_strHost
00497       >> a.m_strPath >> a.m_strPath_encoded >> QueryFromWire >> a.m_strRef_encoded
00498       >> malf >> a.m_iPort;
00499     a.m_bIsMalformed = (malf != 0);
00500 
00501     if ( QueryFromWire.isNull() )
00502       a.m_strQuery_encoded = QString::null;
00503     else if ( QueryFromWire.length() == 1 ) // empty query
00504       a.m_strQuery_encoded = "";
00505     else
00506       a.m_strQuery_encoded = QueryFromWire.mid(1);
00507 
00508     a.m_iUriMode = KURL::uriModeForProtocol( a.m_strProtocol );
00509 
00510     return s;
00511 }
00512 
00513 #ifndef QT_NO_NETWORKPROTOCOL
00514 KURL::KURL( const QUrl &u )
00515 {
00516   *this = u;
00517 }
00518 #endif
00519 
00520 KURL::KURL( const KURL& _u, const QString& _rel_url, int encoding_hint )
00521 {
00522   if (_u.hasSubURL()) // Operate on the last suburl, not the first
00523   {
00524     KURL::List lst = split( _u );
00525     KURL u(lst.last(), _rel_url, encoding_hint);
00526     lst.remove( lst.last() );
00527     lst.append( u );
00528     *this = join( lst );
00529     return;
00530   }
00531   // WORKAROUND THE RFC 1606 LOOPHOLE THAT ALLOWS
00532   // http:/index.html AS A VALID SYNTAX FOR RELATIVE
00533   // URLS. ( RFC 2396 section 5.2 item # 3 )
00534   QString rUrl = _rel_url;
00535   int len = _u.m_strProtocol.length();
00536   if ( !_u.m_strHost.isEmpty() && !rUrl.isEmpty() &&
00537        rUrl.find( _u.m_strProtocol, 0, false ) == 0 &&
00538        rUrl[len] == ':' && (rUrl[len+1] != '/' ||
00539        (rUrl[len+1] == '/' && rUrl[len+2] != '/')) )
00540   {
00541     rUrl.remove( 0, rUrl.find( ':' ) + 1 );
00542   }
00543 
00544   if ( rUrl.isEmpty() )
00545   {
00546     *this = _u;
00547   }
00548   else if ( rUrl[0] == '#' )
00549   {
00550     *this = _u;
00551     m_strRef_encoded = rUrl.mid(1);
00552     QString ref = decode(rUrl.mid(1), encoding_hint);
00553     if ( m_strRef_encoded.isNull() )
00554         m_strRef_encoded = ""; // we know there was an (empty) html ref, we saw the '#'
00555   }
00556   else if ( isRelativeURL( rUrl) )
00557   {
00558     *this = _u;
00559     m_strQuery_encoded = QString::null;
00560     m_strRef_encoded = QString::null;
00561     if ( rUrl[0] == '/')
00562     {
00563         if ((rUrl.length() > 1) && (rUrl[1] == '/'))
00564         {
00565            m_strHost = QString::null;
00566            // File protocol returns file:/// without host, strip // from rUrl
00567            if (_u.m_strProtocol == fileProt)
00568               rUrl.remove(0, 2);
00569         }
00570         m_strPath = QString::null;
00571         m_strPath_encoded = QString::null;
00572     }
00573     else if ( rUrl[0] != '?' )
00574     {
00575        int pos = m_strPath.findRev( '/' );
00576        if (pos >= 0)
00577           m_strPath.truncate(pos);
00578        m_strPath += '/';
00579        if (!m_strPath_encoded.isEmpty())
00580        {
00581           pos = m_strPath_encoded.findRev( '/' );
00582           if (pos >= 0)
00583              m_strPath_encoded.truncate(pos);
00584           m_strPath_encoded += '/';
00585        }
00586     }
00587     else
00588     {
00589        if ( m_strPath.isEmpty() )
00590           m_strPath = '/';
00591     }
00592     KURL tmp( url() + rUrl, encoding_hint);
00593     *this = tmp;
00594     cleanPath(false);
00595   }
00596   else
00597   {
00598     KURL tmp( rUrl, encoding_hint);
00599     *this = tmp;
00600     // Preserve userinfo if applicable.
00601     if (!_u.m_strUser.isEmpty() && m_strUser.isEmpty() && (_u.m_strHost == m_strHost) && (_u.m_strProtocol == m_strProtocol))
00602     {
00603        m_strUser = _u.m_strUser;
00604        m_strPass = _u.m_strPass;
00605     }
00606     cleanPath(false);
00607   }
00608 }
00609 
00610 void KURL::reset()
00611 {
00612   m_strProtocol = QString::null;
00613   m_strUser = QString::null;
00614   m_strPass = QString::null;
00615   m_strHost = QString::null;
00616   m_strPath = QString::null;
00617   m_strPath_encoded = QString::null;
00618   m_strQuery_encoded = QString::null;
00619   m_strRef_encoded = QString::null;
00620   m_bIsMalformed = true;
00621   m_iPort = 0;
00622   m_iUriMode = Auto;
00623 }
00624 
00625 bool KURL::isEmpty() const
00626 {
00627   return (m_strPath.isEmpty() && m_strProtocol.isEmpty());
00628 }
00629 
00630 void KURL::parse( const QString& _url, int encoding_hint )
00631 {
00632     if ( _url.isEmpty() || m_iUriMode == Invalid )
00633     {
00634     m_strProtocol = _url;
00635     m_iUriMode = Invalid;
00636     return;
00637     }
00638 
00639     const QChar* buf = _url.unicode();
00640     const QChar* orig = buf;
00641     uint len = _url.length();
00642     uint pos = 0;
00643 
00644     // Node 1: Accept alpha or slash
00645     QChar x = buf[pos++];
00646 #ifdef Q_WS_WIN
00647     /* win32: accept <letter>: or <letter>:/ or <letter>:\ */
00648     const bool alpha = isalpha((int)x);
00649     if (alpha && len<2)
00650         goto NodeErr;
00651     if (alpha && buf[pos]==':' && (len==2 || (len>2 && (buf[pos+1]=='/' || buf[pos+1]=='\\'))))
00652 #else
00653     if ( x == '/' )
00654 #endif
00655     {
00656     // A slash means we immediately proceed to parse it as a file URL.
00657     m_iUriMode = URL;
00658     m_strProtocol = fileProt;
00659     parseURL( _url, encoding_hint );
00660     return;
00661     }
00662     if ( !isalpha( (int)x ) )
00663     goto NodeErr;
00664 
00665     // Node 2: Accept any amount of (alpha|digit|'+'|'-')
00666     // '.' is not currently accepted, because current KURL may be confused.
00667     // Proceed with :// :/ or :
00668     while( pos < len && (isalpha((int)buf[pos]) || isdigit((int)buf[pos]) ||
00669              buf[pos] == '+' || buf[pos] == '-')) pos++;
00670 
00671     if (pos < len && buf[pos] == ':' )
00672     {
00673     m_strProtocol = QString( orig, pos ).lower();
00674     if ( m_iUriMode == Auto )
00675         m_iUriMode = uriModeForProtocol( m_strProtocol );
00676     // Proceed to correct parse function.
00677     switch ( m_iUriMode )
00678     {
00679     case RawURI:
00680         parseRawURI( _url );
00681         return;
00682     case Mailto:
00683         parseMailto( _url );
00684         return;
00685     case URL:
00686         parseURL( _url, encoding_hint );
00687         return;
00688     default:
00689         // Unknown URI mode results in an invalid URI.
00690         break;
00691     }
00692     }
00693 
00694 NodeErr:
00695     reset();
00696     m_strProtocol = _url;
00697     m_iUriMode = Invalid;
00698 }
00699 
00700 void KURL::parseRawURI( const QString& _url, int encoding_hint )
00701 {
00702     uint len = _url.length();
00703     const QChar* buf = _url.unicode();
00704 
00705     uint pos = 0;
00706 
00707     // Accept any amount of (alpha|digit|'+'|'-')
00708     // '.' is not currently accepted, because current KURL may be confused.
00709     // Proceed with :
00710     while( pos < len && (isalpha((int)buf[pos]) || isdigit((int)buf[pos]) ||
00711              buf[pos] == '+' || buf[pos] == '-')) pos++;
00712 
00713     // Note that m_strProtocol is already set here, so we just skip over the protocol.
00714     if (pos < len && buf[pos] == ':' )
00715     pos++;
00716     else { // can't happen, the caller checked all this already
00717     reset();
00718     m_strProtocol = _url;
00719     m_iUriMode = Invalid;
00720     return;
00721     }
00722 
00723     if ( pos == len ) // can't happen, the caller checked this already
00724     m_strPath = QString::null;
00725     else
00726     m_strPath = decode( QString( buf + pos, len - pos ), encoding_hint );
00727 
00728     m_bIsMalformed = false;
00729 
00730     return;
00731 }
00732 
00733 void KURL::parseMailto( const QString& _url, int encoding_hint )
00734 {
00735     parseURL( _url, encoding_hint);
00736     if ( m_bIsMalformed )
00737         return;
00738     QRegExp mailre("(.+@)(.+)");
00739     if ( mailre.exactMatch( m_strPath ) )
00740     {
00741 #ifndef KDE_QT_ONLY
00742     QString host = KIDNA::toUnicode( mailre.cap( 2 ) );
00743     if (host.isEmpty())
00744         host = mailre.cap( 2 ).lower();
00745 #else
00746     QString host = mailre.cap( 2 ).lower();
00747 #endif
00748     m_strPath = mailre.cap( 1 ) + host;
00749   }
00750 }
00751 
00752 void KURL::parseURL( const QString& _url, int encoding_hint )
00753 {
00754   QString port;
00755   bool badHostName = false;
00756   int start = 0;
00757   uint len = _url.length();
00758   const QChar* buf = _url.unicode();
00759 
00760   QChar delim;
00761   QString tmp;
00762 
00763   uint pos = 0;
00764 
00765   // Node 1: Accept alpha or slash
00766   QChar x = buf[pos++];
00767 #ifdef Q_WS_WIN
00768   /* win32: accept <letter>: or <letter>:/ or <letter>:\ */
00769   const bool alpha = isalpha((int)x);
00770   if (alpha && len<2)
00771     goto NodeErr;
00772   if (alpha && buf[pos]==':' && (len==2 || (len>2 && (buf[pos+1]=='/' || buf[pos+1]=='\\'))))
00773 #else
00774   if ( x == '/' )
00775 #endif
00776     goto Node9;
00777   if ( !isalpha( (int)x ) )
00778     goto NodeErr;
00779 
00780   // Node 2: Accept any amount of (alpha|digit|'+'|'-')
00781   // '.' is not currently accepted, because current KURL may be confused.
00782   // Proceed with :// :/ or :
00783   while( pos < len && (isalpha((int)buf[pos]) || isdigit((int)buf[pos]) ||
00784           buf[pos] == '+' || buf[pos] == '-')) pos++;
00785 
00786   // Note that m_strProtocol is already set here, so we just skip over the protocol.
00787   if ( pos+2 < len && buf[pos] == ':' && buf[pos+1] == '/' && buf[pos+2] == '/' )
00788     {
00789       pos += 3;
00790     }
00791   else if (pos+1 < len && buf[pos] == ':' ) // Need to always compare length()-1 otherwise KURL passes "http:" as legal!!
00792     {
00793       pos++;
00794       start = pos;
00795       goto Node9;
00796     }
00797   else
00798     goto NodeErr;
00799 
00800   //Node 3: We need at least one character here
00801   if ( pos == len )
00802       goto NodeErr;
00803   start = pos;
00804 
00805   // Node 4: Accept any amount of characters.
00806   if (buf[pos] == '[')     // An IPv6 host follows.
00807       goto Node8;
00808   // Terminate on / or @ or ? or # or " or ; or <
00809   x = buf[pos];
00810   while( (x != ':') && (x != '@') && (x != '/') && (x != '?') && (x != '#') )
00811   {
00812      if ((x == '\"') || (x == ';') || (x == '<'))
00813         badHostName = true;
00814      if (++pos == len)
00815         break;
00816      x = buf[pos];
00817   }
00818   if ( pos == len )
00819     {
00820       if (badHostName)
00821          goto NodeErr;
00822 
00823       setHost(decode(QString( buf + start, pos - start ), encoding_hint));
00824       goto NodeOk;
00825     }
00826   if ( x == '@' )
00827     {
00828       m_strUser = decode(QString( buf + start, pos - start ), encoding_hint);
00829       pos++;
00830       goto Node7;
00831     }
00832   else if ( (x == '/') || (x == '?') || (x == '#'))
00833     {
00834       if (badHostName)
00835          goto NodeErr;
00836 
00837       setHost(decode(QString( buf + start, pos - start ), encoding_hint));
00838       start = pos;
00839       goto Node9;
00840     }
00841   else if ( x != ':' )
00842     goto NodeErr;
00843   m_strUser = decode(QString( buf + start, pos - start ), encoding_hint);
00844   pos++;
00845 
00846   // Node 5: We need at least one character
00847   if ( pos == len )
00848     goto NodeErr;
00849   start = pos++;
00850 
00851   // Node 6: Read everything until @, /, ? or #
00852   while( (pos < len) &&
00853         (buf[pos] != '@') &&
00854         (buf[pos] != '/') &&
00855         (buf[pos] != '?') &&
00856         (buf[pos] != '#')) pos++;
00857   // If we now have a '@' the ':' seperates user and password.
00858   // Otherwise it seperates host and port.
00859   if ( (pos == len) || (buf[pos] != '@') )
00860     {
00861       // Ok the : was used to separate host and port
00862       if (badHostName)
00863          goto NodeErr;
00864       setHost(m_strUser);
00865       m_strUser = QString::null;
00866       QString tmp( buf + start, pos - start );
00867       char *endptr;
00868       m_iPort = (unsigned short int)strtol(tmp.ascii(), &endptr, 10);
00869       if ((pos == len) && (strlen(endptr) == 0))
00870         goto NodeOk;
00871       // there is more after the digits
00872       pos -= strlen(endptr);
00873       if ((buf[pos] != '@') &&
00874           (buf[pos] != '/') &&
00875           (buf[pos] != '?') &&
00876           (buf[pos] != '#'))
00877         goto NodeErr;
00878 
00879       start = pos;
00880       goto Node9;
00881     }
00882   m_strPass = decode(QString( buf + start, pos - start), encoding_hint);
00883   pos++;
00884 
00885   // Node 7: We need at least one character
00886  Node7:
00887   if ( pos == len )
00888     goto NodeErr;
00889 
00890  Node8:
00891   if (buf[pos] == '[')
00892   {
00893     // IPv6 address
00894     start = ++pos; // Skip '['
00895 
00896     if (pos == len)
00897     {
00898        badHostName = true;
00899        goto NodeErr;
00900     }
00901     // Node 8a: Read everything until ] or terminate
00902     badHostName = false;
00903     x = buf[pos];
00904     while( (x != ']') )
00905     {
00906        if ((x == '\"') || (x == ';') || (x == '<'))
00907           badHostName = true;
00908        if (++pos == len)
00909        {
00910           badHostName = true;
00911           break;
00912        }
00913        x = buf[pos];
00914     }
00915     if (badHostName)
00916        goto NodeErr;
00917     setHost(decode(QString( buf + start, pos - start ), encoding_hint));
00918     if (pos < len) pos++; // Skip ']'
00919     if (pos == len)
00920        goto NodeOk;
00921   }
00922   else
00923   {
00924     // Non IPv6 address, with a user
00925     start = pos;
00926 
00927     // Node 8b: Read everything until / : or terminate
00928     badHostName = false;
00929     x = buf[pos];
00930     while( (x != ':') && (x != '@') && (x != '/') && (x != '?') && (x != '#') )
00931     {
00932        if ((x == '\"') || (x == ';') || (x == '<'))
00933           badHostName = true;
00934        if (++pos == len)
00935           break;
00936        x = buf[pos];
00937     }
00938     if (badHostName)
00939        goto NodeErr;
00940     if ( pos == len )
00941     {
00942        setHost(decode(QString( buf + start, pos - start ), encoding_hint));
00943        goto NodeOk;
00944     }
00945     setHost(decode(QString( buf + start, pos - start ), encoding_hint));
00946   }
00947   x = buf[pos];
00948   if ( x == '/' || x == '#' || x == '?' )
00949     {
00950       start = pos;
00951       goto Node9;
00952     }
00953   else if ( x != ':' )
00954     goto NodeErr;
00955   pos++;
00956 
00957   // Node 8c: Accept at least one digit
00958   if ( pos == len )
00959     goto NodeErr;
00960   start = pos;
00961   if ( !isdigit( buf[pos++] ) )
00962     goto NodeErr;
00963 
00964   // Node 8d: Accept any amount of digits
00965   while( pos < len && isdigit( buf[pos] ) ) pos++;
00966   port = QString( buf + start, pos - start );
00967   m_iPort = port.toUShort();
00968   if ( pos == len )
00969     goto NodeOk;
00970   start = pos;
00971 
00972  Node9: // parse path until query or reference reached
00973 
00974   while( pos < len && buf[pos] != '#' && buf[pos]!='?' ) pos++;
00975 
00976   tmp = QString( buf + start, pos - start );
00977   //kdDebug(126)<<" setting encoded path to:"<<tmp<<endl;
00978   setEncodedPath( tmp, encoding_hint );
00979 
00980   if ( pos == len )
00981       goto NodeOk;
00982 
00983  //Node10: // parse query or reference depending on what comes first
00984   delim = (buf[pos++]=='#'?'?':'#');
00985 
00986   start = pos;
00987 
00988   while(pos < len && buf[pos]!=delim ) pos++;
00989 
00990   tmp = QString(buf + start, pos - start);
00991   if (delim=='#')
00992       _setQuery(tmp, encoding_hint);
00993   else
00994       m_strRef_encoded = tmp;
00995 
00996   if (pos == len)
00997       goto NodeOk;
00998 
00999  //Node11: // feed the rest into the remaining variable
01000   tmp = QString( buf + pos + 1, len - pos - 1);
01001   if (delim == '#')
01002       m_strRef_encoded = tmp;
01003   else
01004       _setQuery(tmp, encoding_hint);
01005 
01006  NodeOk:
01007   //kdDebug(126)<<"parsing finished. m_strProtocol="<<m_strProtocol<<" m_strHost="<<m_strHost<<" m_strPath="<<m_strPath<<endl;
01008   m_bIsMalformed = false; // Valid URL
01009 
01010   //kdDebug()<<"Prot="<<m_strProtocol<<"\nUser="<<m_strUser<<"\nPass="<<m_strPass<<"\nHost="<<m_strHost<<"\nPath="<<m_strPath<<"\nQuery="<<m_strQuery_encoded<<"\nRef="<<m_strRef_encoded<<"\nPort="<<m_iPort<<endl;
01011   if (m_strProtocol.isEmpty())
01012   {
01013     m_iUriMode = URL;
01014     m_strProtocol = fileProt;
01015   }
01016   return;
01017 
01018  NodeErr:
01019 //  kdDebug(126) << "KURL couldn't parse URL \"" << _url << "\"" << endl;
01020   reset();
01021   m_strProtocol = _url;
01022   m_iUriMode = Invalid;
01023 }
01024 
01025 KURL& KURL::operator=( const QString& _url )
01026 {
01027   reset();
01028   parse( _url );
01029 
01030   return *this;
01031 }
01032 
01033 KURL& KURL::operator=( const char * _url )
01034 {
01035   reset();
01036   parse( QString::fromLatin1(_url) );
01037 
01038   return *this;
01039 }
01040 
01041 #ifndef QT_NO_NETWORKPROTOCOL
01042 KURL& KURL::operator=( const QUrl & u )
01043 {
01044   m_strProtocol = u.protocol();
01045   m_iUriMode = Auto;
01046   m_strUser = u.user();
01047   m_strPass = u.password();
01048   m_strHost = u.host();
01049   m_strPath = u.path( false );
01050   m_strPath_encoded = QString::null;
01051   m_strQuery_encoded = u.query();
01052   m_strRef_encoded = u.ref();
01053   m_bIsMalformed = !u.isValid();
01054   m_iPort = u.port();
01055 
01056   return *this;
01057 }
01058 #endif
01059 
01060 KURL& KURL::operator=( const KURL& _u )
01061 {
01062   m_strProtocol = _u.m_strProtocol;
01063   m_strUser = _u.m_strUser;
01064   m_strPass = _u.m_strPass;
01065   m_strHost = _u.m_strHost;
01066   m_strPath = _u.m_strPath;
01067   m_strPath_encoded = _u.m_strPath_encoded;
01068   m_strQuery_encoded = _u.m_strQuery_encoded;
01069   m_strRef_encoded = _u.m_strRef_encoded;
01070   m_bIsMalformed = _u.m_bIsMalformed;
01071   m_iPort = _u.m_iPort;
01072   m_iUriMode = _u.m_iUriMode;
01073 
01074   return *this;
01075 }
01076 
01077 bool KURL::operator<( const KURL& _u) const
01078 {
01079   int i;
01080   if (!_u.isValid())
01081   {
01082      if (!isValid())
01083      {
01084         i = m_strProtocol.compare(_u.m_strProtocol);
01085         return (i < 0);
01086      }
01087      return false;
01088   }
01089   if (!isValid())
01090      return true;
01091 
01092   i = m_strProtocol.compare(_u.m_strProtocol);
01093   if (i) return (i < 0);
01094 
01095   i = m_strHost.compare(_u.m_strHost);
01096   if (i) return (i < 0);
01097 
01098   if (m_iPort != _u.m_iPort) return (m_iPort < _u.m_iPort);
01099 
01100   i = m_strPath.compare(_u.m_strPath);
01101   if (i) return (i < 0);
01102 
01103   i = m_strQuery_encoded.compare(_u.m_strQuery_encoded);
01104   if (i) return (i < 0);
01105 
01106   i = m_strRef_encoded.compare(_u.m_strRef_encoded);
01107   if (i) return (i < 0);
01108 
01109   i = m_strUser.compare(_u.m_strUser);
01110   if (i) return (i < 0);
01111 
01112   i = m_strPass.compare(_u.m_strPass);
01113   if (i) return (i < 0);
01114 
01115   return false;
01116 }
01117 
01118 bool KURL::operator==( const KURL& _u ) const
01119 {
01120   if ( !isValid() || !_u.isValid() )
01121     return false;
01122 
01123   if ( m_strProtocol == _u.m_strProtocol &&
01124        m_strUser == _u.m_strUser &&
01125        m_strPass == _u.m_strPass &&
01126        m_strHost == _u.m_strHost &&
01127        m_strPath == _u.m_strPath &&
01128        // The encoded path may be null, but the URLs are still equal (David)
01129        ( m_strPath_encoded.isNull() || _u.m_strPath_encoded.isNull() ||
01130          m_strPath_encoded == _u.m_strPath_encoded ) &&
01131        m_strQuery_encoded == _u.m_strQuery_encoded &&
01132        m_strRef_encoded == _u.m_strRef_encoded &&
01133        m_iPort == _u.m_iPort )
01134   {
01135     return true;
01136   }
01137 
01138   return false;
01139 }
01140 
01141 bool KURL::operator==( const QString& _u ) const
01142 {
01143   KURL u( _u );
01144   return ( *this == u );
01145 }
01146 
01147 bool KURL::cmp( const KURL &u, bool ignore_trailing ) const
01148 {
01149   return equals( u, ignore_trailing );
01150 }
01151 
01152 bool KURL::equals( const KURL &_u, bool ignore_trailing ) const
01153 {
01154   if ( !isValid() || !_u.isValid() )
01155     return false;
01156 
01157   if ( ignore_trailing )
01158   {
01159     QString path1 = path(1);
01160     QString path2 = _u.path(1);
01161     if ( path1 != path2 )
01162       return false;
01163 
01164     if ( m_strProtocol == _u.m_strProtocol &&
01165          m_strUser == _u.m_strUser &&
01166          m_strPass == _u.m_strPass &&
01167          m_strHost == _u.m_strHost &&
01168          m_strQuery_encoded == _u.m_strQuery_encoded &&
01169          m_strRef_encoded == _u.m_strRef_encoded &&
01170          m_iPort == _u.m_iPort )
01171       return true;
01172 
01173     return false;
01174   }
01175 
01176   return ( *this == _u );
01177 }
01178 
01179 bool KURL::isParentOf( const KURL& _u ) const
01180 {
01181   if ( !isValid() || !_u.isValid() )
01182     return false;
01183 
01184   if ( m_strProtocol == _u.m_strProtocol &&
01185        m_strUser == _u.m_strUser &&
01186        m_strPass == _u.m_strPass &&
01187        m_strHost == _u.m_strHost &&
01188        m_strQuery_encoded == _u.m_strQuery_encoded &&
01189        m_strRef_encoded == _u.m_strRef_encoded &&
01190        m_iPort == _u.m_iPort )
01191   {
01192     if ( path().isEmpty() || _u.path().isEmpty() )
01193         return false; // can't work with implicit paths
01194 
01195     QString p1( cleanpath( path(), true, false ) );
01196     if ( p1[p1.length()-1] != '/' )
01197         p1 += '/';
01198     QString p2( cleanpath( _u.path(), true, false ) );
01199     if ( p2[p2.length()-1] != '/' )
01200         p2 += '/';
01201 
01202     //kdDebug(126) << "p1=" << p1 << endl;
01203     //kdDebug(126) << "p2=" << p2 << endl;
01204     //kdDebug(126) << "p1.length()=" << p1.length() << endl;
01205     //kdDebug(126) << "p2.left(!$)=" << p2.left( p1.length() ) << endl;
01206     return p2.startsWith( p1 );
01207   }
01208   return false;
01209 }
01210 
01211 void KURL::setFileName( const QString& _txt )
01212 {
01213   m_strRef_encoded = QString::null;
01214   int i = 0;
01215   while( _txt[i] == '/' ) ++i;
01216   QString tmp;
01217   if ( i )
01218     tmp = _txt.mid( i );
01219   else
01220     tmp = _txt;
01221 
01222   QString path = m_strPath_encoded.isEmpty() ? m_strPath : m_strPath_encoded;
01223   if ( path.isEmpty() )
01224     path = "/";
01225   else
01226   {
01227     int lastSlash = path.findRev( '/' );
01228     if ( lastSlash == -1)
01229     {
01230       // The first character is not a '/' ???
01231       // This looks strange ...
01232       path = "/";
01233     }
01234     else if ( path.right(1) != "/" )
01235       path.truncate( lastSlash+1 ); // keep the "/"
01236   }
01237   if (m_strPath_encoded.isEmpty())
01238   {
01239      path += tmp;
01240      setPath( path );
01241   }
01242   else
01243   {
01244      path += encode_string(tmp);
01245      setEncodedPath( path );
01246   }
01247   cleanPath();
01248 }
01249 
01250 void KURL::cleanPath( bool cleanDirSeparator ) // taken from the old KURL
01251 {
01252   if (m_iUriMode != URL) return;
01253   m_strPath = cleanpath(m_strPath, cleanDirSeparator, false);
01254   // WABA: Is this safe when "/../" is encoded with %?
01255   m_strPath_encoded = cleanpath(m_strPath_encoded, cleanDirSeparator, true);
01256 }
01257 
01258 static QString trailingSlash( int _trailing, const QString &path )
01259 {
01260   QString result = path;
01261 
01262   if ( _trailing == 0 )
01263     return result;
01264   else if ( _trailing == 1 )
01265   {
01266     int len = result.length();
01267     if ( (len == 0) || (result[ len - 1 ] != '/') )
01268       result += "/";
01269     return result;
01270   }
01271   else if ( _trailing == -1 )
01272   {
01273     if ( result == "/" )
01274       return result;
01275     int len = result.length();
01276     if ( (len != 0) && (result[ len - 1 ] == '/') )
01277       result.truncate( len - 1 );
01278     return result;
01279   }
01280   else {
01281     assert( 0 );
01282     return QString::null;
01283   }
01284 }
01285 
01286 void KURL::adjustPath( int _trailing )
01287 {
01288   if (!m_strPath_encoded.isEmpty())
01289   {
01290      m_strPath_encoded = trailingSlash( _trailing, m_strPath_encoded );
01291   }
01292   m_strPath = trailingSlash( _trailing, m_strPath );
01293 }
01294 
01295 
01296 QString KURL::encodedPathAndQuery( int _trailing, bool _no_empty_path, int encoding_hint ) const
01297 {
01298   QString tmp;
01299   if (!m_strPath_encoded.isEmpty() && encoding_hint == 0)
01300   {
01301      tmp = trailingSlash( _trailing, m_strPath_encoded );
01302   }
01303   else
01304   {
01305      tmp = path( _trailing );
01306      if ( _no_empty_path && tmp.isEmpty() )
01307         tmp = "/";
01308      if (m_iUriMode == Mailto)
01309      {
01310         tmp = encode( tmp, 2, encoding_hint );
01311      }
01312      else
01313      {
01314         tmp = encode( tmp, 1, encoding_hint );
01315      }
01316   }
01317 
01318   // TODO apply encoding_hint to the query
01319   if (!m_strQuery_encoded.isNull())
01320       tmp += '?' + m_strQuery_encoded;
01321   return tmp;
01322 }
01323 
01324 void KURL::setEncodedPath( const QString& _txt, int encoding_hint )
01325 {
01326   m_strPath_encoded = _txt;
01327 
01328   decode( m_strPath_encoded, m_strPath, m_strPath_encoded, encoding_hint );
01329   // Throw away encoding for local files, makes file-operations faster.
01330   if (m_strProtocol == fileProt)
01331      m_strPath_encoded = QString::null;
01332 
01333   if ( m_iUriMode == Auto )
01334     m_iUriMode = URL;
01335 }
01336 
01337 
01338 void KURL::setEncodedPathAndQuery( const QString& _txt, int encoding_hint )
01339 {
01340   int pos = _txt.find( '?' );
01341   if ( pos == -1 )
01342   {
01343     setEncodedPath(_txt, encoding_hint);
01344     m_strQuery_encoded = QString::null;
01345   }
01346   else
01347   {
01348     setEncodedPath(_txt.left( pos ), encoding_hint);
01349     _setQuery(_txt.right(_txt.length() - pos - 1), encoding_hint);
01350   }
01351 }
01352 
01353 QString KURL::path( int _trailing ) const
01354 {
01355   return trailingSlash( _trailing, path() );
01356 }
01357 
01358 bool KURL::isLocalFile() const
01359 {
01360   if ( (m_strProtocol != fileProt ) || hasSubURL() )
01361      return false;
01362 
01363   if (m_strHost.isEmpty() || (m_strHost == "localhost"))
01364      return true;
01365 
01366   char hostname[ 256 ];
01367   hostname[ 0 ] = '\0';
01368   if (!gethostname( hostname, 255 ))
01369      hostname[sizeof(hostname)-1] = '\0';
01370 
01371   for(char *p = hostname; *p; p++)
01372      *p = tolower(*p);
01373 
01374   return (m_strHost == hostname);
01375 }
01376 
01377 void KURL::setFileEncoding(const QString &encoding)
01378 {
01379   if (!isLocalFile())
01380      return;
01381 
01382   QString q = query();
01383 
01384   if (!q.isEmpty() && (q[0] == '?'))
01385      q = q.mid(1);
01386 
01387   QStringList args = QStringList::split('&', q);
01388   for(QStringList::Iterator it = args.begin();
01389       it != args.end();)
01390   {
01391       QString s = decode_string(*it);
01392       if (s.startsWith("charset="))
01393          it = args.erase(it);
01394       else
01395          ++it;
01396   }
01397   if (!encoding.isEmpty())
01398      args.append("charset="+encode_string(encoding));
01399 
01400   if (args.isEmpty())
01401      _setQuery(QString::null);
01402   else
01403      _setQuery(args.join("&"));
01404 }
01405 
01406 QString KURL::fileEncoding() const
01407 {
01408   if (!isLocalFile())
01409      return QString::null;
01410 
01411   QString q = query();
01412 
01413   if (q.isEmpty())
01414      return QString::null;
01415 
01416   if (q[0] == '?')
01417      q = q.mid(1);
01418 
01419   QStringList args = QStringList::split('&', q);
01420   for(QStringList::ConstIterator it = args.begin();
01421       it != args.end();
01422       ++it)
01423   {
01424       QString s = decode_string(*it);
01425       if (s.startsWith("charset="))
01426          return s.mid(8);
01427   }
01428   return QString::null;
01429 }
01430 
01431 bool KURL::hasSubURL() const
01432 {
01433   if ( m_strProtocol.isEmpty() || m_bIsMalformed )
01434     return false;
01435   if (m_strRef_encoded.isEmpty())
01436      return false;
01437   if (m_strRef_encoded.startsWith("gzip:"))
01438      return true;
01439   if (m_strRef_encoded.startsWith("bzip:"))
01440      return true;
01441   if (m_strRef_encoded.startsWith("bzip2:"))
01442      return true;
01443   if (m_strRef_encoded.startsWith("tar:"))
01444      return true;
01445   if (m_strRef_encoded.startsWith("ar:"))
01446      return true;
01447   if (m_strRef_encoded.startsWith("zip:"))
01448      return true;
01449   if ( m_strProtocol == "error" ) // anything that starts with error: has suburls
01450      return true;
01451   return false;
01452 }
01453 
01454 QString KURL::url( int _trailing, int encoding_hint ) const
01455 {
01456   if( m_bIsMalformed )
01457   {
01458     // Return the whole url even when the url is
01459     // malformed.  Under such conditions the url
01460     // is stored in m_strProtocol.
01461     return m_strProtocol;
01462   }
01463 
01464   QString u = m_strProtocol;
01465   if (!u.isEmpty())
01466     u += ":";
01467 
01468   if ( hasHost() || (m_strProtocol == fileProt) )
01469   {
01470     u += "//";
01471     if ( hasUser() )
01472     {
01473       u += encode(m_strUser, 0, encoding_hint);
01474       if ( hasPass() )
01475       {
01476         u += ":";
01477         u += encode(m_strPass, 0, encoding_hint);
01478       }
01479       u += "@";
01480     }
01481     if ( m_iUriMode == URL )
01482     {
01483       bool IPv6 = (m_strHost.find(':') != -1);
01484       if (IPv6)
01485         u += '[' + m_strHost + ']';
01486       else
01487         u += encodeHost(m_strHost, true, encoding_hint);
01488       if ( m_iPort != 0 ) {
01489         QString buffer;
01490         buffer.sprintf( ":%u", m_iPort );
01491         u += buffer;
01492       }
01493     }
01494     else
01495     {
01496       u += m_strHost;
01497     }
01498   }
01499 
01500   if ( m_iUriMode == URL || m_iUriMode == Mailto )
01501     u += encodedPathAndQuery( _trailing, false, encoding_hint );
01502   else
01503     u += m_strPath;
01504 
01505   if ( hasRef() )
01506   {
01507     u += "#";
01508     u += m_strRef_encoded;
01509   }
01510 
01511   return u;
01512 }
01513 
01514 QString KURL::prettyURL( int _trailing ) const
01515 {
01516   if( m_bIsMalformed )
01517   {
01518     // Return the whole url even when the url is
01519     // malformed.  Under such conditions the url
01520     // is stored in m_strProtocol.
01521     return m_strProtocol;
01522   }
01523 
01524   QString u = m_strProtocol;
01525   if (!u.isEmpty())
01526      u += ":";
01527 
01528   if ( hasHost() || (m_strProtocol == fileProt) )
01529   {
01530     u += "//";
01531     if ( hasUser() )
01532     {
01533       u += lazy_encode(m_strUser);
01534       // Don't show password!
01535       u += "@";
01536     }
01537     if ( m_iUriMode == URL )
01538     {
01539     bool IPv6 = (m_strHost.find(':') != -1);
01540     if (IPv6)
01541     {
01542        u += '[' + m_strHost + ']';
01543     }
01544     else
01545     {
01546        u += lazy_encode(m_strHost);
01547     }
01548     }
01549     else
01550     {
01551       u += lazy_encode(m_strHost);
01552     }
01553     if ( m_iPort != 0 ) {
01554       QString buffer;
01555       buffer.sprintf( ":%u", m_iPort );
01556       u += buffer;
01557     }
01558   }
01559 
01560   if (m_iUriMode == Mailto)
01561   {
01562      u += lazy_encode( m_strPath, false );
01563   }
01564   else
01565   {
01566      u += trailingSlash( _trailing, lazy_encode( m_strPath ) );
01567   }
01568 
01569   if (!m_strQuery_encoded.isNull())
01570       u += '?' + m_strQuery_encoded;
01571 
01572   if ( hasRef() )
01573   {
01574     u += "#";
01575     u += m_strRef_encoded;
01576   }
01577 
01578   return u;
01579 }
01580 
01581 QString KURL::prettyURL( int _trailing, AdjustementFlags _flags) const
01582 {
01583   QString u = prettyURL(_trailing);
01584   if (_flags & StripFileProtocol && u.startsWith("file://")) {
01585     u.remove(0, 7);
01586 #ifdef Q_WS_WIN
01587     return QDir::convertSeparators(u);
01588 #endif
01589   }
01590   return u;
01591 }
01592 
01593 QString KURL::pathOrURL() const
01594 {
01595   if ( isLocalFile() && m_strRef_encoded.isNull() && m_strQuery_encoded.isNull() ) {
01596     return path();
01597   } else {
01598     return prettyURL();
01599   }
01600 }
01601 
01602 QString KURL::htmlURL() const
01603 {
01604   return QStyleSheet::escape(prettyURL());
01605 }
01606 
01607 KURL::List KURL::split( const KURL& _url )
01608 {
01609   QString ref;
01610   KURL::List lst;
01611   KURL url = _url;
01612 
01613   while(true)
01614   {
01615      KURL u = url;
01616      u.m_strRef_encoded = QString::null;
01617      lst.append(u);
01618      if (url.hasSubURL())
01619      {
01620         url = KURL(url.m_strRef_encoded);
01621      }
01622      else
01623      {
01624         ref = url.m_strRef_encoded;
01625         break;
01626      }
01627   }
01628 
01629   // Set HTML ref in all URLs.
01630   KURL::List::Iterator it;
01631   for( it = lst.begin() ; it != lst.end(); ++it )
01632   {
01633      (*it).m_strRef_encoded = ref;
01634   }
01635 
01636   return lst;
01637 }
01638 
01639 KURL::List KURL::split( const QString& _url )
01640 {
01641   return split(KURL(_url));
01642 }
01643 
01644 KURL KURL::join( const KURL::List & lst )
01645 {
01646   if (lst.isEmpty()) return KURL();
01647   KURL tmp;
01648 
01649   KURL::List::ConstIterator first = lst.fromLast();
01650   for( KURL::List::ConstIterator it = first; it != lst.end(); --it )
01651   {
01652      KURL u(*it);
01653      if (it != first)
01654      {
01655         if (!u.m_strRef_encoded) u.m_strRef_encoded = tmp.url();
01656         else u.m_strRef_encoded += "#" + tmp.url(); // Support more than one suburl thingy
01657      }
01658      tmp = u;
01659   }
01660 
01661   return tmp;
01662 }
01663 
01664 QString KURL::fileName( bool _strip_trailing_slash ) const
01665 {
01666   QString fname;
01667   if (hasSubURL()) { // If we have a suburl, then return the filename from there
01668     KURL::List list = KURL::split(*this);
01669     KURL::List::Iterator it = list.fromLast();
01670     return (*it).fileName(_strip_trailing_slash);
01671   }
01672   const QString &path = m_strPath;
01673 
01674   int len = path.length();
01675   if ( len == 0 )
01676     return fname;
01677 
01678   if ( _strip_trailing_slash )
01679   {
01680     while ( len >= 1 && path[ len - 1 ] == '/' )
01681       len--;
01682   }
01683   else if ( path[ len - 1 ] == '/' )
01684     return fname;
01685 
01686   // Does the path only consist of '/' characters ?
01687   if ( len == 1 && path[ 0 ] == '/' )
01688     return fname;
01689 
01690   // Skip last n slashes
01691   int n = 1;
01692   if (!m_strPath_encoded.isEmpty())
01693   {
01694      // This is hairy, we need the last unencoded slash.
01695      // Count in the encoded string how many encoded slashes follow the last
01696      // unencoded one.
01697      int i = m_strPath_encoded.findRev( '/', len - 1 );
01698      QString fileName_encoded = m_strPath_encoded.mid(i+1);
01699      n += fileName_encoded.contains("%2f", false);
01700   }
01701   int i = len;
01702   do {
01703     i = path.findRev( '/', i - 1 );
01704   }
01705   while (--n && (i > 0));
01706 
01707   // If ( i == -1 ) => the first character is not a '/'
01708   // So it's some URL like file:blah.tgz, return the whole path
01709   if ( i == -1 ) {
01710     if ( len == (int)path.length() )
01711       fname = path;
01712     else
01713       // Might get here if _strip_trailing_slash is true
01714       fname = path.left( len );
01715   }
01716   else
01717   {
01718      fname = path.mid( i + 1, len - i - 1 ); // TO CHECK
01719   }
01720   return fname;
01721 }
01722 
01723 void KURL::addPath( const QString& _txt )
01724 {
01725   if (hasSubURL())
01726   {
01727      KURL::List lst = split( *this );
01728      KURL &u = lst.last();
01729      u.addPath(_txt);
01730      *this = join( lst );
01731      return;
01732   }
01733 
01734   m_strPath_encoded = QString::null;
01735 
01736   if ( _txt.isEmpty() )
01737     return;
01738 
01739   int i = 0;
01740   int len = m_strPath.length();
01741   // Add the trailing '/' if it is missing
01742   if ( _txt[0] != '/' && ( len == 0 || m_strPath[ len - 1 ] != '/' ) )
01743     m_strPath += "/";
01744 
01745   // No double '/' characters
01746   i = 0;
01747   if ( len != 0 && m_strPath[ len - 1 ] == '/' )
01748   {
01749     while( _txt[i] == '/' )
01750       ++i;
01751   }
01752 
01753   m_strPath += _txt.mid( i );
01754 }
01755 
01756 QString KURL::directory( bool _strip_trailing_slash_from_result,
01757                          bool _ignore_trailing_slash_in_path ) const
01758 {
01759   QString result = m_strPath_encoded.isEmpty() ? m_strPath : m_strPath_encoded;
01760   if ( _ignore_trailing_slash_in_path )
01761     result = trailingSlash( -1, result );
01762 
01763   if ( result.isEmpty() || result == "/" )
01764     return result;
01765 
01766   int i = result.findRev( "/" );
01767   // If ( i == -1 ) => the first character is not a '/'
01768   // So it's some URL like file:blah.tgz, with no path
01769   if ( i == -1 )
01770     return QString::null;
01771 
01772   if ( i == 0 )
01773   {
01774     result = "/";
01775     return result;
01776   }
01777 
01778   if ( _strip_trailing_slash_from_result )
01779     result = result.left( i );
01780   else
01781     result = result.left( i + 1 );
01782 
01783   if (!m_strPath_encoded.isEmpty())
01784     result = decode(result);
01785 
01786   return result;
01787 }
01788 
01789 
01790 bool KURL::cd( const QString& _dir )
01791 {
01792   if ( _dir.isEmpty() || m_bIsMalformed )
01793     return false;
01794 
01795   if (hasSubURL())
01796   {
01797      KURL::List lst = split( *this );
01798      KURL &u = lst.last();
01799      u.cd(_dir);
01800      *this = join( lst );
01801      return true;
01802   }
01803 
01804   // absolute path ?
01805   if ( _dir[0] == '/' )
01806   {
01807     m_strPath_encoded = QString::null;
01808     m_strPath = _dir;
01809     setHTMLRef( QString::null );
01810     m_strQuery_encoded = QString::null;
01811     return true;
01812   }
01813 
01814   // Users home directory on the local disk ?
01815   if ( ( _dir[0] == '~' ) && ( m_strProtocol == fileProt ))
01816   {
01817     m_strPath_encoded = QString::null;
01818     m_strPath = QDir::homeDirPath();
01819     m_strPath += "/";
01820     m_strPath += _dir.right(m_strPath.length() - 1);
01821     setHTMLRef( QString::null );
01822     m_strQuery_encoded = QString::null;
01823     return true;
01824   }
01825 
01826   // relative path
01827   // we always work on the past of the first url.
01828   // Sub URLs are not touched.
01829 
01830   // append '/' if necessary
01831   QString p = path(1);
01832   p += _dir;
01833   p = cleanpath( p, true, false );
01834   setPath( p );
01835 
01836   setHTMLRef( QString::null );
01837   m_strQuery_encoded = QString::null;
01838 
01839   return true;
01840 }
01841 
01842 KURL KURL::upURL( ) const
01843 {
01844   if (!query().isEmpty())
01845   {
01846      KURL u(*this);
01847      u._setQuery(QString::null);
01848      return u;
01849   };
01850 
01851   if (!hasSubURL())
01852   {
01853      KURL u(*this);
01854      u.cd("../");
01855      return u;
01856   }
01857 
01858   // We have a subURL.
01859   KURL::List lst = split( *this );
01860   if (lst.isEmpty())
01861       return KURL(); // Huh?
01862   while (true)
01863   {
01864      KURL &u = lst.last();
01865      QString old = u.path();
01866      u.cd("../");
01867      if (u.path() != old)
01868          break; // Finshed.
01869      if (lst.count() == 1)
01870          break; // Finished.
01871      lst.remove(lst.fromLast());
01872   }
01873   return join( lst );
01874 }
01875 
01876 QString KURL::htmlRef() const
01877 {
01878   if ( !hasSubURL() )
01879   {
01880     return decode( ref() );
01881   }
01882 
01883   List lst = split( *this );
01884   return decode( (*lst.begin()).ref() );
01885 }
01886 
01887 QString KURL::encodedHtmlRef() const
01888 {
01889   if ( !hasSubURL() )
01890   {
01891     return ref();
01892   }
01893 
01894   List lst = split( *this );
01895   return (*lst.begin()).ref();
01896 }
01897 
01898 void KURL::setHTMLRef( const QString& _ref )
01899 {
01900   if ( !hasSubURL() )
01901   {
01902     m_strRef_encoded = encode( _ref, 0, 0 /*?*/);
01903     return;
01904   }
01905 
01906   List lst = split( *this );
01907 
01908   (*lst.begin()).setRef( encode( _ref, 0, 0 /*?*/) );
01909 
01910   *this = join( lst );
01911 }
01912 
01913 bool KURL::hasHTMLRef() const
01914 {
01915   if ( !hasSubURL() )
01916   {
01917     return hasRef();
01918   }
01919 
01920   List lst = split( *this );
01921   return (*lst.begin()).hasRef();
01922 }
01923 
01924 void
01925 KURL::setProtocol( const QString& _txt )
01926 {
01927    m_strProtocol = _txt;
01928    if ( m_iUriMode == Auto ) m_iUriMode = uriModeForProtocol( m_strProtocol );
01929    m_bIsMalformed = false;
01930 }
01931 
01932 void
01933 KURL::setUser( const QString& _txt )
01934 {
01935    m_strUser = _txt;
01936 }
01937 
01938 void
01939 KURL::setPass( const QString& _txt )
01940 {
01941    m_strPass = _txt;
01942 }
01943 
01944 void
01945 KURL::setHost( const QString& _txt )
01946 {
01947   if ( m_iUriMode == Auto )
01948     m_iUriMode = URL;
01949   switch ( m_iUriMode )
01950   {
01951   case URL:
01952 #ifndef KDE_QT_ONLY
01953    m_strHost = KIDNA::toUnicode(_txt);
01954    if (m_strHost.isEmpty())
01955       m_strHost = _txt.lower(); // Probably an invalid hostname, but...
01956 #else
01957    m_strHost = _txt.lower();
01958 #endif
01959     break;
01960   default:
01961     m_strHost = _txt;
01962     break;
01963   }
01964 }
01965 
01966 void
01967 KURL::setPort( unsigned short int _p )
01968 {
01969    m_iPort = _p;
01970 }
01971 
01972 void KURL::setPath( const QString & path )
01973 {
01974   if (isEmpty())
01975     m_bIsMalformed = false;
01976   if (m_strProtocol.isEmpty())
01977   {
01978     m_strProtocol = fileProt;
01979   }
01980   m_strPath = path;
01981   m_strPath_encoded = QString::null;
01982   if ( m_iUriMode == Auto )
01983     m_iUriMode = URL;
01984 }
01985 
01986 void KURL::setDirectory( const QString &dir)
01987 {
01988   if ( dir.endsWith("/"))
01989      setPath(dir);
01990   else
01991      setPath(dir+"/");
01992 }
01993 
01994 void KURL::setQuery( const QString &_txt, int encoding_hint)
01995 {
01996    if (_txt[0] == '?')
01997       _setQuery( _txt.mid(1), encoding_hint );
01998    else
01999       _setQuery( _txt, encoding_hint );
02000 }
02001 
02002 // This is a private function that expects a query without '?'
02003 void KURL::_setQuery( const QString &_txt, int encoding_hint)
02004 {
02005    m_strQuery_encoded = _txt;
02006    if (!_txt.length())
02007       return;
02008 
02009    int l = m_strQuery_encoded.length();
02010    int i = 0;
02011    QString result;
02012    while (i < l)
02013    {
02014       int s = i;
02015       // Re-encode. Break encoded string up according to the reserved
02016       // characters '&:;=/?' and re-encode part by part.
02017       while(i < l)
02018       {
02019          char c = m_strQuery_encoded[i].latin1();
02020          if ((c == '&') || (c == ':') || (c == ';') ||
02021              (c == '=') || (c == '/') || (c == '?'))
02022             break;
02023          i++;
02024       }
02025       if (i > s)
02026       {
02027          QString tmp = m_strQuery_encoded.mid(s, i-s);
02028          QString newTmp;
02029          decode( tmp, newTmp, tmp, encoding_hint, false );
02030          result += tmp;
02031       }
02032       if (i < l)
02033       {
02034          result += m_strQuery_encoded[i];
02035          i++;
02036       }
02037    }
02038    m_strQuery_encoded = result;
02039 }
02040 
02041 QString KURL::query() const
02042 {
02043     if (m_strQuery_encoded.isNull())
02044         return QString::null;
02045     return '?'+m_strQuery_encoded;
02046 }
02047 
02048 QString KURL::decode_string(const QString &str, int encoding_hint)
02049 {
02050    return decode(str, encoding_hint);
02051 }
02052 
02053 QString KURL::encode_string(const QString &str, int encoding_hint)
02054 {
02055    return encode(str, 1, encoding_hint);
02056 }
02057 
02058 QString KURL::encode_string_no_slash(const QString &str, int encoding_hint)
02059 {
02060    return encode(str, 0, encoding_hint);
02061 }
02062 
02063 bool urlcmp( const QString& _url1, const QString& _url2 )
02064 {
02065   // Both empty ?
02066   if ( _url1.isEmpty() && _url2.isEmpty() )
02067     return true;
02068   // Only one empty ?
02069   if ( _url1.isEmpty() || _url2.isEmpty() )
02070     return false;
02071 
02072   KURL::List list1 = KURL::split( _url1 );
02073   KURL::List list2 = KURL::split( _url2 );
02074 
02075   // Malformed ?
02076   if ( list1.isEmpty() || list2.isEmpty() )
02077     return false;
02078 
02079   return ( list1 == list2 );
02080 }
02081 
02082 bool urlcmp( const QString& _url1, const QString& _url2, bool _ignore_trailing, bool _ignore_ref )
02083 {
02084   // Both empty ?
02085   if ( _url1.isEmpty() && _url2.isEmpty() )
02086     return true;
02087   // Only one empty ?
02088   if ( _url1.isEmpty() || _url2.isEmpty() )
02089     return false;
02090 
02091   KURL::List list1 = KURL::split( _url1 );
02092   KURL::List list2 = KURL::split( _url2 );
02093 
02094   // Malformed ?
02095   if ( list1.isEmpty() || list2.isEmpty() )
02096     return false;
02097 
02098   unsigned int size = list1.count();
02099   if ( list2.count() != size )
02100     return false;
02101 
02102   if ( _ignore_ref )
02103   {
02104     (*list1.begin()).setRef(QString::null);
02105     (*list2.begin()).setRef(QString::null);
02106   }
02107 
02108   KURL::List::Iterator it1 = list1.begin();
02109   KURL::List::Iterator it2 = list2.begin();
02110   for( ; it1 != list1.end() ; ++it1, ++it2 )
02111     if ( !(*it1).equals( *it2, _ignore_trailing ) )
02112       return false;
02113 
02114   return true;
02115 }
02116 
02117 QMap< QString, QString > KURL::queryItems( int options ) const {
02118   return queryItems(options, 0);
02119 }
02120 
02121 QMap< QString, QString > KURL::queryItems( int options, int encoding_hint ) const {
02122   if ( m_strQuery_encoded.isEmpty() )
02123     return QMap<QString,QString>();
02124 
02125   QMap< QString, QString > result;
02126   QStringList items = QStringList::split( '&', m_strQuery_encoded );
02127   for ( QStringList::const_iterator it = items.begin() ; it != items.end() ; ++it ) {
02128     int equal_pos = (*it).find( '=' );
02129     if ( equal_pos > 0 ) { // = is not the first char...
02130       QString name = (*it).left( equal_pos );
02131       if ( options & CaseInsensitiveKeys )
02132     name = name.lower();
02133       QString value = (*it).mid( equal_pos + 1 );
02134       if ( value.isEmpty() )
02135     result.insert( name, QString::fromLatin1("") );
02136       else {
02137     // ### why is decoding name not necessary?
02138     value.replace( '+', ' ' ); // + in queries means space
02139     result.insert( name, decode_string( value, encoding_hint ) );
02140       }
02141     } else if ( equal_pos < 0 ) { // no =
02142       QString name = (*it);
02143       if ( options & CaseInsensitiveKeys )
02144     name = name.lower();
02145       result.insert( name, QString::null );
02146     }
02147   }
02148 
02149   return result;
02150 }
02151 
02152 QString KURL::queryItem( const QString& _item ) const
02153 {
02154   return queryItem( _item, 0 );
02155 }
02156 
02157 QString KURL::queryItem( const QString& _item, int encoding_hint ) const
02158 {
02159   QString item = _item + '=';
02160   if ( m_strQuery_encoded.length() <= 1 )
02161     return QString::null;
02162 
02163   QStringList items = QStringList::split( '&', m_strQuery_encoded );
02164   unsigned int _len = item.length();
02165   for ( QStringList::ConstIterator it = items.begin(); it != items.end(); ++it )
02166   {
02167     if ( (*it).startsWith( item ) )
02168     {
02169       if ( (*it).length() > _len )
02170       {
02171         QString str = (*it).mid( _len );
02172         str.replace( '+', ' ' ); // + in queries means space.
02173         return decode_string( str, encoding_hint );
02174       }
02175       else // empty value
02176         return QString::fromLatin1("");
02177     }
02178   }
02179 
02180   return QString::null;
02181 }
02182 
02183 void KURL::removeQueryItem( const QString& _item )
02184 {
02185   QString item = _item + '=';
02186   if ( m_strQuery_encoded.length() <= 1 )
02187     return;
02188 
02189   QStringList items = QStringList::split( '&', m_strQuery_encoded );
02190   for ( QStringList::Iterator it = items.begin(); it != items.end(); )
02191   {
02192     if ( (*it).startsWith( item ) || (*it == _item) )
02193     {
02194       QStringList::Iterator deleteIt = it;
02195       ++it;
02196       items.remove(deleteIt);
02197     }
02198     else
02199     {
02200        ++it;
02201     }
02202   }
02203   m_strQuery_encoded = items.join( "&" );
02204 }
02205 
02206 void KURL::addQueryItem( const QString& _item, const QString& _value, int encoding_hint )
02207 {
02208   QString item = _item + '=';
02209   QString value = encode( _value, 0, encoding_hint );
02210 
02211   if (!m_strQuery_encoded.isEmpty())
02212      m_strQuery_encoded += '&';
02213   m_strQuery_encoded += item + value;
02214 }
02215 
02216 // static
02217 KURL KURL::fromPathOrURL( const QString& text )
02218 {
02219     if ( text.isEmpty() )
02220         return KURL();
02221 
02222     KURL url;
02223     if (!QDir::isRelativePath(text))
02224         url.setPath( text );
02225     else
02226         url = text;
02227 
02228     return url;
02229 }
02230 
02231 static QString _relativePath(const QString &base_dir, const QString &path, bool &isParent)
02232 {
02233    QString _base_dir(QDir::cleanDirPath(base_dir));
02234    QString _path(QDir::cleanDirPath(path.isEmpty() || (path[0] != '/') ? _base_dir+"/"+path : path));
02235 
02236    if (_base_dir.isEmpty())
02237       return _path;
02238 
02239    if (_base_dir[_base_dir.length()-1] != '/')
02240       _base_dir.append('/');
02241 
02242    QStringList list1 = QStringList::split('/', _base_dir);
02243    QStringList list2 = QStringList::split('/', _path);
02244 
02245    // Find where they meet
02246    uint level = 0;
02247    uint maxLevel = QMIN(list1.count(), list2.count());
02248    while((level < maxLevel) && (list1[level] == list2[level])) level++;
02249 
02250    QString result;
02251    // Need to go down out of the first path to the common branch.
02252    for(uint i = level; i < list1.count(); i++)
02253       result.append("../");
02254 
02255    // Now up up from the common branch to the second path.
02256    for(uint i = level; i < list2.count(); i++)
02257       result.append(list2[i]).append("/");
02258 
02259    if ((level < list2.count()) && (path[path.length()-1] != '/'))
02260       result.truncate(result.length()-1);
02261 
02262    isParent = (level == list1.count());
02263 
02264    return result;
02265 }
02266 
02267 QString KURL::relativePath(const QString &base_dir, const QString &path, bool *isParent)
02268 {
02269    bool parent = false;
02270    QString result = _relativePath(base_dir, path, parent);
02271    if (parent)
02272       result.prepend("./");
02273 
02274    if (isParent)
02275       *isParent = parent;
02276 
02277    return result;
02278 }
02279 
02280 
02281 QString KURL::relativeURL(const KURL &base_url, const KURL &url, int encoding_hint)
02282 {
02283    if ((url.protocol() != base_url.protocol()) ||
02284        (url.host() != base_url.host()) ||
02285        (url.port() && url.port() != base_url.port()) ||
02286        (url.hasUser() && url.user() != base_url.user()) ||
02287        (url.hasPass() && url.pass() != base_url.pass()))
02288    {
02289       return url.url(0, encoding_hint);
02290    }
02291 
02292    QString relURL;
02293 
02294    if ((base_url.path() != url.path()) || (base_url.query() != url.query()))
02295    {
02296       bool dummy;
02297       QString basePath = base_url.directory(false, false);
02298       relURL = encode( _relativePath(basePath, url.path(), dummy), 1, encoding_hint);
02299       relURL += url.query();
02300    }
02301 
02302    if ( url.hasRef() )
02303    {
02304       relURL += "#";
02305       relURL += url.ref();
02306    }
02307 
02308    if ( relURL.isEmpty() )
02309       return "./";
02310 
02311    return relURL;
02312 }
02313 
02314 int KURL::uriMode() const
02315 {
02316   return m_iUriMode;
02317 }
02318 
02319 KURL::URIMode KURL::uriModeForProtocol(const QString& protocol)
02320 {
02321 #ifndef KDE_QT_ONLY
02322     KURL::URIMode mode = Auto;
02323     if (protocol == fileProt)
02324         return URL;
02325     if (KGlobal::_instance)
02326         mode = KProtocolInfo::uriParseMode(protocol);
02327     if (mode == Auto ) {
02328 #else
02329         KURL::URIMode mode = Auto;
02330 #endif
02331     if ( protocol == "ed2k" || protocol == "sig2dat" || protocol == "slsk" || protocol == "data" ) mode = RawURI;
02332     else if ( protocol == "mailto" ) mode = Mailto;
02333     else mode = URL;
02334 #ifndef KDE_QT_ONLY
02335     }
02336 #endif
02337     return mode;
02338 }
KDE Logo
This file is part of the documentation for kdecore Library Version 3.4.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Feb 8 06:22:10 2006 by doxygen 1.4.4 written by Dimitri van Heesch, © 1997-2003