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