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