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