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     KURL tmp( url() + rUrl, encoding_hint);
00601     *this = tmp;
00602     cleanPath(false);
00603   }
00604   else
00605   {
00606     KURL tmp( rUrl, encoding_hint);
00607     *this = tmp;
00608     // Preserve userinfo if applicable.
00609     if (!_u.m_strUser.isEmpty() && m_strUser.isEmpty() && (_u.m_strHost == m_strHost) && (_u.m_strProtocol == m_strProtocol))
00610     {
00611        m_strUser = _u.m_strUser;
00612        m_strPass = _u.m_strPass;
00613     }
00614     cleanPath(false);
00615   }
00616 }
00617 
00618 void KURL::reset()
00619 {
00620   m_strProtocol = QString::null;
00621   m_strUser = QString::null;
00622   m_strPass = QString::null;
00623   m_strHost = QString::null;
00624   m_strPath = QString::null;
00625   m_strPath_encoded = QString::null;
00626   m_strQuery_encoded = QString::null;
00627   m_strRef_encoded = QString::null;
00628   m_bIsMalformed = true;
00629   m_iPort = 0;
00630   m_iUriMode = Auto;
00631 }
00632 
00633 bool KURL::isEmpty() const
00634 {
00635   return (m_strPath.isEmpty() && m_strProtocol.isEmpty());
00636 }
00637 
00638 void KURL::parse( const QString& _url, int encoding_hint )
00639 {
00640     if ( _url.isEmpty() || m_iUriMode == Invalid )
00641     {
00642     m_strProtocol = _url;
00643     m_iUriMode = Invalid;
00644     return;
00645     }
00646 
00647     const QChar* buf = _url.unicode();
00648     const QChar* orig = buf;
00649     uint len = _url.length();
00650     uint pos = 0;
00651 
00652     // Node 1: Accept alpha or slash
00653     QChar x = buf[pos++];
00654 #ifdef Q_WS_WIN
00655     /* win32: accept <letter>: or <letter>:/ or <letter>:\ */
00656     const bool alpha = isalpha((int)x);
00657     if (alpha && len<2)
00658         goto NodeErr;
00659     if (alpha && buf[pos]==':' && (len==2 || (len>2 && (buf[pos+1]=='/' || buf[pos+1]=='\\'))))
00660 #else
00661     if ( x == '/' )
00662 #endif
00663     {
00664     // A slash means we immediately proceed to parse it as a file URL.
00665     m_iUriMode = URL;
00666     m_strProtocol = fileProt;
00667     parseURL( _url, encoding_hint );
00668     return;
00669     }
00670     if ( !isalpha( (int)x ) )
00671     goto NodeErr;
00672 
00673     // Node 2: Accept any amount of (alpha|digit|'+'|'-')
00674     // '.' is not currently accepted, because current KURL may be confused.
00675     // Proceed with :// :/ or :
00676     while( pos < len && (isalpha((int)buf[pos]) || isdigit((int)buf[pos]) ||
00677              buf[pos] == '+' || buf[pos] == '-')) pos++;
00678 
00679     if (pos < len && buf[pos] == ':' )
00680     {
00681     m_strProtocol = QString( orig, pos ).lower();
00682     if ( m_iUriMode == Auto )
00683         m_iUriMode = uriModeForProtocol( m_strProtocol );
00684     // Proceed to correct parse function.
00685     switch ( m_iUriMode )
00686     {
00687     case RawURI:
00688         parseRawURI( _url );
00689         return;
00690     case Mailto:
00691         parseMailto( _url );
00692         return;
00693     case URL:
00694         parseURL( _url, encoding_hint );
00695         return;
00696     default:
00697         // Unknown URI mode results in an invalid URI.
00698         break;
00699     }
00700     }
00701 
00702 NodeErr:
00703     reset();
00704     m_strProtocol = _url;
00705     m_iUriMode = Invalid;
00706 }
00707 
00708 void KURL::parseRawURI( const QString& _url, int encoding_hint )
00709 {
00710     uint len = _url.length();
00711     const QChar* buf = _url.unicode();
00712 
00713     uint pos = 0;
00714 
00715     // Accept any amount of (alpha|digit|'+'|'-')
00716     // '.' is not currently accepted, because current KURL may be confused.
00717     // Proceed with :
00718     while( pos < len && (isalpha((int)buf[pos]) || isdigit((int)buf[pos]) ||
00719              buf[pos] == '+' || buf[pos] == '-')) pos++;
00720 
00721     // Note that m_strProtocol is already set here, so we just skip over the protocol.
00722     if (pos < len && buf[pos] == ':' )
00723     pos++;
00724     else { // can't happen, the caller checked all this already
00725     reset();
00726     m_strProtocol = _url;
00727     m_iUriMode = Invalid;
00728     return;
00729     }
00730 
00731     if ( pos == len ) // can't happen, the caller checked this already
00732     m_strPath = QString::null;
00733     else
00734     m_strPath = decode( QString( buf + pos, len - pos ), encoding_hint, true );
00735 
00736     m_bIsMalformed = false;
00737 
00738     return;
00739 }
00740 
00741 void KURL::parseMailto( const QString& _url, int encoding_hint )
00742 {
00743     parseURL( _url, encoding_hint);
00744     if ( m_bIsMalformed )
00745         return;
00746     QRegExp mailre("(.+@)(.+)");
00747     if ( mailre.exactMatch( m_strPath ) )
00748     {
00749 #ifndef KDE_QT_ONLY
00750     QString host = KIDNA::toUnicode( mailre.cap( 2 ) );
00751     if (host.isEmpty())
00752         host = mailre.cap( 2 ).lower();
00753 #else
00754     QString host = mailre.cap( 2 ).lower();
00755 #endif
00756     m_strPath = mailre.cap( 1 ) + host;
00757   }
00758 }
00759 
00760 void KURL::parseURL( const QString& _url, int encoding_hint )
00761 {
00762   QString port;
00763   bool badHostName = false;
00764   int start = 0;
00765   uint len = _url.length();
00766   const QChar* buf = _url.unicode();
00767 
00768   QChar delim;
00769   QString tmp;
00770 
00771   uint pos = 0;
00772 
00773   // Node 1: Accept alpha or slash
00774   QChar x = buf[pos++];
00775 #ifdef Q_WS_WIN
00776   /* win32: accept <letter>: or <letter>:/ or <letter>:\ */
00777   const bool alpha = isalpha((int)x);
00778   if (alpha && len<2)
00779     goto NodeErr;
00780   if (alpha && buf[pos]==':' && (len==2 || (len>2 && (buf[pos+1]=='/' || buf[pos+1]=='\\'))))
00781 #else
00782   if ( x == '/' )
00783 #endif
00784     goto Node9;
00785   if ( !isalpha( (int)x ) )
00786     goto NodeErr;
00787 
00788   // Node 2: Accept any amount of (alpha|digit|'+'|'-')
00789   // '.' is not currently accepted, because current KURL may be confused.
00790   // Proceed with :// :/ or :
00791   while( pos < len && (isalpha((int)buf[pos]) || isdigit((int)buf[pos]) ||
00792           buf[pos] == '+' || buf[pos] == '-')) pos++;
00793 
00794   // Note that m_strProtocol is already set here, so we just skip over the protocol.
00795   if ( pos+2 < len && buf[pos] == ':' && buf[pos+1] == '/' && buf[pos+2] == '/' )
00796     {
00797       pos += 3;
00798     }
00799   else if (pos+1 < len && buf[pos] == ':' ) // Need to always compare length()-1 otherwise KURL passes "http:" as legal!!
00800     {
00801       pos++;
00802       start = pos;
00803       goto Node9;
00804     }
00805   else
00806     goto NodeErr;
00807 
00808   //Node 3: We need at least one character here
00809   if ( pos == len )
00810       goto NodeErr;
00811   start = pos;
00812 
00813   // Node 4: Accept any amount of characters.
00814   if (buf[pos] == '[')     // An IPv6 host follows.
00815       goto Node8;
00816   // Terminate on / or @ or ? or # or " or ; or <
00817   x = buf[pos];
00818   while( (x != ':') && (x != '@') && (x != '/') && (x != '?') && (x != '#') )
00819   {
00820      if ((x == '\"') || (x == ';') || (x == '<'))
00821         badHostName = true;
00822      if (++pos == len)
00823         break;
00824      x = buf[pos];
00825   }
00826   if ( pos == len )
00827     {
00828       if (badHostName)
00829          goto NodeErr;
00830 
00831       setHost(decode(QString( buf + start, pos - start ), encoding_hint));
00832       goto NodeOk;
00833     }
00834   if ( x == '@' )
00835     {
00836       m_strUser = decode(QString( buf + start, pos - start ), encoding_hint);
00837       pos++;
00838       goto Node7;
00839     }
00840   else if ( (x == '/') || (x == '?') || (x == '#'))
00841     {
00842       if (badHostName)
00843          goto NodeErr;
00844 
00845       setHost(decode(QString( buf + start, pos - start ), encoding_hint));
00846       start = pos;
00847       goto Node9;
00848     }
00849   else if ( x != ':' )
00850     goto NodeErr;
00851   m_strUser = decode(QString( buf + start, pos - start ), encoding_hint);
00852   pos++;
00853 
00854   // Node 5: We need at least one character
00855   if ( pos == len )
00856     goto NodeErr;
00857   start = pos++;
00858 
00859   // Node 6: Read everything until @, /, ? or #
00860   while( (pos < len) &&
00861         (buf[pos] != '@') &&
00862         (buf[pos] != '/') &&
00863         (buf[pos] != '?') &&
00864         (buf[pos] != '#')) pos++;
00865   // If we now have a '@' the ':' seperates user and password.
00866   // Otherwise it seperates host and port.
00867   if ( (pos == len) || (buf[pos] != '@') )
00868     {
00869       // Ok the : was used to separate host and port
00870       if (badHostName)
00871          goto NodeErr;
00872       setHost(m_strUser);
00873       m_strUser = QString::null;
00874       QString tmp( buf + start, pos - start );
00875       char *endptr;
00876       m_iPort = (unsigned short int)strtol(tmp.ascii(), &endptr, 10);
00877       if ((pos == len) && (strlen(endptr) == 0))
00878         goto NodeOk;
00879       // there is more after the digits
00880       pos -= strlen(endptr);
00881       if ((buf[pos] != '@') &&
00882           (buf[pos] != '/') &&
00883           (buf[pos] != '?') &&
00884           (buf[pos] != '#'))
00885         goto NodeErr;
00886 
00887       start = pos;
00888       goto Node9;
00889     }
00890   m_strPass = decode(QString( buf + start, pos - start), encoding_hint);
00891   pos++;
00892 
00893   // Node 7: We need at least one character
00894  Node7:
00895   if ( pos == len )
00896     goto NodeErr;
00897 
00898  Node8:
00899   if (buf[pos] == '[')
00900   {
00901     // IPv6 address
00902     start = ++pos; // Skip '['
00903 
00904     if (pos == len)
00905     {
00906        badHostName = true;
00907        goto NodeErr;
00908     }
00909     // Node 8a: Read everything until ] or terminate
00910     badHostName = false;
00911     x = buf[pos];
00912     while( (x != ']') )
00913     {
00914        if ((x == '\"') || (x == ';') || (x == '<'))
00915           badHostName = true;
00916        if (++pos == len)
00917        {
00918           badHostName = true;
00919           break;
00920        }
00921        x = buf[pos];
00922     }
00923     if (badHostName)
00924        goto NodeErr;
00925     setHost(decode(QString( buf + start, pos - start ), encoding_hint));
00926     if (pos < len) pos++; // Skip ']'
00927     if (pos == len)
00928        goto NodeOk;
00929   }
00930   else
00931   {
00932     // Non IPv6 address, with a user
00933     start = pos;
00934 
00935     // Node 8b: Read everything until / : or terminate
00936     badHostName = false;
00937     x = buf[pos];
00938     while( (x != ':') && (x != '@') && (x != '/') && (x != '?') && (x != '#') )
00939     {
00940        if ((x == '\"') || (x == ';') || (x == '<'))
00941           badHostName = true;
00942        if (++pos == len)
00943           break;
00944        x = buf[pos];
00945     }
00946     if (badHostName)
00947        goto NodeErr;
00948     if ( pos == len )
00949     {
00950        setHost(decode(QString( buf + start, pos - start ), encoding_hint));
00951        goto NodeOk;
00952     }
00953     setHost(decode(QString( buf + start, pos - start ), encoding_hint));
00954   }
00955   x = buf[pos];
00956   if ( x == '/' || x == '#' || x == '?' )
00957     {
00958       start = pos;
00959       goto Node9;
00960     }
00961   else if ( x != ':' )
00962     goto NodeErr;
00963   pos++;
00964 
00965   // Node 8c: Accept at least one digit
00966   if ( pos == len )
00967     goto NodeErr;
00968   start = pos;
00969   if ( !isdigit( buf[pos++] ) )
00970     goto NodeErr;
00971 
00972   // Node 8d: Accept any amount of digits
00973   while( pos < len && isdigit( buf[pos] ) ) pos++;
00974   port = QString( buf + start, pos - start );
00975   m_iPort = port.toUShort();
00976   if ( pos == len )
00977     goto NodeOk;
00978   start = pos;
00979 
00980  Node9: // parse path until query or reference reached
00981 
00982   while( pos < len && buf[pos] != '#' && buf[pos]!='?' ) pos++;
00983 
00984   tmp = QString( buf + start, pos - start );
00985   //kdDebug(126)<<" setting encoded path to:"<<tmp<<endl;
00986   setEncodedPath( tmp, encoding_hint );
00987 
00988   if ( pos == len )
00989       goto NodeOk;
00990 
00991  //Node10: // parse query or reference depending on what comes first
00992   delim = (buf[pos++]=='#'?'?':'#');
00993 
00994   start = pos;
00995 
00996   while(pos < len && buf[pos]!=delim ) pos++;
00997 
00998   tmp = QString(buf + start, pos - start);
00999   if (delim=='#')
01000       _setQuery(tmp, encoding_hint);
01001   else
01002       m_strRef_encoded = tmp;
01003 
01004   if (pos == len)
01005       goto NodeOk;
01006 
01007  //Node11: // feed the rest into the remaining variable
01008   tmp = QString( buf + pos + 1, len - pos - 1);
01009   if (delim == '#')
01010       m_strRef_encoded = tmp;
01011   else
01012       _setQuery(tmp, encoding_hint);
01013 
01014  NodeOk:
01015   //kdDebug(126)<<"parsing finished. m_strProtocol="<<m_strProtocol<<" m_strHost="<<m_strHost<<" m_strPath="<<m_strPath<<endl;
01016   m_bIsMalformed = false; // Valid URL
01017 
01018   //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;
01019   if (m_strProtocol.isEmpty())
01020   {
01021     m_iUriMode = URL;
01022     m_strProtocol = fileProt;
01023   }
01024   return;
01025 
01026  NodeErr:
01027 //  kdDebug(126) << "KURL couldn't parse URL \"" << _url << "\"" << endl;
01028   reset();
01029   m_strProtocol = _url;
01030   m_iUriMode = Invalid;
01031 }
01032 
01033 KURL& KURL::operator=( const QString& _url )
01034 {
01035   reset();
01036   parse( _url );
01037 
01038   return *this;
01039 }
01040 
01041 KURL& KURL::operator=( const char * _url )
01042 {
01043   reset();
01044   parse( QString::fromLatin1(_url) );
01045 
01046   return *this;
01047 }
01048 
01049 #ifndef QT_NO_NETWORKPROTOCOL
01050 KURL& KURL::operator=( const QUrl & u )
01051 {
01052   m_strProtocol = u.protocol();
01053   m_iUriMode = Auto;
01054   m_strUser = u.user();
01055   m_strPass = u.password();
01056   m_strHost = u.host();
01057   m_strPath = u.path( false );
01058   m_strPath_encoded = QString::null;
01059   m_strQuery_encoded = u.query();
01060   m_strRef_encoded = u.ref();
01061   m_bIsMalformed = !u.isValid();
01062   m_iPort = u.port();
01063 
01064   return *this;
01065 }
01066 #endif
01067 
01068 KURL& KURL::operator=( const KURL& _u )
01069 {
01070   m_strProtocol = _u.m_strProtocol;
01071   m_strUser = _u.m_strUser;
01072   m_strPass = _u.m_strPass;
01073   m_strHost = _u.m_strHost;
01074   m_strPath = _u.m_strPath;
01075   m_strPath_encoded = _u.m_strPath_encoded;
01076   m_strQuery_encoded = _u.m_strQuery_encoded;
01077   m_strRef_encoded = _u.m_strRef_encoded;
01078   m_bIsMalformed = _u.m_bIsMalformed;
01079   m_iPort = _u.m_iPort;
01080   m_iUriMode = _u.m_iUriMode;
01081 
01082   return *this;
01083 }
01084 
01085 bool KURL::operator<( const KURL& _u) const
01086 {
01087   int i;
01088   if (!_u.isValid())
01089   {
01090      if (!isValid())
01091      {
01092         i = m_strProtocol.compare(_u.m_strProtocol);
01093         return (i < 0);
01094      }
01095      return false;
01096   }
01097   if (!isValid())
01098      return true;
01099 
01100   i = m_strProtocol.compare(_u.m_strProtocol);
01101   if (i) return (i < 0);
01102 
01103   i = m_strHost.compare(_u.m_strHost);
01104   if (i) return (i < 0);
01105 
01106   if (m_iPort != _u.m_iPort) return (m_iPort < _u.m_iPort);
01107 
01108   i = m_strPath.compare(_u.m_strPath);
01109   if (i) return (i < 0);
01110 
01111   i = m_strQuery_encoded.compare(_u.m_strQuery_encoded);
01112   if (i) return (i < 0);
01113 
01114   i = m_strRef_encoded.compare(_u.m_strRef_encoded);
01115   if (i) return (i < 0);
01116 
01117   i = m_strUser.compare(_u.m_strUser);
01118   if (i) return (i < 0);
01119 
01120   i = m_strPass.compare(_u.m_strPass);
01121   if (i) return (i < 0);
01122 
01123   return false;
01124 }
01125 
01126 bool KURL::operator==( const KURL& _u ) const
01127 {
01128   if ( !isValid() || !_u.isValid() )
01129     return false;
01130 
01131   if ( m_strProtocol == _u.m_strProtocol &&
01132        m_strUser == _u.m_strUser &&
01133        m_strPass == _u.m_strPass &&
01134        m_strHost == _u.m_strHost &&
01135        m_strPath == _u.m_strPath &&
01136        // The encoded path may be null, but the URLs are still equal (David)
01137        ( m_strPath_encoded.isNull() || _u.m_strPath_encoded.isNull() ||
01138          m_strPath_encoded == _u.m_strPath_encoded ) &&
01139        m_strQuery_encoded == _u.m_strQuery_encoded &&
01140        m_strRef_encoded == _u.m_strRef_encoded &&
01141        m_iPort == _u.m_iPort )
01142   {
01143     return true;
01144   }
01145 
01146   return false;
01147 }
01148 
01149 bool KURL::operator==( const QString& _u ) const
01150 {
01151   KURL u( _u );
01152   return ( *this == u );
01153 }
01154 
01155 bool KURL::cmp( const KURL &u, bool ignore_trailing ) const
01156 {
01157   return equals( u, ignore_trailing );
01158 }
01159 
01160 bool KURL::equals( const KURL &_u, bool ignore_trailing ) const
01161 {
01162   if ( !isValid() || !_u.isValid() )
01163     return false;
01164 
01165   if ( ignore_trailing )
01166   {
01167     QString path1 = path(1);
01168     QString path2 = _u.path(1);
01169     if ( path1 != path2 )
01170       return false;
01171 
01172     if ( m_strProtocol == _u.m_strProtocol &&
01173          m_strUser == _u.m_strUser &&
01174          m_strPass == _u.m_strPass &&
01175          m_strHost == _u.m_strHost &&
01176          m_strQuery_encoded == _u.m_strQuery_encoded &&
01177          m_strRef_encoded == _u.m_strRef_encoded &&
01178          m_iPort == _u.m_iPort )
01179       return true;
01180 
01181     return false;
01182   }
01183 
01184   return ( *this == _u );
01185 }
01186 
01187 bool KURL::isParentOf( const KURL& _u ) const
01188 {
01189   if ( !isValid() || !_u.isValid() )
01190     return false;
01191 
01192   if ( m_strProtocol == _u.m_strProtocol &&
01193        m_strUser == _u.m_strUser &&
01194        m_strPass == _u.m_strPass &&
01195        m_strHost == _u.m_strHost &&
01196        m_strQuery_encoded == _u.m_strQuery_encoded &&
01197        m_strRef_encoded == _u.m_strRef_encoded &&
01198        m_iPort == _u.m_iPort )
01199   {
01200     if ( path().isEmpty() || _u.path().isEmpty() )
01201         return false; // can't work with implicit paths
01202 
01203     QString p1( cleanpath( path(), true, false ) );
01204     if ( p1[p1.length()-1] != '/' )
01205         p1 += '/';
01206     QString p2( cleanpath( _u.path(), true, false ) );
01207     if ( p2[p2.length()-1] != '/' )
01208         p2 += '/';
01209 
01210     //kdDebug(126) << "p1=" << p1 << endl;
01211     //kdDebug(126) << "p2=" << p2 << endl;
01212     //kdDebug(126) << "p1.length()=" << p1.length() << endl;
01213     //kdDebug(126) << "p2.left(!$)=" << p2.left( p1.length() ) << endl;
01214     return p2.startsWith( p1 );
01215   }
01216   return false;
01217 }
01218 
01219 void KURL::setFileName( const QString& _txt )
01220 {
01221   m_strRef_encoded = QString::null;
01222   int i = 0;
01223   while( _txt[i] == '/' ) ++i;
01224   QString tmp;
01225   if ( i )
01226     tmp = _txt.mid( i );
01227   else
01228     tmp = _txt;
01229 
01230   QString path = m_strPath_encoded.isEmpty() ? m_strPath : m_strPath_encoded;
01231   if ( path.isEmpty() )
01232     path = "/";
01233   else
01234   {
01235     int lastSlash = path.findRev( '/' );
01236     if ( lastSlash == -1)
01237     {
01238       // The first character is not a '/' ???
01239       // This looks strange ...
01240       path = "/";
01241     }
01242     else if ( path.right(1) != "/" )
01243       path.truncate( lastSlash+1 ); // keep the "/"
01244   }
01245   if (m_strPath_encoded.isEmpty())
01246   {
01247      path += tmp;
01248      setPath( path );
01249   }
01250   else
01251   {
01252      path += encode_string(tmp);
01253      setEncodedPath( path );
01254   }
01255   cleanPath();
01256 }
01257 
01258 void KURL::cleanPath( bool cleanDirSeparator ) // taken from the old KURL
01259 {
01260   if (m_iUriMode != URL) return;
01261   m_strPath = cleanpath(m_strPath, cleanDirSeparator, false);
01262   // WABA: Is this safe when "/../" is encoded with %?
01263   m_strPath_encoded = cleanpath(m_strPath_encoded, cleanDirSeparator, true);
01264 }
01265 
01266 static QString trailingSlash( int _trailing, const QString &path )
01267 {
01268   QString result = path;
01269 
01270   if ( _trailing == 0 )
01271     return result;
01272   else if ( _trailing == 1 )
01273   {
01274     int len = result.length();
01275     if ( (len == 0) || (result[ len - 1 ] != '/') )
01276       result += "/";
01277     return result;
01278   }
01279   else if ( _trailing == -1 )
01280   {
01281     if ( result == "/" )
01282       return result;
01283     int len = result.length();
01284     while (len > 1 && result[ len - 1 ] == '/')
01285     {
01286       len--;
01287     }
01288     result.truncate( len );
01289     return result;
01290   }
01291   else {
01292     assert( 0 );
01293     return QString::null;
01294   }
01295 }
01296 
01297 void KURL::adjustPath( int _trailing )
01298 {
01299   if (!m_strPath_encoded.isEmpty())
01300   {
01301      m_strPath_encoded = trailingSlash( _trailing, m_strPath_encoded );
01302   }
01303   m_strPath = trailingSlash( _trailing, m_strPath );
01304 }
01305 
01306 
01307 QString KURL::encodedPathAndQuery( int _trailing, bool _no_empty_path, int encoding_hint ) const
01308 {
01309   QString tmp;
01310   if (!m_strPath_encoded.isEmpty() && encoding_hint == 0)
01311   {
01312      tmp = trailingSlash( _trailing, m_strPath_encoded );
01313   }
01314   else
01315   {
01316      tmp = path( _trailing );
01317      if ( _no_empty_path && tmp.isEmpty() )
01318         tmp = "/";
01319      if (m_iUriMode == Mailto)
01320      {
01321         tmp = encode( tmp, 2, encoding_hint );
01322      }
01323      else
01324      {
01325         tmp = encode( tmp, 1, encoding_hint );
01326      }
01327   }
01328 
01329   // TODO apply encoding_hint to the query
01330   if (!m_strQuery_encoded.isNull())
01331       tmp += '?' + m_strQuery_encoded;
01332   return tmp;
01333 }
01334 
01335 void KURL::setEncodedPath( const QString& _txt, int encoding_hint )
01336 {
01337   m_strPath_encoded = _txt;
01338 
01339   decode( m_strPath_encoded, m_strPath, m_strPath_encoded, encoding_hint );
01340   // Throw away encoding for local files, makes file-operations faster.
01341   if (m_strProtocol == fileProt)
01342      m_strPath_encoded = QString::null;
01343 
01344   if ( m_iUriMode == Auto )
01345     m_iUriMode = URL;
01346 }
01347 
01348 
01349 void KURL::setEncodedPathAndQuery( const QString& _txt, int encoding_hint )
01350 {
01351   int pos = _txt.find( '?' );
01352   if ( pos == -1 )
01353   {
01354     setEncodedPath(_txt, encoding_hint);
01355     m_strQuery_encoded = QString::null;
01356   }
01357   else
01358   {
01359     setEncodedPath(_txt.left( pos ), encoding_hint);
01360     _setQuery(_txt.right(_txt.length() - pos - 1), encoding_hint);
01361   }
01362 }
01363 
01364 QString KURL::path( int _trailing ) const
01365 {
01366   return trailingSlash( _trailing, path() );
01367 }
01368 
01369 bool KURL::isLocalFile() const
01370 {
01371   if ( (m_strProtocol != fileProt ) || hasSubURL() )
01372      return false;
01373 
01374   if (m_strHost.isEmpty() || (m_strHost == "localhost"))
01375      return true;
01376 
01377   char hostname[ 256 ];
01378   hostname[ 0 ] = '\0';
01379   if (!gethostname( hostname, 255 ))
01380      hostname[sizeof(hostname)-1] = '\0';
01381 
01382   for(char *p = hostname; *p; p++)
01383      *p = tolower(*p);
01384 
01385   return (m_strHost == hostname);
01386 }
01387 
01388 void KURL::setFileEncoding(const QString &encoding)
01389 {
01390   if (!isLocalFile())
01391      return;
01392 
01393   QString q = query();
01394 
01395   if (!q.isEmpty() && (q[0] == '?'))
01396      q = q.mid(1);
01397 
01398   QStringList args = QStringList::split('&', q);
01399   for(QStringList::Iterator it = args.begin();
01400       it != args.end();)
01401   {
01402       QString s = decode_string(*it);
01403       if (s.startsWith("charset="))
01404          it = args.erase(it);
01405       else
01406          ++it;
01407   }
01408   if (!encoding.isEmpty())
01409      args.append("charset="+encode_string(encoding));
01410 
01411   if (args.isEmpty())
01412      _setQuery(QString::null);
01413   else
01414      _setQuery(args.join("&"));
01415 }
01416 
01417 QString KURL::fileEncoding() const
01418 {
01419   if (!isLocalFile())
01420      return QString::null;
01421 
01422   QString q = query();
01423 
01424   if (q.isEmpty())
01425      return QString::null;
01426 
01427   if (q[0] == '?')
01428      q = q.mid(1);
01429 
01430   QStringList args = QStringList::split('&', q);
01431   for(QStringList::ConstIterator it = args.begin();
01432       it != args.end();
01433       ++it)
01434   {
01435       QString s = decode_string(*it);
01436       if (s.startsWith("charset="))
01437          return s.mid(8);
01438   }
01439   return QString::null;
01440 }
01441 
01442 bool KURL::hasSubURL() const
01443 {
01444   if ( m_strProtocol.isEmpty() || m_bIsMalformed )
01445     return false;
01446   if (m_strRef_encoded.isEmpty())
01447      return false;
01448   if (m_strRef_encoded.startsWith("gzip:"))
01449      return true;
01450   if (m_strRef_encoded.startsWith("bzip:"))
01451      return true;
01452   if (m_strRef_encoded.startsWith("bzip2:"))
01453      return true;
01454   if (m_strRef_encoded.startsWith("tar:"))
01455      return true;
01456   if (m_strRef_encoded.startsWith("ar:"))
01457      return true;
01458   if (m_strRef_encoded.startsWith("zip:"))
01459      return true;
01460   if ( m_strProtocol == "error" ) // anything that starts with error: has suburls
01461      return true;
01462   return false;
01463 }
01464 
01465 QString KURL::url( int _trailing, int encoding_hint ) const
01466 {
01467   if( m_bIsMalformed )
01468   {
01469     // Return the whole url even when the url is
01470     // malformed.  Under such conditions the url
01471     // is stored in m_strProtocol.
01472     return m_strProtocol;
01473   }
01474 
01475   QString u = m_strProtocol;
01476   if (!u.isEmpty())
01477     u += ":";
01478 
01479   if ( hasHost() || (m_strProtocol == fileProt) )
01480   {
01481     u += "//";
01482     if ( hasUser() )
01483     {
01484       u += encode(m_strUser, 0, encoding_hint);
01485       if ( hasPass() )
01486       {
01487         u += ":";
01488         u += encode(m_strPass, 0, encoding_hint);
01489       }
01490       u += "@";
01491     }
01492     if ( m_iUriMode == URL )
01493     {
01494       bool IPv6 = (m_strHost.find(':') != -1);
01495       if (IPv6)
01496         u += '[' + m_strHost + ']';
01497       else
01498         u += encodeHost(m_strHost, true, encoding_hint);
01499       if ( m_iPort != 0 ) {
01500         QString buffer;
01501         buffer.sprintf( ":%u", m_iPort );
01502         u += buffer;
01503       }
01504     }
01505     else
01506     {
01507       u += m_strHost;
01508     }
01509   }
01510 
01511   if ( m_iUriMode == URL || m_iUriMode == Mailto )
01512     u += encodedPathAndQuery( _trailing, false, encoding_hint );
01513   else
01514     u += encode( m_strPath, 21, encoding_hint, true );
01515 
01516   if ( hasRef() )
01517   {
01518     u += "#";
01519     u += m_strRef_encoded;
01520   }
01521 
01522   return u;
01523 }
01524 
01525 QString KURL::prettyURL( int _trailing ) const
01526 {
01527   if( m_bIsMalformed )
01528   {
01529     // Return the whole url even when the url is
01530     // malformed.  Under such conditions the url
01531     // is stored in m_strProtocol.
01532     return m_strProtocol;
01533   }
01534 
01535   QString u = m_strProtocol;
01536   if (!u.isEmpty())
01537      u += ":";
01538 
01539   if ( hasHost() || (m_strProtocol == fileProt) )
01540   {
01541     u += "//";
01542     if ( hasUser() )
01543     {
01544       QString s = m_strUser;
01545 #ifndef KDE_QT_ONLY
01546       // shorten the username, its unlikely to be valid without password anyway
01547       if (!hasPass())
01548           s = KStringHandler::csqueeze(s, 16);
01549 #endif
01550       u += encode(s, 0, 0);
01551       // Don't show password!
01552       u += "@";
01553     }
01554     if ( m_iUriMode == URL )
01555     {
01556     bool IPv6 = (m_strHost.find(':') != -1);
01557     if (IPv6)
01558     {
01559        u += '[' + m_strHost + ']';
01560     }
01561     else
01562     {
01563        u += lazy_encode(m_strHost);
01564     }
01565     }
01566     else
01567     {
01568       u += lazy_encode(m_strHost);
01569     }
01570     if ( m_iPort != 0 ) {
01571       QString buffer;
01572       buffer.sprintf( ":%u", m_iPort );
01573       u += buffer;
01574     }
01575   }
01576 
01577   if (m_iUriMode == Mailto)
01578   {
01579      u += lazy_encode( m_strPath, false );
01580   }
01581   else
01582   {
01583      u += trailingSlash( _trailing, lazy_encode( m_strPath ) );
01584   }
01585 
01586   if (!m_strQuery_encoded.isNull())
01587       u += '?' + m_strQuery_encoded;
01588 
01589   if ( hasRef() )
01590   {
01591     u += "#";
01592     u += m_strRef_encoded;
01593   }
01594 
01595   return u;
01596 }
01597 
01598 QString KURL::prettyURL( int _trailing, AdjustementFlags _flags) const
01599 {
01600   QString u = prettyURL(_trailing);
01601   if (_flags & StripFileProtocol && u.startsWith("file://")) {
01602     u.remove(0, 7);
01603 #ifdef Q_WS_WIN
01604     return QDir::convertSeparators(u);
01605 #endif
01606   }
01607   return u;
01608 }
01609 
01610 QString KURL::pathOrURL() const
01611 {
01612   if ( isLocalFile() && m_strRef_encoded.isNull() && m_strQuery_encoded.isNull() ) {
01613     return path();
01614   } else {
01615     return prettyURL();
01616   }
01617 }
01618 
01619 QString KURL::htmlURL() const
01620 {
01621   return QStyleSheet::escape(prettyURL());
01622 }
01623 
01624 KURL::List KURL::split( const KURL& _url )
01625 {
01626   QString ref;
01627   KURL::List lst;
01628   KURL url = _url;
01629 
01630   while(true)
01631   {
01632      KURL u = url;
01633      u.m_strRef_encoded = QString::null;
01634      lst.append(u);
01635      if (url.hasSubURL())
01636      {
01637         url = KURL(url.m_strRef_encoded);
01638      }
01639      else
01640      {
01641         ref = url.m_strRef_encoded;
01642         break;
01643      }
01644   }
01645 
01646   // Set HTML ref in all URLs.
01647   KURL::List::Iterator it;
01648   for( it = lst.begin() ; it != lst.end(); ++it )
01649   {
01650      (*it).m_strRef_encoded = ref;
01651   }
01652 
01653   return lst;
01654 }
01655 
01656 KURL::List KURL::split( const QString& _url )
01657 {
01658   return split(KURL(_url));
01659 }
01660 
01661 KURL KURL::join( const KURL::List & lst )
01662 {
01663   if (lst.isEmpty()) return KURL();
01664   KURL tmp;
01665 
01666   KURL::List::ConstIterator first = lst.fromLast();
01667   for( KURL::List::ConstIterator it = first; it != lst.end(); --it )
01668   {
01669      KURL u(*it);
01670      if (it != first)
01671      {
01672         if (!u.m_strRef_encoded) u.m_strRef_encoded = tmp.url();
01673         else u.m_strRef_encoded += "#" + tmp.url(); // Support more than one suburl thingy
01674      }
01675      tmp = u;
01676   }
01677 
01678   return tmp;
01679 }
01680 
01681 QString KURL::fileName( bool _strip_trailing_slash ) const
01682 {
01683   QString fname;
01684   if (hasSubURL()) { // If we have a suburl, then return the filename from there
01685     KURL::List list = KURL::split(*this);
01686     KURL::List::Iterator it = list.fromLast();
01687     return (*it).fileName(_strip_trailing_slash);
01688   }
01689   const QString &path = m_strPath;
01690 
01691   int len = path.length();
01692   if ( len == 0 )
01693     return fname;
01694 
01695   if ( _strip_trailing_slash )
01696   {
01697     while ( len >= 1 && path[ len - 1 ] == '/' )
01698       len--;
01699   }
01700   else if ( path[ len - 1 ] == '/' )
01701     return fname;
01702 
01703   // Does the path only consist of '/' characters ?
01704   if ( len == 1 && path[ 0 ] == '/' )
01705     return fname;
01706 
01707   // Skip last n slashes
01708   int n = 1;
01709   if (!m_strPath_encoded.isEmpty())
01710   {
01711      // This is hairy, we need the last unencoded slash.
01712      // Count in the encoded string how many encoded slashes follow the last
01713      // unencoded one.
01714      int i = m_strPath_encoded.findRev( '/', len - 1 );
01715      QString fileName_encoded = m_strPath_encoded.mid(i+1);
01716      n += fileName_encoded.contains("%2f", false);
01717   }
01718   int i = len;
01719   do {
01720     i = path.findRev( '/', i - 1 );
01721   }
01722   while (--n && (i > 0));
01723 
01724   // If ( i == -1 ) => the first character is not a '/'
01725   // So it's some URL like file:blah.tgz, return the whole path
01726   if ( i == -1 ) {
01727     if ( len == (int)path.length() )
01728       fname = path;
01729     else
01730       // Might get here if _strip_trailing_slash is true
01731       fname = path.left( len );
01732   }
01733   else
01734   {
01735      fname = path.mid( i + 1, len - i - 1 ); // TO CHECK
01736   }
01737   return fname;
01738 }
01739 
01740 void KURL::addPath( const QString& _txt )
01741 {
01742   if (hasSubURL())
01743   {
01744      KURL::List lst = split( *this );
01745      KURL &u = lst.last();
01746      u.addPath(_txt);
01747      *this = join( lst );
01748      return;
01749   }
01750 
01751   m_strPath_encoded = QString::null;
01752 
01753   if ( _txt.isEmpty() )
01754     return;
01755 
01756   int i = 0;
01757   int len = m_strPath.length();
01758   // Add the trailing '/' if it is missing
01759   if ( _txt[0] != '/' && ( len == 0 || m_strPath[ len - 1 ] != '/' ) )
01760     m_strPath += "/";
01761 
01762   // No double '/' characters
01763   i = 0;
01764   if ( len != 0 && m_strPath[ len - 1 ] == '/' )
01765   {
01766     while( _txt[i] == '/' )
01767       ++i;
01768   }
01769 
01770   m_strPath += _txt.mid( i );
01771 }
01772 
01773 QString KURL::directory( bool _strip_trailing_slash_from_result,
01774                          bool _ignore_trailing_slash_in_path ) const
01775 {
01776   QString result = m_strPath_encoded.isEmpty() ? m_strPath : m_strPath_encoded;
01777   if ( _ignore_trailing_slash_in_path )
01778     result = trailingSlash( -1, result );
01779 
01780   if ( result.isEmpty() || result == "/" )
01781     return result;
01782 
01783   int i = result.findRev( "/" );
01784   // If ( i == -1 ) => the first character is not a '/'
01785   // So it's some URL like file:blah.tgz, with no path
01786   if ( i == -1 )
01787     return QString::null;
01788 
01789   if ( i == 0 )
01790   {
01791     result = "/";
01792     return result;
01793   }
01794 
01795   if ( _strip_trailing_slash_from_result )
01796     result = result.left( i );
01797   else
01798     result = result.left( i + 1 );
01799 
01800   if (!m_strPath_encoded.isEmpty())
01801     result = decode(result);
01802 
01803   return result;
01804 }
01805 
01806 
01807 bool KURL::cd( const QString& _dir )
01808 {
01809   if ( _dir.isEmpty() || m_bIsMalformed )
01810     return false;
01811 
01812   if (hasSubURL())
01813   {
01814      KURL::List lst = split( *this );
01815      KURL &u = lst.last();
01816      u.cd(_dir);
01817      *this = join( lst );
01818      return true;
01819   }
01820 
01821   // absolute path ?
01822   if ( _dir[0] == '/' )
01823   {
01824     m_strPath_encoded = QString::null;
01825     m_strPath = _dir;
01826     setHTMLRef( QString::null );
01827     m_strQuery_encoded = QString::null;
01828     return true;
01829   }
01830 
01831   // Users home directory on the local disk ?
01832   if ( ( _dir[0] == '~' ) && ( m_strProtocol == fileProt ))
01833   {
01834     m_strPath_encoded = QString::null;
01835     m_strPath = QDir::homeDirPath();
01836     m_strPath += "/";
01837     m_strPath += _dir.right(m_strPath.length() - 1);
01838     setHTMLRef( QString::null );
01839     m_strQuery_encoded = QString::null;
01840     return true;
01841   }
01842 
01843   // relative path
01844   // we always work on the past of the first url.
01845   // Sub URLs are not touched.
01846 
01847   // append '/' if necessary
01848   QString p = path(1);
01849   p += _dir;
01850   p = cleanpath( p, true, false );
01851   setPath( p );
01852 
01853   setHTMLRef( QString::null );
01854   m_strQuery_encoded = QString::null;
01855 
01856   return true;
01857 }
01858 
01859 KURL KURL::upURL( ) const
01860 {
01861   if (!query().isEmpty())
01862   {
01863      KURL u(*this);
01864      u._setQuery(QString::null);
01865      return u;
01866   };
01867 
01868   if (!hasSubURL())
01869   {
01870      KURL u(*this);
01871 
01872      u.cd("../");
01873 
01874      return u;
01875   }
01876 
01877   // We have a subURL.
01878   KURL::List lst = split( *this );
01879   if (lst.isEmpty())
01880       return KURL(); // Huh?
01881   while (true)
01882   {
01883      KURL &u = lst.last();
01884      QString old = u.path();
01885      u.cd("../");
01886      if (u.path() != old)
01887          break; // Finshed.
01888      if (lst.count() == 1)
01889          break; // Finished.
01890      lst.remove(lst.fromLast());
01891   }
01892   return join( lst );
01893 }
01894 
01895 QString KURL::htmlRef() const
01896 {
01897   if ( !hasSubURL() )
01898   {
01899     return decode( ref() );
01900   }
01901 
01902   List lst = split( *this );
01903   return decode( (*lst.begin()).ref() );
01904 }
01905 
01906 QString KURL::encodedHtmlRef() const
01907 {
01908   if ( !hasSubURL() )
01909   {
01910     return ref();
01911   }
01912 
01913   List lst = split( *this );
01914   return (*lst.begin()).ref();
01915 }
01916 
01917 void KURL::setHTMLRef( const QString& _ref )
01918 {
01919   if ( !hasSubURL() )
01920   {
01921     m_strRef_encoded = encode( _ref, 0, 0 /*?*/);
01922     return;
01923   }
01924 
01925   List lst = split( *this );
01926 
01927   (*lst.begin()).setRef( encode( _ref, 0, 0 /*?*/) );
01928 
01929   *this = join( lst );
01930 }
01931 
01932 bool KURL::hasHTMLRef() const
01933 {
01934   if ( !hasSubURL() )
01935   {
01936     return hasRef();
01937   }
01938 
01939   List lst = split( *this );
01940   return (*lst.begin()).hasRef();
01941 }
01942 
01943 void
01944 KURL::setProtocol( const QString& _txt )
01945 {
01946    m_strProtocol = _txt;
01947    if ( m_iUriMode == Auto ) m_iUriMode = uriModeForProtocol( m_strProtocol );
01948    m_bIsMalformed = false;
01949 }
01950 
01951 void
01952 KURL::setUser( const QString& _txt )
01953 {
01954    if ( _txt.isEmpty() )
01955      m_strUser = QString::null;
01956    else
01957      m_strUser = _txt;
01958 }
01959 
01960 void
01961 KURL::setPass( const QString& _txt )
01962 {
01963    if ( _txt.isEmpty() )
01964      m_strPass = QString::null;
01965    else
01966      m_strPass = _txt;
01967 }
01968 
01969 void
01970 KURL::setHost( const QString& _txt )
01971 {
01972   if ( m_iUriMode == Auto )
01973     m_iUriMode = URL;
01974   switch ( m_iUriMode )
01975   {
01976   case URL:
01977 #ifndef KDE_QT_ONLY
01978    m_strHost = KIDNA::toUnicode(_txt);
01979    if (m_strHost.isEmpty())
01980       m_strHost = _txt.lower(); // Probably an invalid hostname, but...
01981 #else
01982    m_strHost = _txt.lower();
01983 #endif
01984     break;
01985   default:
01986     m_strHost = _txt;
01987     break;
01988   }
01989 }
01990 
01991 void
01992 KURL::setPort( unsigned short int _p )
01993 {
01994    m_iPort = _p;
01995 }
01996 
01997 void KURL::setPath( const QString & path )
01998 {
01999   if (isEmpty())
02000     m_bIsMalformed = false;
02001   if (m_strProtocol.isEmpty())
02002   {
02003     m_strProtocol = fileProt;
02004   }
02005   m_strPath = path;
02006   m_strPath_encoded = QString::null;
02007   if ( m_iUriMode == Auto )
02008     m_iUriMode = URL;
02009 }
02010 
02011 void KURL::setDirectory( const QString &dir)
02012 {
02013   if ( dir.endsWith("/"))
02014      setPath(dir);
02015   else
02016      setPath(dir+"/");
02017 }
02018 
02019 void KURL::setQuery( const QString &_txt, int encoding_hint)
02020 {
02021    if (_txt[0] == '?')
02022       _setQuery( _txt.length() > 1 ? _txt.mid(1) : "" /*empty, not null*/, encoding_hint );
02023    else
02024       _setQuery( _txt, encoding_hint );
02025 }
02026 
02027 // This is a private function that expects a query without '?'
02028 void KURL::_setQuery( const QString &_txt, int encoding_hint)
02029 {
02030    m_strQuery_encoded = _txt;
02031    if (!_txt.length())
02032       return;
02033 
02034    int l = m_strQuery_encoded.length();
02035    int i = 0;
02036    QString result;
02037    while (i < l)
02038    {
02039       int s = i;
02040       // Re-encode. Break encoded string up according to the reserved
02041       // characters '&:;=/?' and re-encode part by part.
02042       while(i < l)
02043       {
02044          char c = m_strQuery_encoded[i].latin1();
02045          if ((c == '&') || (c == ':') || (c == ';') ||
02046              (c == '=') || (c == '/') || (c == '?'))
02047             break;
02048          i++;
02049       }
02050       if (i > s)
02051       {
02052          QString tmp = m_strQuery_encoded.mid(s, i-s);
02053          QString newTmp;
02054          decode( tmp, newTmp, tmp, encoding_hint, false );
02055          result += tmp;
02056       }
02057       if (i < l)
02058       {
02059          result += m_strQuery_encoded[i];
02060          i++;
02061       }
02062    }
02063    m_strQuery_encoded = result;
02064 }
02065 
02066 QString KURL::query() const
02067 {
02068     if (m_strQuery_encoded.isNull())
02069         return QString::null;
02070     return '?'+m_strQuery_encoded;
02071 }
02072 
02073 QString KURL::decode_string(const QString &str, int encoding_hint)
02074 {
02075    return decode(str, encoding_hint);
02076 }
02077 
02078 QString KURL::encode_string(const QString &str, int encoding_hint)
02079 {
02080    return encode(str, 1, encoding_hint);
02081 }
02082 
02083 QString KURL::encode_string_no_slash(const QString &str, int encoding_hint)
02084 {
02085    return encode(str, 0, encoding_hint);
02086 }
02087 
02088 bool urlcmp( const QString& _url1, const QString& _url2 )
02089 {
02090   // Both empty ?
02091   if ( _url1.isEmpty() && _url2.isEmpty() )
02092     return true;
02093   // Only one empty ?
02094   if ( _url1.isEmpty() || _url2.isEmpty() )
02095     return false;
02096 
02097   KURL::List list1 = KURL::split( _url1 );
02098   KURL::List list2 = KURL::split( _url2 );
02099 
02100   // Malformed ?
02101   if ( list1.isEmpty() || list2.isEmpty() )
02102     return false;
02103 
02104   return ( list1 == list2 );
02105 }
02106 
02107 bool urlcmp( const QString& _url1, const QString& _url2, bool _ignore_trailing, bool _ignore_ref )
02108 {
02109   // Both empty ?
02110   if ( _url1.isEmpty() && _url2.isEmpty() )
02111     return true;
02112   // Only one empty ?
02113   if ( _url1.isEmpty() || _url2.isEmpty() )
02114     return false;
02115 
02116   KURL::List list1 = KURL::split( _url1 );
02117   KURL::List list2 = KURL::split( _url2 );
02118 
02119   // Malformed ?
02120   if ( list1.isEmpty() || list2.isEmpty() )
02121     return false;
02122 
02123   unsigned int size = list1.count();
02124   if ( list2.count() != size )
02125     return false;
02126 
02127   if ( _ignore_ref )
02128   {
02129     (*list1.begin()).setRef(QString::null);
02130     (*list2.begin()).setRef(QString::null);
02131   }
02132 
02133   KURL::List::Iterator it1 = list1.begin();
02134   KURL::List::Iterator it2 = list2.begin();
02135   for( ; it1 != list1.end() ; ++it1, ++it2 )
02136     if ( !(*it1).equals( *it2, _ignore_trailing ) )
02137       return false;
02138 
02139   return true;
02140 }
02141 
02142 QMap< QString, QString > KURL::queryItems( int options ) const {
02143   return queryItems(options, 0);
02144 }
02145 
02146 QMap< QString, QString > KURL::queryItems( int options, int encoding_hint ) const {
02147   if ( m_strQuery_encoded.isEmpty() )
02148     return QMap<QString,QString>();
02149 
02150   QMap< QString, QString > result;
02151   QStringList items = QStringList::split( '&', m_strQuery_encoded );
02152   for ( QStringList::const_iterator it = items.begin() ; it != items.end() ; ++it ) {
02153     int equal_pos = (*it).find( '=' );
02154     if ( equal_pos > 0 ) { // = is not the first char...
02155       QString name = (*it).left( equal_pos );
02156       if ( options & CaseInsensitiveKeys )
02157     name = name.lower();
02158       QString value = (*it).mid( equal_pos + 1 );
02159       if ( value.isEmpty() )
02160     result.insert( name, QString::fromLatin1("") );
02161       else {
02162     // ### why is decoding name not necessary?
02163     value.replace( '+', ' ' ); // + in queries means space
02164     result.insert( name, decode_string( value, encoding_hint ) );
02165       }
02166     } else if ( equal_pos < 0 ) { // no =
02167       QString name = (*it);
02168       if ( options & CaseInsensitiveKeys )
02169     name = name.lower();
02170       result.insert( name, QString::null );
02171     }
02172   }
02173 
02174   return result;
02175 }
02176 
02177 QString KURL::queryItem( const QString& _item ) const
02178 {
02179   return queryItem( _item, 0 );
02180 }
02181 
02182 QString KURL::queryItem( const QString& _item, int encoding_hint ) const
02183 {
02184   QString item = _item + '=';
02185   if ( m_strQuery_encoded.length() <= 1 )
02186     return QString::null;
02187 
02188   QStringList items = QStringList::split( '&', m_strQuery_encoded );
02189   unsigned int _len = item.length();
02190   for ( QStringList::ConstIterator it = items.begin(); it != items.end(); ++it )
02191   {
02192     if ( (*it).startsWith( item ) )
02193     {
02194       if ( (*it).length() > _len )
02195       {
02196         QString str = (*it).mid( _len );
02197         str.replace( '+', ' ' ); // + in queries means space.
02198         return decode_string( str, encoding_hint );
02199       }
02200       else // empty value
02201         return QString::fromLatin1("");
02202     }
02203   }
02204 
02205   return QString::null;
02206 }
02207 
02208 void KURL::removeQueryItem( const QString& _item )
02209 {
02210   QString item = _item + '=';
02211   if ( m_strQuery_encoded.length() <= 1 )
02212     return;
02213 
02214   QStringList items = QStringList::split( '&', m_strQuery_encoded );
02215   for ( QStringList::Iterator it = items.begin(); it != items.end(); )
02216   {
02217     if ( (*it).startsWith( item ) || (*it == _item) )
02218     {
02219       QStringList::Iterator deleteIt = it;
02220       ++it;
02221       items.remove(deleteIt);
02222     }
02223     else
02224     {
02225        ++it;
02226     }
02227   }
02228   m_strQuery_encoded = items.join( "&" );
02229 }
02230 
02231 void KURL::addQueryItem( const QString& _item, const QString& _value, int encoding_hint )
02232 {
02233   QString item = _item + '=';
02234   QString value = encode( _value, 0, encoding_hint );
02235 
02236   if (!m_strQuery_encoded.isEmpty())
02237      m_strQuery_encoded += '&';
02238   m_strQuery_encoded += item + value;
02239 }
02240 
02241 // static
02242 KURL KURL::fromPathOrURL( const QString& text )
02243 {
02244     if ( text.isEmpty() )
02245         return KURL();
02246 
02247     KURL url;
02248     if (!QDir::isRelativePath(text))
02249         url.setPath( text );
02250     else
02251         url = text;
02252 
02253     return url;
02254 }
02255 
02256 static QString _relativePath(const QString &base_dir, const QString &path, bool &isParent)
02257 {
02258    QString _base_dir(QDir::cleanDirPath(base_dir));
02259    QString _path(QDir::cleanDirPath(path.isEmpty() || (path[0] != '/') ? _base_dir+"/"+path : path));
02260 
02261    if (_base_dir.isEmpty())
02262       return _path;
02263 
02264    if (_base_dir[_base_dir.length()-1] != '/')
02265       _base_dir.append('/');
02266 
02267    QStringList list1 = QStringList::split('/', _base_dir);
02268    QStringList list2 = QStringList::split('/', _path);
02269 
02270    // Find where they meet
02271    uint level = 0;
02272    uint maxLevel = QMIN(list1.count(), list2.count());
02273    while((level < maxLevel) && (list1[level] == list2[level])) level++;
02274 
02275    QString result;
02276    // Need to go down out of the first path to the common branch.
02277    for(uint i = level; i < list1.count(); i++)
02278       result.append("../");
02279 
02280    // Now up up from the common branch to the second path.
02281    for(uint i = level; i < list2.count(); i++)
02282       result.append(list2[i]).append("/");
02283 
02284    if ((level < list2.count()) && (path[path.length()-1] != '/'))
02285       result.truncate(result.length()-1);
02286 
02287    isParent = (level == list1.count());
02288 
02289    return result;
02290 }
02291 
02292 QString KURL::relativePath(const QString &base_dir, const QString &path, bool *isParent)
02293 {
02294    bool parent = false;
02295    QString result = _relativePath(base_dir, path, parent);
02296    if (parent)
02297       result.prepend("./");
02298 
02299    if (isParent)
02300       *isParent = parent;
02301 
02302    return result;
02303 }
02304 
02305 
02306 QString KURL::relativeURL(const KURL &base_url, const KURL &url, int encoding_hint)
02307 {
02308    if ((url.protocol() != base_url.protocol()) ||
02309        (url.host() != base_url.host()) ||
02310        (url.port() && url.port() != base_url.port()) ||
02311        (url.hasUser() && url.user() != base_url.user()) ||
02312        (url.hasPass() && url.pass() != base_url.pass()))
02313    {
02314       return url.url(0, encoding_hint);
02315    }
02316 
02317    QString relURL;
02318 
02319    if ((base_url.path() != url.path()) || (base_url.query() != url.query()))
02320    {
02321       bool dummy;
02322       QString basePath = base_url.directory(false, false);
02323       relURL = encode( _relativePath(basePath, url.path(), dummy), 1, encoding_hint);
02324       relURL += url.query();
02325    }
02326 
02327    if ( url.hasRef() )
02328    {
02329       relURL += "#";
02330       relURL += url.ref();
02331    }
02332 
02333    if ( relURL.isEmpty() )
02334       return "./";
02335 
02336    return relURL;
02337 }
02338 
02339 int KURL::uriMode() const
02340 {
02341   return m_iUriMode;
02342 }
02343 
02344 KURL::URIMode KURL::uriModeForProtocol(const QString& protocol)
02345 {
02346 #ifndef KDE_QT_ONLY
02347     KURL::URIMode mode = Auto;
02348     if (protocol == fileProt)
02349         return URL;
02350     if (KGlobal::_instance)
02351         mode = KProtocolInfo::uriParseMode(protocol);
02352     if (mode == Auto ) {
02353 #else
02354         KURL::URIMode mode = Auto;
02355 #endif
02356     if ( protocol == "ed2k" || protocol == "sig2dat" || protocol == "slsk" || protocol == "data" ) mode = RawURI;
02357     else if ( protocol == "mailto" ) mode = Mailto;
02358     else mode = URL;
02359 #ifndef KDE_QT_ONLY
02360     }
02361 #endif
02362     return mode;
02363 }
KDE Home | KDE Accessibility Home | Description of Access Keys