kdecore Library API Documentation

kurl.cpp

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