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 #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 }
KDE Logo
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