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