File indexing completed on 2024-10-06 09:36:01
0001 /* 0002 Copyright (C) 1999 Torben Weis <weis@kde.org> 0003 Copyright (C) 2005-2006 David Faure <faure@kde.org> 0004 0005 This library is free software; you can redistribute it and/or 0006 modify it under the terms of the GNU Library General Public 0007 License as published by the Free Software Foundation; either 0008 version 2 of the License, or (at your option) any later version. 0009 0010 This library is distributed in the hope that it will be useful, 0011 but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0013 Library General Public License for more details. 0014 0015 You should have received a copy of the GNU Library General Public License 0016 along with this library; see the file COPYING.LIB. If not, write to 0017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0018 Boston, MA 02110-1301, USA. 0019 */ 0020 0021 /// KDE4 TODO: maybe we should use QUrl::resolved() 0022 0023 /* 0024 * The currently active RFC for URL/URIs is RFC3986 0025 * Previous (and now deprecated) RFCs are RFC1738 and RFC2396 0026 */ 0027 0028 #include "kurl.h" 0029 #include "kurlmimedata.h" 0030 0031 #include <stdio.h> 0032 #include <assert.h> 0033 #include <ctype.h> 0034 #include <stdlib.h> 0035 #include <unistd.h> 0036 0037 #include <QDebug> 0038 #include <QDir> 0039 #include <QStringList> 0040 #include <QRegExp> 0041 #include <QMimeData> 0042 #include <QTextCodec> 0043 0044 #ifdef DEBUG_KURL 0045 static int kurlDebugArea() 0046 { 0047 static int s_area = KDebug::registerArea("kdecore (KUrl)"); 0048 return s_area; 0049 } 0050 #endif 0051 0052 static QString cleanpath(const QString &_path, bool cleanDirSeparator, bool decodeDots) 0053 { 0054 if (_path.isEmpty()) { 0055 return QString(); 0056 } 0057 0058 if (QFileInfo(_path).isRelative()) { 0059 return _path; // Don't mangle mailto-style URLs 0060 } 0061 0062 QString path = _path; 0063 0064 int len = path.length(); 0065 0066 if (decodeDots) { 0067 static const QLatin1String encodedDot("%2e"); 0068 if (path.indexOf(encodedDot, 0, Qt::CaseInsensitive) != -1) { 0069 static const QLatin1String encodedDOT("%2E"); // Uppercase! 0070 path.replace(encodedDot, QString(QLatin1Char('.'))); 0071 path.replace(encodedDOT, QString(QLatin1Char('.'))); 0072 len = path.length(); 0073 } 0074 } 0075 0076 const bool slash = (len && path[len - 1] == QLatin1Char('/')) || 0077 (len > 1 && path[len - 2] == QLatin1Char('/') && path[len - 1] == QLatin1Char('.')); 0078 0079 // The following code cleans up directory path much like 0080 // QDir::cleanPath() except it can be made to ignore multiple 0081 // directory separators by setting the flag to false. That fixes 0082 // bug# 15044, mail.altavista.com and other similar brain-dead server 0083 // implementations that do not follow what has been specified in 0084 // RFC 2396!! (dA) 0085 QString result; 0086 int cdUp, orig_pos, pos; 0087 0088 cdUp = 0; 0089 pos = orig_pos = len; 0090 while (pos && (pos = path.lastIndexOf(QLatin1Char('/'), --pos)) != -1) { 0091 len = orig_pos - pos - 1; 0092 if (len == 2 && path[pos + 1] == QLatin1Char('.') && path[pos + 2] == QLatin1Char('.')) { 0093 cdUp++; 0094 } else { 0095 // Ignore any occurrences of '.' 0096 // This includes entries that simply do not make sense like /..../ 0097 if ((len || !cleanDirSeparator) && 0098 (len != 1 || path[pos + 1] != QLatin1Char('.'))) { 0099 if (!cdUp) { 0100 result.prepend(path.mid(pos, len + 1)); 0101 } else { 0102 cdUp--; 0103 } 0104 } 0105 } 0106 orig_pos = pos; 0107 } 0108 0109 #ifdef Q_OS_WIN // prepend drive letter if exists (js) 0110 if (orig_pos >= 2 && path[0].isLetter() && path[1] == QLatin1Char(':')) { 0111 result.prepend(QString(path[0]) + QLatin1Char(':')); 0112 } 0113 #endif 0114 0115 if (result.isEmpty()) { 0116 result = QLatin1Char('/'); 0117 } else if (slash && result[result.length() - 1] != QLatin1Char('/')) { 0118 result.append(QLatin1Char('/')); 0119 } 0120 0121 return result; 0122 } 0123 0124 #ifdef Q_OS_WIN 0125 0126 // returns true if provided arguments desinate letter+colon or double slash 0127 #define IS_DRIVE_OR_DOUBLESLASH(isletter, char1, char2, colon, slash) \ 0128 ((isletter && char2 == colon) || (char1 == slash && char2 == slash)) 0129 0130 // Removes file:/// or file:// or file:/ or / prefix assuming that str 0131 // is (nonempty) Windows absolute path with a drive letter or double slash. 0132 // If there was file protocol, the path is decoded from percent encoding 0133 static QString removeSlashOrFilePrefix(const QString &str) 0134 { 0135 // FIXME this should maybe be replaced with some (faster?)/nicer logic 0136 const int len = str.length(); 0137 if (str[0] == QLatin1Char('f')) { 0138 if (len > 10 && str.startsWith(QLatin1String("file:///")) 0139 && IS_DRIVE_OR_DOUBLESLASH(str[8].isLetter(), str[8], str[9], QLatin1Char(':'), QLatin1Char('/'))) { 0140 return QUrl::fromPercentEncoding(str.toLatin1()).mid(8); 0141 } else if (len > 9 && str.startsWith(QLatin1String("file://")) 0142 && IS_DRIVE_OR_DOUBLESLASH(str[7].isLetter(), str[7], str[8], QLatin1Char(':'), QLatin1Char('/'))) { 0143 return QUrl::fromPercentEncoding(str.toLatin1()).mid(7); 0144 } else if (len > 8 && str.startsWith(QLatin1String("file:/")) 0145 && IS_DRIVE_OR_DOUBLESLASH(str[6].isLetter(), str[6], str[7], QLatin1Char(':'), QLatin1Char('/'))) { 0146 return QUrl::fromPercentEncoding(str.toLatin1()).mid(6); 0147 } 0148 } 0149 /* No 'else' here since there can be "f:/" path. */ 0150 0151 /* '/' + drive letter or // */ 0152 if (len > 2 && str[0] == QLatin1Char('/') 0153 && IS_DRIVE_OR_DOUBLESLASH(str[1].isLetter(), str[1], str[2], QLatin1Char(':'), QLatin1Char('/'))) { 0154 return str.mid(1); 0155 } 0156 /* drive letter or // */ 0157 else if (len >= 2 && IS_DRIVE_OR_DOUBLESLASH(str[0].isLetter(), str[0], str[1], QLatin1Char(':'), QLatin1Char('/'))) { 0158 return str; 0159 } 0160 return QString(); 0161 } 0162 #endif 0163 0164 bool KUrl::isRelativeUrl(const QString &_url) 0165 { 0166 int len = _url.length(); 0167 if (!len) { 0168 return true; // Very short relative URL. 0169 } 0170 const QChar *str = _url.unicode(); 0171 0172 // Absolute URL must start with alpha-character 0173 if (!isalpha(str[0].toLatin1())) { 0174 return true; // Relative URL 0175 } 0176 0177 for (int i = 1; i < len; i++) { 0178 char c = str[i].toLatin1(); // Note: non-latin1 chars return 0! 0179 if (c == ':') { 0180 return false; // Absolute URL 0181 } 0182 0183 // Protocol part may only contain alpha, digit, + or - 0184 if (!isalpha(c) && !isdigit(c) && (c != '+') && (c != '-')) { 0185 return true; // Relative URL 0186 } 0187 } 0188 // URL did not contain ':' 0189 return true; // Relative URL 0190 } 0191 0192 KUrl::List::List(const KUrl &url) 0193 { 0194 append(url); 0195 } 0196 0197 KUrl::List::List(const QList<KUrl> &list) 0198 : QList<KUrl>(list) 0199 { 0200 } 0201 0202 KUrl::List::List(const QList<QUrl> &list) 0203 { 0204 Q_FOREACH (const QUrl &url, list) { 0205 append(KUrl(url)); 0206 } 0207 } 0208 0209 KUrl::List::List(const QStringList &list) 0210 { 0211 for (QStringList::ConstIterator it = list.begin(); 0212 it != list.end(); 0213 ++it) { 0214 append(KUrl(*it)); 0215 } 0216 } 0217 0218 QStringList KUrl::List::toStringList() const 0219 { 0220 return toStringList(KUrl::LeaveTrailingSlash); 0221 } 0222 0223 QStringList KUrl::List::toStringList(KUrl::AdjustPathOption trailing) const 0224 { 0225 QStringList lst; 0226 for (KUrl::List::ConstIterator it = constBegin(); 0227 it != constEnd(); ++it) { 0228 lst.append(it->url(trailing)); 0229 } 0230 return lst; 0231 } 0232 0233 static void populateMimeDataHelper(const KUrl::List &urls, 0234 QMimeData *mimeData, 0235 const KUrl::MetaDataMap &metaData, 0236 KUrl::MimeDataFlags flags) 0237 { 0238 const QString oldText = mimeData->text(); 0239 mimeData->setUrls(urls); // set text/uri-list and text/plain 0240 0241 if ((flags & KUrl::NoTextExport) == 0) { 0242 mimeData->setText(oldText); 0243 } 0244 0245 if (!metaData.isEmpty()) { 0246 KUrlMimeData::setMetaData(metaData, mimeData); 0247 } 0248 } 0249 0250 void KUrl::List::populateMimeData(QMimeData *mimeData, 0251 const KUrl::MetaDataMap &metaData, 0252 MimeDataFlags flags) const 0253 { 0254 populateMimeDataHelper(*this, mimeData, metaData, flags); 0255 } 0256 0257 void KUrl::List::populateMimeData(const KUrl::List &mostLocalUrls, 0258 QMimeData *mimeData, 0259 const KUrl::MetaDataMap &metaData, 0260 MimeDataFlags flags) const 0261 { 0262 const QString oldText = mimeData->text(); 0263 KUrlMimeData::setUrls(*this, mostLocalUrls, mimeData); 0264 0265 if ((flags & KUrl::NoTextExport) == 0) { 0266 mimeData->setText(oldText); 0267 } 0268 0269 if (!metaData.isEmpty()) { 0270 KUrlMimeData::setMetaData(metaData, mimeData); 0271 } 0272 } 0273 0274 bool KUrl::List::canDecode(const QMimeData *mimeData) 0275 { 0276 return mimeData->hasUrls(); 0277 } 0278 0279 QStringList KUrl::List::mimeDataTypes() 0280 { 0281 return KUrlMimeData::mimeDataTypes(); 0282 } 0283 0284 KUrl::List KUrl::List::fromMimeData(const QMimeData *mimeData, 0285 DecodeOptions decodeOptions, 0286 KUrl::MetaDataMap *metaData) 0287 { 0288 KUrlMimeData::DecodeOptions options = KUrlMimeData::PreferKdeUrls; 0289 if (decodeOptions == PreferLocalUrls) { 0290 options = KUrlMimeData::PreferLocalUrls; 0291 } 0292 return KUrlMimeData::urlsFromMimeData(mimeData, options, metaData); 0293 } 0294 0295 KUrl::List::operator QVariant() const 0296 { 0297 return QVariant::fromValue(*this); 0298 } 0299 0300 KUrl::List::operator QList<QUrl>() const 0301 { 0302 QList<QUrl> list; 0303 Q_FOREACH (const KUrl &url, *this) { 0304 list << url; 0305 } 0306 return list; 0307 } 0308 0309 /// 0310 0311 KUrl::KUrl() 0312 : QUrl() 0313 { 0314 } 0315 0316 KUrl::~KUrl() 0317 { 0318 } 0319 0320 KUrl::KUrl(const QString &str) 0321 : QUrl() 0322 { 0323 if (!str.isEmpty()) { 0324 #ifdef Q_OS_WIN 0325 #ifdef DEBUG_KURL 0326 qDebug() << "KUrl::KUrl ( const QString &str = " << str.toLatin1().data() << " )"; 0327 #endif 0328 QString pathToSet; 0329 // when it starts with file:// it's a url and must be valid. we don't care if the 0330 // path exist/ is valid or not 0331 if (!str.startsWith(QLatin1String("file://"))) { 0332 pathToSet = removeSlashOrFilePrefix(QDir::fromNativeSeparators(str)); 0333 } 0334 if (!pathToSet.isEmpty()) { 0335 // we have a prefix indicating this is a local URL 0336 // remember the possible query using _setEncodedUrl(), then set up the correct path without query protocol part 0337 int index = pathToSet.lastIndexOf(QLatin1Char('?')); 0338 if (index == -1) { 0339 setPath(pathToSet); 0340 } else { 0341 setPath(pathToSet.left(index)); 0342 _setQuery(pathToSet.mid(index + 1)); 0343 } 0344 return; 0345 } 0346 #endif 0347 if (str[0] == QLatin1Char('/') || str[0] == QLatin1Char('~')) { 0348 setPath(str); 0349 } else { 0350 _setEncodedUrl(str.toUtf8()); 0351 } 0352 } 0353 } 0354 0355 KUrl::KUrl(const char *str) 0356 : QUrl() 0357 { 0358 #ifdef Q_OS_WIN 0359 // true if @a c is letter 0360 #define IS_LETTER(c) \ 0361 ((c >= QLatin1Char('A') && c <= QLatin1Char('Z')) || (c >= QLatin1Char('a') && c <= QLatin1Char('z'))) 0362 0363 // like IS_DRIVE_OR_DOUBLESLASH, but slash is prepended 0364 #define IS_SLASH_AND_DRIVE_OR_DOUBLESLASH_0 \ 0365 ( QLatin1Char(str[0]) == QLatin1Char('/') && IS_DRIVE_OR_DOUBLESLASH(IS_LETTER(QLatin1Char(str[1])), QLatin1Char(str[1]), QLatin1Char(str[2]), QLatin1Char(':'), QLatin1Char('/')) ) 0366 0367 // like IS_DRIVE_OR_DOUBLESLASH, with characters == str[0] and str[1] 0368 #define IS_DRIVE_OR_DOUBLESLASH_0 \ 0369 ( IS_DRIVE_OR_DOUBLESLASH(IS_LETTER(QLatin1Char(str[0])), QLatin1Char(str[0]), QLatin1Char(str[1]), QLatin1Char(':'), QLatin1Char('/')) ) 0370 0371 #if defined(DEBUG_KURL) 0372 qDebug() << "KUrl::KUrl " << " " << str; 0373 #endif 0374 if (str && str[0] && str[1] && str[2]) { 0375 if (IS_SLASH_AND_DRIVE_OR_DOUBLESLASH_0) { 0376 setPath(QString::fromUtf8(str + 1)); 0377 } else if (IS_DRIVE_OR_DOUBLESLASH_0) { 0378 setPath(QString::fromUtf8(str)); 0379 } 0380 } 0381 #endif 0382 if (str && str[0]) { 0383 if (str[0] == '/' || str[0] == '~') { 0384 setPath(QString::fromUtf8(str)); 0385 } else { 0386 _setEncodedUrl(str); 0387 } 0388 } 0389 } 0390 0391 KUrl::KUrl(const QByteArray &str) 0392 : QUrl() 0393 { 0394 if (!str.isEmpty()) { 0395 #ifdef Q_OS_WIN 0396 #ifdef DEBUG_KURL 0397 qDebug() << "KUrl::KUrl " << " " << str.data(); 0398 #endif 0399 if (IS_SLASH_AND_DRIVE_OR_DOUBLESLASH_0) { 0400 setPath(QString::fromUtf8(str.mid(1))); 0401 } else if (IS_DRIVE_OR_DOUBLESLASH_0) { 0402 setPath(QString::fromUtf8(str)); 0403 } 0404 #else 0405 if (str[0] == '/' || str[0] == '~') { 0406 setPath(QString::fromUtf8(str.data())); 0407 } 0408 #endif 0409 else { 0410 _setEncodedUrl(str); 0411 } 0412 } 0413 } 0414 0415 KUrl::KUrl(const KUrl &_u) 0416 : QUrl(_u) 0417 { 0418 #if defined(Q_OS_WIN) && defined(DEBUG_KURL) 0419 qDebug() << "KUrl::KUrl(KUrl) " << " path " << _u.path() << " toLocalFile " << _u.toLocalFile(); 0420 #endif 0421 } 0422 0423 KUrl::KUrl(const QUrl &u) 0424 : QUrl(u) 0425 { 0426 #if defined(Q_OS_WIN) && defined(DEBUG_KURL) 0427 qDebug() << "KUrl::KUrl(Qurl) " << " path " << u.path() << " toLocalFile " << u.toLocalFile(); 0428 #endif 0429 } 0430 0431 KUrl::KUrl(const KUrl &_u, const QString &_rel_url) 0432 : QUrl() 0433 { 0434 #if defined(Q_OS_WIN) && defined(DEBUG_KURL) 0435 qDebug() << "KUrl::KUrl(KUrl,QString rel_url) " << " path " << _u.path() << " toLocalFile " << _u.toLocalFile(); 0436 #endif 0437 #if 0 0438 if (_u.hasSubUrl()) { // Operate on the last suburl, not the first 0439 KUrl::List lst = split(_u); 0440 KUrl u(lst.last(), _rel_url); 0441 lst.erase(--lst.end()); 0442 lst.append(u); 0443 *this = join(lst); 0444 return; 0445 } 0446 #endif 0447 QString rUrl = _rel_url; 0448 0449 // WORKAROUND THE RFC 1606 LOOPHOLE THAT ALLOWS 0450 // http:/index.html AS A VALID SYNTAX FOR RELATIVE 0451 // URLS. ( RFC 2396 section 5.2 item # 3 ) 0452 const int len = _u.scheme().length(); 0453 if (!_u.host().isEmpty() && !rUrl.isEmpty() && 0454 rUrl.indexOf(_u.scheme(), 0, Qt::CaseInsensitive) == 0 && 0455 rUrl[len] == QLatin1Char(':') && (rUrl[len + 1] != QLatin1Char('/') || 0456 (rUrl[len + 1] == QLatin1Char('/') && rUrl[len + 2] != QLatin1Char('/')))) { 0457 rUrl.remove(0, rUrl.indexOf(QLatin1Char(':')) + 1); 0458 } 0459 0460 if (rUrl.isEmpty()) { 0461 *this = _u; 0462 } else if (rUrl[0] == QLatin1Char('#')) { 0463 *this = _u; 0464 QByteArray strRef_encoded = rUrl.mid(1).toLatin1(); 0465 if (strRef_encoded.isNull()) { 0466 setFragment(QString::fromLatin1("")); // we know there was an (empty) html ref, we saw the '#' 0467 } else { 0468 setFragment(QUrl::fromPercentEncoding(strRef_encoded)); 0469 } 0470 } else if (isRelativeUrl(rUrl)) { 0471 *this = _u; 0472 setFragment(QString()); 0473 setEncodedQuery(QByteArray()); 0474 QString strPath = path(); 0475 if (rUrl[0] == QLatin1Char('/')) { 0476 if ((rUrl.length() > 1) && (rUrl[1] == QLatin1Char('/'))) { 0477 setHost(QString()); 0478 setPort(-1); 0479 // File protocol returns file:/// without host, strip // from rUrl 0480 if (_u.isLocalFile()) { 0481 rUrl.remove(0, 2); 0482 } 0483 } 0484 strPath.clear(); 0485 } else if (rUrl[0] != QLatin1Char('?')) { 0486 const int pos = strPath.lastIndexOf(QLatin1Char('/')); 0487 if (pos >= 0) { 0488 strPath.truncate(pos); 0489 } 0490 strPath += QLatin1Char('/'); 0491 } else { 0492 if (strPath.isEmpty()) { 0493 strPath = QLatin1Char('/'); 0494 } 0495 } 0496 setPath(strPath); 0497 //kDebug(kurlDebugArea()) << "url()=" << url() << " rUrl=" << rUrl; 0498 const KUrl tmp(url() + rUrl); 0499 //kDebug(kurlDebugArea()) << "assigning tmp=" << tmp.url(); 0500 *this = tmp; 0501 cleanPath(KeepDirSeparators); 0502 } else { 0503 const KUrl tmp(rUrl); 0504 //kDebug(kurlDebugArea()) << "not relative; assigning tmp=" << tmp.url(); 0505 *this = tmp; 0506 // Preserve userinfo if applicable. 0507 if (!_u.userInfo().isEmpty() && userInfo().isEmpty() 0508 && (_u.host() == host()) && (_u.scheme() == scheme())) { 0509 setUserInfo(_u.userInfo()); 0510 } 0511 cleanPath(KeepDirSeparators); 0512 } 0513 } 0514 0515 KUrl &KUrl::operator=(const KUrl &_u) 0516 { 0517 QUrl::operator=(_u); 0518 return *this; 0519 } 0520 0521 bool KUrl::operator==(const KUrl &_u) const 0522 { 0523 return QUrl::operator==(_u); 0524 } 0525 0526 bool KUrl::operator==(const QString &_u) const 0527 { 0528 KUrl u(_u); 0529 return (*this == u); 0530 } 0531 0532 KUrl::operator QVariant() const 0533 { 0534 return QVariant::fromValue(*this); 0535 } 0536 0537 #ifndef KDELIBS4SUPPORT_NO_DEPRECATED 0538 bool KUrl::cmp(const KUrl &u, bool ignore_trailing) const 0539 { 0540 return equals(u, ignore_trailing ? CompareWithoutTrailingSlash : EqualsOptions()); 0541 } 0542 #endif 0543 0544 bool KUrl::equals(const KUrl &_u, const EqualsOptions &options) const 0545 { 0546 if (!isValid() || !_u.isValid()) { 0547 return false; 0548 } 0549 0550 if (options & CompareWithoutTrailingSlash || options & CompareWithoutFragment) { 0551 QString path1 = path((options & CompareWithoutTrailingSlash) ? RemoveTrailingSlash : LeaveTrailingSlash); 0552 QString path2 = _u.path((options & CompareWithoutTrailingSlash) ? RemoveTrailingSlash : LeaveTrailingSlash); 0553 0554 if (options & AllowEmptyPath) { 0555 if (path1 == QLatin1String("/")) { 0556 path1.clear(); 0557 } 0558 if (path2 == QLatin1String("/")) { 0559 path2.clear(); 0560 } 0561 } 0562 0563 #ifdef Q_OS_WIN 0564 const bool bLocal1 = isLocalFile(); 0565 const bool bLocal2 = _u.isLocalFile(); 0566 if (!bLocal1 && bLocal2 || bLocal1 && !bLocal2) { 0567 return false; 0568 } 0569 // local files are case insensitive 0570 if (bLocal1 && bLocal2 && 0 != QString::compare(path1, path2, Qt::CaseInsensitive)) { 0571 return false; 0572 } 0573 #endif 0574 if (path1 != path2) { 0575 return false; 0576 } 0577 0578 if (scheme() == _u.scheme() && 0579 authority() == _u.authority() && // user+pass+host+port 0580 encodedQuery() == _u.encodedQuery() && 0581 (fragment() == _u.fragment() || options & CompareWithoutFragment)) { 0582 return true; 0583 } 0584 0585 return false; 0586 } 0587 0588 return (*this == _u); 0589 } 0590 0591 QString KUrl::protocol() const 0592 { 0593 return scheme().toLower(); 0594 } 0595 0596 void KUrl::setProtocol(const QString &proto) 0597 { 0598 setScheme(proto); 0599 } 0600 0601 QString KUrl::user() const 0602 { 0603 return userName(); 0604 } 0605 0606 void KUrl::setUser(const QString &user) 0607 { 0608 setUserName(user); 0609 } 0610 0611 bool KUrl::hasUser() const 0612 { 0613 return !userName().isEmpty(); 0614 } 0615 0616 QString KUrl::pass() const 0617 { 0618 return password(); 0619 } 0620 0621 void KUrl::setPass(const QString &pass) 0622 { 0623 setPassword(pass); 0624 } 0625 0626 bool KUrl::hasPass() const 0627 { 0628 return !password().isEmpty(); 0629 } 0630 0631 bool KUrl::hasHost() const 0632 { 0633 return !host().isEmpty(); 0634 } 0635 0636 bool KUrl::hasPath() const 0637 { 0638 return !path().isEmpty(); 0639 } 0640 0641 void KUrl::setFileName(const QString &_txt) 0642 { 0643 setFragment(QString()); 0644 int i = 0; 0645 while (i < _txt.length() && _txt[i] == QLatin1Char('/')) { 0646 ++i; 0647 } 0648 QString tmp = i ? _txt.mid(i) : _txt; 0649 0650 QString path = this->path(); 0651 if (path.isEmpty()) 0652 #ifdef Q_OS_WIN 0653 path = isLocalFile() ? QDir::rootPath() : QLatin1String("/"); 0654 #else 0655 path = QDir::rootPath(); 0656 #endif 0657 else { 0658 int lastSlash = path.lastIndexOf(QLatin1Char('/')); 0659 if (lastSlash == -1) { 0660 path.clear(); // there's only the file name, remove it 0661 } else if (!path.endsWith(QLatin1Char('/'))) { 0662 path.truncate(lastSlash + 1); // keep the "/" 0663 } 0664 } 0665 0666 path += tmp; 0667 setPath(path); 0668 0669 cleanPath(); 0670 } 0671 0672 void KUrl::cleanPath(const CleanPathOption &options) 0673 { 0674 //if (m_iUriMode != URL) return; 0675 const QString newPath = cleanpath(path(), !(options & KeepDirSeparators), false); 0676 if (path() != newPath) { 0677 setPath(newPath); 0678 } 0679 } 0680 0681 static QString trailingSlash(KUrl::AdjustPathOption trailing, const QString &path) 0682 { 0683 if (trailing == KUrl::LeaveTrailingSlash) { 0684 return path; 0685 } 0686 0687 QString result = path; 0688 0689 if (trailing == KUrl::AddTrailingSlash) { 0690 int len = result.length(); 0691 if ((len > 0) && (result[ len - 1 ] != QLatin1Char('/'))) { 0692 result += QLatin1Char('/'); 0693 } 0694 return result; 0695 } else if (trailing == KUrl::RemoveTrailingSlash) { 0696 if (result == QLatin1String("/")) { 0697 return result; 0698 } 0699 int len = result.length(); 0700 while (len > 1 && result[ len - 1 ] == QLatin1Char('/')) { 0701 len--; 0702 } 0703 result.truncate(len); 0704 return result; 0705 } else { 0706 assert(0); 0707 return result; 0708 } 0709 } 0710 0711 void KUrl::adjustPath(AdjustPathOption trailing) 0712 { 0713 const QString newPath = trailingSlash(trailing, path()); 0714 if (path() != newPath) { 0715 setPath(newPath); 0716 } 0717 } 0718 0719 QString KUrl::encodedPathAndQuery(AdjustPathOption trailing, const EncodedPathAndQueryOptions &options) const 0720 { 0721 QString encodedPath; 0722 #ifdef Q_OS_WIN 0723 // see KUrl::path() 0724 if (isLocalFile()) { 0725 // ### this is probably broken 0726 encodedPath = trailingSlash(trailing, QUrl::toLocalFile()); 0727 encodedPath = QString::fromLatin1(QUrl::toPercentEncoding(encodedPath, "!$&'()*+,;=:@/")); 0728 } else { 0729 encodedPath = trailingSlash(trailing, QString::fromLatin1(QUrl::encodedPath())); 0730 } 0731 #else 0732 encodedPath = trailingSlash(trailing, QString::fromLatin1(QUrl::encodedPath().data())); 0733 #endif 0734 0735 if ((options & AvoidEmptyPath) && encodedPath.isEmpty()) { 0736 encodedPath.append(QLatin1Char('/')); 0737 } 0738 0739 if (hasQuery()) { 0740 return encodedPath + QLatin1Char('?') + QString::fromLatin1(encodedQuery().data()); 0741 } else { 0742 return encodedPath; 0743 } 0744 } 0745 0746 void KUrl::setEncodedPathAndQuery(const QString &_txt) 0747 { 0748 const int pos = _txt.indexOf(QLatin1Char('?')); 0749 if (pos == -1) { 0750 setPath(QUrl::fromPercentEncoding(_txt.toLatin1())); 0751 setEncodedQuery(QByteArray()); 0752 } else { 0753 setPath(QUrl::fromPercentEncoding(_txt.toLatin1().left(pos))); 0754 _setQuery(_txt.right(_txt.length() - pos - 1)); 0755 } 0756 } 0757 0758 QString KUrl::path(AdjustPathOption trailing) const 0759 { 0760 const QString decodedPath = QUrl::path(QUrl::FullyDecoded); 0761 0762 #ifdef Q_OS_WIN 0763 #ifdef DEBUG_KURL 0764 kWarning() << (isLocalFile() ? "converted to local file - the related call should be converted to toLocalFile()" : "") << QUrl::path(); 0765 #endif 0766 return trailingSlash(trailing, isLocalFile() ? QUrl::toLocalFile() : decodedPath); 0767 #else 0768 return trailingSlash(trailing, decodedPath); 0769 #endif 0770 } 0771 0772 QString KUrl::toLocalFile(AdjustPathOption trailing) const 0773 { 0774 if (hasHost() && isLocalFile()) { 0775 KUrl urlWithoutHost(*this); 0776 urlWithoutHost.setHost(QString()); 0777 return trailingSlash(trailing, urlWithoutHost.toLocalFile()); 0778 } 0779 0780 return trailingSlash(trailing, QUrl::toLocalFile()); 0781 } 0782 0783 inline static bool hasSubUrl(const QUrl &url); 0784 0785 static inline bool isLocalFile(const QUrl &url) 0786 { 0787 if (url.scheme().compare(QLatin1String("file"), Qt::CaseInsensitive) != 0 || hasSubUrl(url)) { 0788 return false; 0789 } 0790 0791 if (url.host().isEmpty() || (url.host() == QLatin1String("localhost"))) { 0792 return true; 0793 } 0794 0795 char hostname[ 256 ]; 0796 hostname[ 0 ] = '\0'; 0797 if (!gethostname(hostname, 255)) { 0798 hostname[sizeof(hostname) - 1] = '\0'; 0799 } 0800 0801 for (char *p = hostname; *p; p++) { 0802 *p = tolower(*p); 0803 } 0804 0805 return (url.host() == QString::fromLatin1(hostname)); 0806 } 0807 0808 bool KUrl::isLocalFile() const 0809 { 0810 return ::isLocalFile(*this); 0811 } 0812 0813 void KUrl::setFileEncoding(const QString &encoding) 0814 { 0815 if (!isLocalFile()) { 0816 return; 0817 } 0818 0819 QString q = query(); 0820 0821 if (!q.isEmpty() && q[0] == QLatin1Char('?')) { 0822 q = q.mid(1); 0823 } 0824 0825 QStringList args = q.split(QLatin1Char('&'), QString::SkipEmptyParts); 0826 for (QStringList::Iterator it = args.begin(); 0827 it != args.end();) { 0828 QString s = QUrl::fromPercentEncoding((*it).toLatin1()); 0829 if (s.startsWith(QLatin1String("charset="))) { 0830 it = args.erase(it); 0831 } else { 0832 ++it; 0833 } 0834 } 0835 if (!encoding.isEmpty()) { 0836 args.append(QLatin1String("charset=") + QString::fromLatin1(QUrl::toPercentEncoding(encoding).data())); 0837 } 0838 0839 if (args.isEmpty()) { 0840 _setQuery(QString()); 0841 } else { 0842 _setQuery(args.join(QString(QLatin1Char('&')))); 0843 } 0844 } 0845 0846 QString KUrl::fileEncoding() const 0847 { 0848 if (!isLocalFile()) { 0849 return QString(); 0850 } 0851 0852 QString q = query(); 0853 0854 if (q.isEmpty()) { 0855 return QString(); 0856 } 0857 0858 if (q[0] == QLatin1Char('?')) { 0859 q = q.mid(1); 0860 } 0861 0862 const QStringList args = q.split(QLatin1Char('&'), QString::SkipEmptyParts); 0863 for (QStringList::ConstIterator it = args.begin(); 0864 it != args.end(); 0865 ++it) { 0866 QString s = QUrl::fromPercentEncoding((*it).toLatin1()); 0867 if (s.startsWith(QLatin1String("charset="))) { 0868 return s.mid(8); 0869 } 0870 } 0871 return QString(); 0872 } 0873 0874 inline static bool hasSubUrl(const QUrl &url) 0875 { 0876 // The isValid call triggers QUrlPrivate::validate which needs the full encoded url, 0877 // all this takes too much time for isLocalFile() 0878 const QString scheme = url.scheme(); 0879 if (scheme.isEmpty() /*|| !isValid()*/) { 0880 return false; 0881 } 0882 const QString ref(url.fragment()); 0883 if (ref.isEmpty()) { 0884 return false; 0885 } 0886 switch (ref.at(0).unicode()) { 0887 case 'g': 0888 if (ref.startsWith(QLatin1String("gzip:"))) { 0889 return true; 0890 } 0891 break; 0892 case 'b': 0893 if (ref.startsWith(QLatin1String("bzip:")) || ref.startsWith(QLatin1String("bzip2:"))) { 0894 return true; 0895 } 0896 break; 0897 case 'l': 0898 if (ref.startsWith(QLatin1String("lzma:"))) { 0899 return true; 0900 } 0901 break; 0902 case 'x': 0903 if (ref.startsWith(QLatin1String("xz:"))) { 0904 return true; 0905 } 0906 break; 0907 case 't': 0908 if (ref.startsWith(QLatin1String("tar:"))) { 0909 return true; 0910 } 0911 break; 0912 case 'a': 0913 if (ref.startsWith(QLatin1String("ar:"))) { 0914 return true; 0915 } 0916 break; 0917 case 'z': 0918 if (ref.startsWith(QLatin1String("zip:"))) { 0919 return true; 0920 } 0921 break; 0922 default: 0923 break; 0924 } 0925 if (scheme == QLatin1String("error")) { // anything that starts with error: has suburls 0926 return true; 0927 } 0928 return false; 0929 } 0930 0931 bool KUrl::hasSubUrl() const 0932 { 0933 return ::hasSubUrl(*this); 0934 } 0935 0936 QString KUrl::url(AdjustPathOption trailing) const 0937 { 0938 if (QString::compare(scheme(), QLatin1String("mailto"), Qt::CaseInsensitive) == 0) { 0939 // mailto urls should be prettified, see the url183433 testcase. 0940 return prettyUrl(trailing); 0941 } 0942 if (trailing == AddTrailingSlash && !path().endsWith(QLatin1Char('/'))) { 0943 // -1 and 0 are provided by QUrl, but not +1, so that one is a bit tricky. 0944 // To avoid reimplementing toEncoded() all over again, I just use another QUrl 0945 // Let's hope this is fast, or not called often... 0946 QUrl newUrl(*this); 0947 newUrl.setPath(path() + QLatin1Char('/')); 0948 return QString::fromLatin1(newUrl.toEncoded().data()); 0949 } else if (trailing == RemoveTrailingSlash) { 0950 const QString cleanedPath = trailingSlash(trailing, path()); 0951 if (cleanedPath == QLatin1String("/")) { 0952 if (path() != QLatin1String("/")) { 0953 QUrl fixedUrl = *this; 0954 fixedUrl.setPath(cleanedPath); 0955 return QLatin1String(fixedUrl.toEncoded(None).data()); 0956 } 0957 return QLatin1String(toEncoded(None).data()); 0958 } 0959 } 0960 return QString::fromLatin1(toEncoded(trailing == RemoveTrailingSlash ? StripTrailingSlash : None).data()); 0961 } 0962 0963 static QString toPrettyPercentEncoding(const QString &input, bool forFragment) 0964 { 0965 QString result; 0966 result.reserve(input.length()); 0967 for (int i = 0; i < input.length(); ++i) { 0968 const QChar c = input.at(i); 0969 ushort u = c.unicode(); 0970 if (u < 0x20 0971 || (!forFragment && u == '?') // don't escape '?' in fragments, not needed and wrong (#173101) 0972 || u == '#' || u == '%' 0973 || (u == ' ' && (i + 1 == input.length() || input.at(i + 1).unicode() == ' '))) { 0974 static const char hexdigits[] = "0123456789ABCDEF"; 0975 result += QLatin1Char('%'); 0976 result += QLatin1Char(hexdigits[(u & 0xf0) >> 4]); 0977 result += QLatin1Char(hexdigits[u & 0xf]); 0978 } else { 0979 result += c; 0980 } 0981 } 0982 0983 return result; 0984 } 0985 0986 QString KUrl::prettyUrl(AdjustPathOption trailing) const 0987 { 0988 // reconstruct the URL in a "pretty" form 0989 // a "pretty" URL is NOT suitable for data transfer. It's only for showing data to the user. 0990 // however, it must be parseable back to its original state, since 0991 // notably Konqueror displays it in the Location address. 0992 0993 // A pretty URL is the same as a normal URL, except that: 0994 // - the password is removed 0995 // - the hostname is shown in Unicode (as opposed to ACE/Punycode) 0996 // - the pathname and fragment parts are shown in Unicode (as opposed to %-encoding) 0997 QString result = scheme(); 0998 if (!result.isEmpty()) { 0999 if (!authority().isEmpty() || result == QLatin1String("file") || path().isEmpty()) { 1000 result += QLatin1String("://"); 1001 } else { 1002 result += QLatin1Char(':'); 1003 } 1004 } 1005 1006 QString tmp = userName(); 1007 if (!tmp.isEmpty()) { 1008 result += QString::fromLatin1(QUrl::toPercentEncoding(tmp).data()); 1009 result += QLatin1Char('@'); 1010 } 1011 1012 // Check if host is an ipv6 address 1013 tmp = host(); 1014 if (tmp.contains(QLatin1Char(':'))) { 1015 result += QLatin1Char('[') + tmp + QLatin1Char(']'); 1016 } else { 1017 result += tmp; 1018 } 1019 1020 if (port() != -1) { 1021 result += QLatin1Char(':'); 1022 result += QString::number(port()); 1023 } 1024 1025 tmp = path(); 1026 #ifdef Q_OS_WIN 1027 if (isLocalFile()) { 1028 tmp.prepend(QLatin1Char('/')); // KUrl::path() returns toLocalFile() on windows so we need to add the / back to create a proper url 1029 } 1030 #endif 1031 result += toPrettyPercentEncoding(tmp, false); 1032 1033 // adjust the trailing slash, if necessary 1034 if (trailing == AddTrailingSlash && !tmp.endsWith(QLatin1Char('/'))) { 1035 result += QLatin1Char('/'); 1036 } else if (trailing == RemoveTrailingSlash && tmp.length() > 1 && tmp.endsWith(QLatin1Char('/'))) { 1037 result.chop(1); 1038 } 1039 1040 if (hasQuery()) { 1041 result += QLatin1Char('?'); 1042 result += QString::fromLatin1(encodedQuery().data()); 1043 } 1044 1045 if (hasFragment()) { 1046 result += QLatin1Char('#'); 1047 result += toPrettyPercentEncoding(fragment(), true); 1048 } 1049 1050 return result; 1051 } 1052 1053 QString KUrl::pathOrUrl(AdjustPathOption trailing) const 1054 { 1055 if (isLocalFile() && fragment().isNull() && encodedQuery().isNull()) { 1056 return toLocalFile(trailing); 1057 } else { 1058 return prettyUrl(trailing); 1059 } 1060 } 1061 1062 // Used for text/uri-list in the mime data 1063 QString KUrl::toMimeDataString() const // don't fold this into populateMimeData, it's also needed by other code like konqdrag 1064 { 1065 if (isLocalFile()) { 1066 #if 1 1067 return url(); 1068 #else 1069 // According to the XDND spec, file:/ URLs for DND must have 1070 // the hostname part. But in really it just breaks many apps, 1071 // so it's disabled for now. 1072 const QString s = url(); 1073 if (!s.startsWith(QLatin1String("file://"))) { 1074 char hostname[257]; 1075 if (gethostname(hostname, 255) == 0) { 1076 hostname[256] = '\0'; 1077 return QString("file://") + hostname + s.mid(5); 1078 } 1079 } 1080 #endif 1081 } 1082 1083 if (hasPass()) { 1084 KUrl safeUrl(*this); 1085 safeUrl.setPassword(QString()); 1086 return safeUrl.url(); 1087 } 1088 return url(); 1089 } 1090 1091 KUrl::List KUrl::split(const KUrl &_url) 1092 { 1093 QString ref; 1094 bool hasRef; 1095 KUrl::List lst; 1096 KUrl url = _url; 1097 1098 while (true) { 1099 KUrl u = url; 1100 u.setFragment(QString()); 1101 lst.append(u); 1102 if (url.hasSubUrl()) { 1103 url = KUrl(url.fragment()); 1104 } else { 1105 ref = url.fragment(); 1106 hasRef = url.hasFragment(); 1107 break; 1108 } 1109 } 1110 1111 if (hasRef) { 1112 // Set HTML ref in all URLs. 1113 KUrl::List::Iterator it; 1114 for (it = lst.begin(); it != lst.end(); ++it) { 1115 (*it).setFragment(ref); 1116 } 1117 } 1118 1119 return lst; 1120 } 1121 1122 KUrl::List KUrl::split(const QString &_url) 1123 { 1124 return split(KUrl(_url)); 1125 } 1126 1127 KUrl KUrl::join(const KUrl::List &lst) 1128 { 1129 if (lst.isEmpty()) { 1130 return KUrl(); 1131 } 1132 KUrl tmp; 1133 1134 bool first = true; 1135 QListIterator<KUrl> it(lst); 1136 it.toBack(); 1137 while (it.hasPrevious()) { 1138 KUrl u(it.previous()); 1139 if (!first) { 1140 u.setEncodedFragment(tmp.url().toLatin1() /* TODO double check encoding */); 1141 } 1142 tmp = u; 1143 1144 first = false; 1145 } 1146 1147 return tmp; 1148 } 1149 1150 QString KUrl::fileName(const DirectoryOptions &options) const 1151 { 1152 Q_ASSERT(options != 0); //Disallow options == false 1153 QString fname; 1154 if (hasSubUrl()) { // If we have a suburl, then return the filename from there 1155 const KUrl::List list = KUrl::split(*this); 1156 return list.last().fileName(options); 1157 } 1158 const QString path = this->path(); 1159 1160 int len = path.length(); 1161 if (len == 0) { 1162 return fname; 1163 } 1164 1165 if (!(options & ObeyTrailingSlash)) { 1166 while (len >= 1 && path[ len - 1 ] == QLatin1Char('/')) { 1167 len--; 1168 } 1169 } else if (path[ len - 1 ] == QLatin1Char('/')) { 1170 return fname; 1171 } 1172 1173 // Does the path only consist of '/' characters ? 1174 if (len == 1 && path[ 0 ] == QLatin1Char('/')) { 1175 return fname; 1176 } 1177 1178 // Skip last n slashes 1179 int n = 1; 1180 int i = len; 1181 do { 1182 i = path.lastIndexOf(QLatin1Char('/'), i - 1); 1183 } while (--n && (i > 0)); 1184 1185 // If ( i == -1 ) => the first character is not a '/' 1186 // So it's some URL like file:blah.tgz, return the whole path 1187 if (i == -1) { 1188 if (len == (int)path.length()) { 1189 fname = path; 1190 } else 1191 // Might get here if _strip_trailing_slash is true 1192 { 1193 fname = path.left(len); 1194 } 1195 } else { 1196 fname = path.mid(i + 1, len - i - 1); // TO CHECK 1197 } 1198 return fname; 1199 } 1200 1201 void KUrl::addPath(const QString &_txt) 1202 { 1203 if (hasSubUrl()) { 1204 KUrl::List lst = split(*this); 1205 KUrl &u = lst.last(); 1206 u.addPath(_txt); 1207 *this = join(lst); 1208 return; 1209 } 1210 1211 if (_txt.isEmpty()) { 1212 return; 1213 } 1214 1215 QString strPath = path(); 1216 int i = 0; 1217 int len = strPath.length(); 1218 // Add the trailing '/' if it is missing 1219 if (_txt[0] != QLatin1Char('/') && (len == 0 || strPath[ len - 1 ] != QLatin1Char('/'))) { 1220 strPath += QLatin1Char('/'); 1221 } 1222 1223 // No double '/' characters 1224 i = 0; 1225 const int _txtlen = _txt.length(); 1226 if (strPath.endsWith(QLatin1Char('/'))) { 1227 while ((i < _txtlen) && (_txt[i] == QLatin1Char('/'))) { 1228 ++i; 1229 } 1230 } 1231 1232 setPath(strPath + _txt.mid(i)); 1233 //kDebug(kurlDebugArea())<<"addPath: resultpath="<<path(); 1234 } 1235 1236 QString KUrl::directory(const DirectoryOptions &options) const 1237 { 1238 Q_ASSERT(options != 0); //Disallow options == false 1239 QString result = path(); 1240 if (!(options & ObeyTrailingSlash)) { 1241 result = trailingSlash(RemoveTrailingSlash, result); 1242 } 1243 1244 if (result.isEmpty() || result == QLatin1String("/")) { 1245 return result; 1246 } 1247 1248 int i = result.lastIndexOf(QLatin1Char('/')); 1249 // If ( i == -1 ) => the first character is not a '/' 1250 // So it's some URL like file:blah.tgz, with no path 1251 if (i == -1) { 1252 return QString(); 1253 } 1254 1255 if (i == 0) { 1256 return QString(QLatin1Char('/')); 1257 } 1258 1259 #ifdef Q_OS_WIN 1260 if (i == 2 && result[1] == QLatin1Char(':')) { 1261 return result.left(3); 1262 } 1263 #endif 1264 1265 if (options & AppendTrailingSlash) { 1266 result = result.left(i + 1); 1267 } else { 1268 result = result.left(i); 1269 } 1270 1271 return result; 1272 } 1273 1274 bool KUrl::cd(const QString &_dir) 1275 { 1276 if (_dir.isEmpty() || !isValid()) { 1277 return false; 1278 } 1279 1280 if (hasSubUrl()) { 1281 KUrl::List lst = split(*this); 1282 KUrl &u = lst.last(); 1283 u.cd(_dir); 1284 *this = join(lst); 1285 return true; 1286 } 1287 1288 // absolute path ? 1289 #ifdef Q_OS_WIN 1290 if (!QFileInfo(_dir).isRelative()) 1291 #else 1292 if (_dir[0] == QLatin1Char('/')) 1293 #endif 1294 { 1295 setPath(_dir); 1296 setHTMLRef(QString()); 1297 setEncodedQuery(QByteArray()); 1298 return true; 1299 } 1300 1301 // Users home directory on the local disk ? 1302 if (_dir[0] == QLatin1Char('~') && scheme() == QLatin1String("file")) { 1303 QString strPath = QDir::homePath(); 1304 strPath += QLatin1Char('/'); 1305 strPath += _dir.right(strPath.length() - 1); 1306 setPath(strPath); 1307 setHTMLRef(QString()); 1308 setEncodedQuery(QByteArray()); 1309 return true; 1310 } 1311 1312 // relative path 1313 // we always work on the past of the first url. 1314 // Sub URLs are not touched. 1315 1316 // append '/' if necessary 1317 QString p = path(AddTrailingSlash); 1318 p += _dir; 1319 p = cleanpath(p, true, false); 1320 setPath(p); 1321 1322 setHTMLRef(QString()); 1323 setEncodedQuery(QByteArray()); 1324 1325 return true; 1326 } 1327 1328 KUrl KUrl::upUrl() const 1329 { 1330 if (!isValid() || isRelative()) { 1331 return KUrl(); 1332 } 1333 1334 if (!encodedQuery().isEmpty()) { 1335 KUrl u(*this); 1336 u.setEncodedQuery(QByteArray()); 1337 return u; 1338 } 1339 1340 if (!hasSubUrl()) { 1341 KUrl u(*this); 1342 u.cd(QLatin1String("../")); 1343 return u; 1344 } 1345 1346 // We have a subURL. 1347 KUrl::List lst = split(*this); 1348 if (lst.isEmpty()) { 1349 return KUrl(); // Huh? 1350 } 1351 while (true) { 1352 KUrl &u = lst.last(); 1353 const QString old = u.path(); 1354 u.cd(QLatin1String("../")); 1355 if (u.path() != old) { 1356 break; // Finished. 1357 } 1358 if (lst.count() == 1) { 1359 break; // Finished. 1360 } 1361 lst.removeLast(); 1362 } 1363 return join(lst); 1364 } 1365 1366 QString KUrl::htmlRef() const 1367 { 1368 if (!hasSubUrl()) { 1369 return fragment(QUrl::FullyDecoded); 1370 } 1371 1372 const List lst = split(*this); 1373 return (*lst.begin()).fragment(QUrl::FullyDecoded); 1374 } 1375 1376 QString KUrl::encodedHtmlRef() const 1377 { 1378 if (!hasSubUrl()) { 1379 return ref(); 1380 } 1381 1382 const List lst = split(*this); 1383 return (*lst.begin()).ref(); 1384 } 1385 1386 void KUrl::setHTMLRef(const QString &_ref) 1387 { 1388 if (!hasSubUrl()) { 1389 setFragment(_ref); 1390 return; 1391 } 1392 1393 List lst = split(*this); 1394 1395 (*lst.begin()).setFragment(_ref); 1396 1397 *this = join(lst); 1398 } 1399 1400 bool KUrl::hasHTMLRef() const 1401 { 1402 if (!hasSubUrl()) { 1403 return hasRef(); 1404 } 1405 1406 const List lst = split(*this); 1407 return (*lst.begin()).hasRef(); 1408 } 1409 1410 void KUrl::setDirectory(const QString &dir) 1411 { 1412 if (dir.endsWith(QLatin1Char('/'))) { 1413 setPath(dir); 1414 } else { 1415 setPath(dir + QLatin1Char('/')); 1416 } 1417 } 1418 1419 void KUrl::setQuery(const QString &_txt) 1420 { 1421 if (!_txt.isEmpty() && _txt[0] == QLatin1Char('?')) { 1422 _setQuery(_txt.length() > 1 ? _txt.mid(1) : QString::fromLatin1("") /*empty, not null*/); 1423 } else { 1424 _setQuery(_txt); 1425 } 1426 } 1427 1428 void KUrl::_setQuery(const QString &query) 1429 { 1430 if (query.isNull()) { 1431 setEncodedQuery(QByteArray()); 1432 } else if (query.isEmpty()) { 1433 setEncodedQuery(""); 1434 } else { 1435 setEncodedQuery(query.toLatin1()); // already percent-escaped, so toLatin1 is ok 1436 } 1437 } 1438 1439 QString KUrl::query() const 1440 { 1441 if (!hasQuery()) { 1442 return QString(); 1443 } 1444 return QString(QLatin1Char('?')) + QString::fromLatin1(encodedQuery().data()); 1445 } 1446 1447 void KUrl::_setEncodedUrl(const QByteArray &url) 1448 { 1449 setEncodedUrl(url, QUrl::TolerantMode); 1450 if (!isValid()) { // see unit tests referring to N183630/task 183874 1451 setUrl(QString::fromUtf8(url.data()), QUrl::TolerantMode); 1452 } 1453 } 1454 1455 #ifndef KDELIBS4SUPPORT_NO_DEPRECATED 1456 bool urlcmp(const QString &_url1, const QString &_url2) 1457 { 1458 return QUrl(_url1, QUrl::TolerantMode) == QUrl(_url2, QUrl::TolerantMode); 1459 #if 0 1460 // Both empty ? 1461 if (_url1.isEmpty() && _url2.isEmpty()) { 1462 return true; 1463 } 1464 // Only one empty ? 1465 if (_url1.isEmpty() || _url2.isEmpty()) { 1466 return false; 1467 } 1468 1469 KUrl::List list1 = KUrl::split(_url1); 1470 KUrl::List list2 = KUrl::split(_url2); 1471 1472 // Malformed ? 1473 if (list1.isEmpty() || list2.isEmpty()) { 1474 return false; 1475 } 1476 1477 return (list1 == list2); 1478 #endif 1479 } 1480 #endif 1481 1482 #ifndef KDELIBS4SUPPORT_NO_DEPRECATED 1483 bool urlcmp(const QString &_url1, const QString &_url2, const KUrl::EqualsOptions &_options) 1484 { 1485 // Both empty ? 1486 if (_url1.isEmpty() && _url2.isEmpty()) { 1487 return true; 1488 } 1489 // Only one empty ? 1490 if (_url1.isEmpty() || _url2.isEmpty()) { 1491 return false; 1492 } 1493 1494 KUrl u1(_url1); 1495 KUrl u2(_url2); 1496 return u1.equals(u2, _options); 1497 1498 #if 0 // kde3 code that supported nested urls 1499 1500 KUrl::List list1 = KUrl::split(_url1); 1501 KUrl::List list2 = KUrl::split(_url2); 1502 1503 // Malformed ? 1504 if (list1.isEmpty() || list2.isEmpty()) { 1505 return false; 1506 } 1507 1508 int size = list1.count(); 1509 if (list2.count() != size) { 1510 return false; 1511 } 1512 1513 if (_ignore_ref) { 1514 (*list1.begin()).setRef(QString()); 1515 (*list2.begin()).setRef(QString()); 1516 } 1517 1518 KUrl::List::Iterator it1 = list1.begin(); 1519 KUrl::List::Iterator it2 = list2.begin(); 1520 for (; it1 != list1.end(); ++it1, ++it2) 1521 if (!(*it1).equals(*it2, _ignore_trailing)) { 1522 return false; 1523 } 1524 return true; 1525 #endif 1526 } 1527 #endif 1528 1529 // static 1530 #ifndef KDELIBS4SUPPORT_NO_DEPRECATED 1531 KUrl KUrl::fromPathOrUrl(const QString &text) 1532 { 1533 KUrl url; 1534 if (!text.isEmpty()) { 1535 if (!QDir::isRelativePath(text) || text[0] == QLatin1Char('~')) { 1536 url.setPath(text); 1537 } else { 1538 url = KUrl(text); 1539 } 1540 } 1541 1542 return url; 1543 } 1544 #endif 1545 1546 static QString _relativePath(const QString &base_dir, const QString &path, bool &isParent) 1547 { 1548 QString _base_dir(QDir::cleanPath(base_dir)); 1549 QString _path(QDir::cleanPath(path.isEmpty() || QDir::isRelativePath(path) ? _base_dir + QLatin1Char('/') + path : path)); 1550 1551 if (_base_dir.isEmpty()) { 1552 return _path; 1553 } 1554 1555 if (_base_dir[_base_dir.length() - 1] != QLatin1Char('/')) { 1556 _base_dir.append(QLatin1Char('/')); 1557 } 1558 1559 const QStringList list1 = _base_dir.split(QLatin1Char('/'), QString::SkipEmptyParts); 1560 const QStringList list2 = _path.split(QLatin1Char('/'), QString::SkipEmptyParts); 1561 1562 // Find where they meet 1563 int level = 0; 1564 int maxLevel = qMin(list1.count(), list2.count()); 1565 while ((level < maxLevel) && (list1[level] == list2[level])) { 1566 level++; 1567 } 1568 1569 QString result; 1570 // Need to go down out of the first path to the common branch. 1571 for (int i = level; i < list1.count(); i++) { 1572 result.append(QLatin1String("../")); 1573 } 1574 1575 // Now up up from the common branch to the second path. 1576 for (int i = level; i < list2.count(); i++) { 1577 result.append(list2[i]).append(QLatin1Char('/')); 1578 } 1579 1580 if ((level < list2.count()) && (path[path.length() - 1] != QLatin1Char('/'))) { 1581 result.truncate(result.length() - 1); 1582 } 1583 1584 isParent = (level == list1.count()); 1585 1586 return result; 1587 } 1588 1589 QString KUrl::relativePath(const QString &base_dir, const QString &path, bool *isParent) 1590 { 1591 bool parent = false; 1592 QString result = _relativePath(base_dir, path, parent); 1593 if (parent) { 1594 result.prepend(QLatin1String("./")); 1595 } 1596 1597 if (isParent) { 1598 *isParent = parent; 1599 } 1600 1601 return result; 1602 } 1603 1604 QString KUrl::relativeUrl(const KUrl &base_url, const KUrl &url) 1605 { 1606 if ((url.protocol() != base_url.protocol()) || 1607 (url.host() != base_url.host()) || 1608 (url.port() && url.port() != base_url.port()) || 1609 (url.hasUser() && url.user() != base_url.user()) || 1610 (url.hasPass() && url.pass() != base_url.pass())) { 1611 return url.url(); 1612 } 1613 1614 QString relURL; 1615 1616 if ((base_url.path() != url.path()) || (base_url.query() != url.query())) { 1617 bool dummy; 1618 QString basePath = base_url.directory(KUrl::ObeyTrailingSlash); 1619 static const char s_pathExcludeChars[] = "!$&'()*+,;=:@/"; 1620 relURL = QString::fromLatin1(QUrl::toPercentEncoding(_relativePath(basePath, url.path(), dummy), s_pathExcludeChars)); 1621 relURL += url.query(); 1622 } 1623 1624 if (url.hasRef()) { 1625 relURL += QLatin1Char('#'); 1626 relURL += url.ref(); 1627 } 1628 1629 if (relURL.isEmpty()) { 1630 return QLatin1String("./"); 1631 } 1632 1633 return relURL; 1634 } 1635 1636 void KUrl::setPath(const QString &_path) 1637 { 1638 #if defined(Q_OS_WIN) && defined(DEBUG_KURL) 1639 qDebug() << "KUrl::setPath " << " " << _path.toLatin1().data(); 1640 #endif 1641 if (scheme().isEmpty()) { 1642 setScheme(QLatin1String("file")); 1643 } 1644 #pragma message("KDE5 TODO: Remove tildeExpand feature for local paths") 1645 #if 0 1646 QString path = KShell::tildeExpand(_path); 1647 if (path.isEmpty()) 1648 #endif 1649 QString path = _path; 1650 #ifdef Q_OS_WIN 1651 const int len = path.length(); 1652 if (len == 2 && IS_LETTER(path[0]) && path[1] == QLatin1Char(':')) { 1653 path += QLatin1Char('/'); 1654 } 1655 //This is necessary because QUrl has the "path" part including the first slash 1656 //Without this QUrl doesn't understand that this is a path, and some operations fail 1657 //e.g. C:/blah needs to become /C:/blah 1658 else if (len > 0 && path[0] != QLatin1Char('/') && scheme() == QLatin1String("file")) { 1659 path = QLatin1Char('/') + path; 1660 } 1661 #endif 1662 QUrl::setPath(path, QUrl::DecodedMode); 1663 } 1664 1665 #if 0 // this would be if we didn't decode '+' into ' ' 1666 QMap< QString, QString > KUrl::queryItems(int options) const 1667 { 1668 QMap< QString, QString > result; 1669 const QList<QPair<QString, QString> > items = QUrl::queryItems(); 1670 QPair<QString, QString> item; 1671 Q_FOREACH (item, items) { 1672 result.insert(options & CaseInsensitiveKeys ? item.first.toLower() : item.first, item.second); 1673 } 1674 return result; 1675 } 1676 #endif 1677 1678 QMap< QString, QString > KUrl::queryItems(const QueryItemsOptions &options) const 1679 { 1680 const QString strQueryEncoded = QString::fromLatin1(encodedQuery().data()); 1681 if (strQueryEncoded.isEmpty()) { 1682 return QMap<QString, QString>(); 1683 } 1684 1685 QMap< QString, QString > result; 1686 const QStringList items = strQueryEncoded.split(QLatin1Char('&'), QString::SkipEmptyParts); 1687 for (QStringList::const_iterator it = items.begin(); it != items.end(); ++it) { 1688 const int equal_pos = (*it).indexOf(QLatin1Char('=')); 1689 if (equal_pos > 0) { // = is not the first char... 1690 QString name = (*it).left(equal_pos); 1691 if (options & CaseInsensitiveKeys) { 1692 name = name.toLower(); 1693 } 1694 QString value = (*it).mid(equal_pos + 1); 1695 if (value.isEmpty()) { 1696 result.insert(name, QString::fromLatin1("")); 1697 } else { 1698 // ### why is decoding name not necessary? 1699 value.replace(QLatin1Char('+'), QLatin1Char(' ')); // + in queries means space 1700 result.insert(name, QUrl::fromPercentEncoding(value.toLatin1())); 1701 } 1702 } else if (equal_pos < 0) { // no = 1703 QString name = (*it); 1704 if (options & CaseInsensitiveKeys) { 1705 name = name.toLower(); 1706 } 1707 result.insert(name, QString()); 1708 } 1709 } 1710 1711 return result; 1712 } 1713 1714 QString KUrl::queryItem(const QString &_item) const 1715 { 1716 const QString strQueryEncoded = QString::fromLatin1(encodedQuery().data()); 1717 const QString item = _item + QLatin1Char('='); 1718 if (strQueryEncoded.length() <= 1) { 1719 return QString(); 1720 } 1721 1722 const QStringList items = strQueryEncoded.split(QString(QLatin1Char('&')), QString::SkipEmptyParts); 1723 const int _len = item.length(); 1724 for (QStringList::ConstIterator it = items.begin(); it != items.end(); ++it) { 1725 if ((*it).startsWith(item)) { 1726 if ((*it).length() > _len) { 1727 QString str = (*it).mid(_len); 1728 str.replace(QLatin1Char('+'), QLatin1Char(' ')); // + in queries means space. 1729 return QUrl::fromPercentEncoding(str.toLatin1()); 1730 } else { // empty value 1731 return QString::fromLatin1(""); 1732 } 1733 } 1734 } 1735 1736 return QString(); 1737 } 1738 1739 void KUrl::addQueryItem(const QString &_item, const QString &_value) 1740 { 1741 QString item = _item + QLatin1Char('='); 1742 QString value = QString::fromLatin1(QUrl::toPercentEncoding(_value).data()); 1743 1744 QString strQueryEncoded = QString::fromLatin1(encodedQuery().data()); 1745 if (!strQueryEncoded.isEmpty()) { 1746 strQueryEncoded += QLatin1Char('&'); 1747 } 1748 strQueryEncoded += item + value; 1749 setEncodedQuery(strQueryEncoded.toLatin1()); 1750 } 1751 1752 void KUrl::populateMimeData(QMimeData *mimeData, 1753 const MetaDataMap &metaData, 1754 MimeDataFlags flags) const 1755 { 1756 populateMimeDataHelper(KUrl::List(*this), mimeData, metaData, flags); 1757 } 1758 1759 bool KUrl::hasRef() const 1760 { 1761 return hasFragment(); 1762 } 1763 1764 void KUrl::setRef(const QString &fragment) 1765 { 1766 setFragment(fragment); 1767 } 1768 1769 QString KUrl::ref() const 1770 { 1771 if (!hasFragment()) { 1772 return QString(); 1773 } else { 1774 return fragment(QUrl::FullyEncoded); 1775 } 1776 } 1777 1778 bool KUrl::isParentOf(const KUrl &u) const 1779 { 1780 return QUrl::isParentOf(u) || equals(u, CompareWithoutTrailingSlash); 1781 }