# kdecore Library API Documentation

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

This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Jan 28 12:46:59 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001