kdecore Library: kurl.cpp Source File (kdecore Library)

| # kdecore Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Namespaces | Source Files

# kurl.cpp

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

This file is part of the documentation for kdecore Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:18:12 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003