File indexing completed on 2023-09-24 04:08:38
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 1999 Torben Weis <weis@kde.org> 0004 SPDX-FileCopyrightText: 2000 Waldo Bastain <bastain@kde.org> 0005 SPDX-FileCopyrightText: 2000 Dawit Alemayehu <adawit@kde.org> 0006 SPDX-FileCopyrightText: 2008 Jarosław Staniek <staniek@kde.org> 0007 SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org> 0008 0009 SPDX-License-Identifier: LGPL-2.0-only 0010 */ 0011 0012 #include "kprotocolmanager.h" 0013 #include "kprotocolinfo_p.h" 0014 0015 #include "hostinfo.h" 0016 0017 #include <config-kiocore.h> 0018 0019 #include <qplatformdefs.h> 0020 #include <string.h> 0021 #ifdef Q_OS_WIN 0022 #include <qt_windows.h> 0023 #undef interface // windows.h defines this, breaks QtDBus since it has parameters named interface 0024 #else 0025 #include <sys/utsname.h> 0026 #endif 0027 0028 #include <QCache> 0029 #include <QCoreApplication> 0030 #ifndef KIO_ANDROID_STUB 0031 #include <QDBusInterface> 0032 #include <QDBusReply> 0033 #endif 0034 #include <QHostAddress> 0035 #include <QHostInfo> 0036 #include <QLocale> 0037 #include <QMimeDatabase> 0038 #include <QRegularExpression> 0039 #include <QSslSocket> 0040 #include <QStandardPaths> 0041 #include <QUrl> 0042 0043 #if !defined(QT_NO_NETWORKPROXY) && (defined(Q_OS_WIN32) || defined(Q_OS_MAC)) 0044 #include <QNetworkProxyFactory> 0045 #include <QNetworkProxyQuery> 0046 #endif 0047 0048 #include <KConfigGroup> 0049 #include <KSharedConfig> 0050 #include <kio_version.h> 0051 0052 #include <kprotocolinfofactory_p.h> 0053 0054 #include "http_worker_defaults.h" 0055 #include "ioworker_defaults.h" 0056 #include "workerconfig.h" 0057 0058 using SubnetPair = QPair<QHostAddress, int>; 0059 0060 /* 0061 Domain suffix match. E.g. return true if host is "cuzco.inka.de" and 0062 nplist is "inka.de,hadiko.de" or if host is "localhost" and nplist is 0063 "localhost". 0064 */ 0065 static bool revmatch(const char *host, const char *nplist) 0066 { 0067 if (host == nullptr) { 0068 return false; 0069 } 0070 0071 const char *hptr = host + strlen(host) - 1; 0072 const char *nptr = nplist + strlen(nplist) - 1; 0073 const char *shptr = hptr; 0074 0075 while (nptr >= nplist) { 0076 if (*hptr != *nptr) { 0077 hptr = shptr; 0078 0079 // Try to find another domain or host in the list 0080 while (--nptr >= nplist && *nptr != ',' && *nptr != ' ') { 0081 ; 0082 } 0083 0084 // Strip out multiple spaces and commas 0085 while (--nptr >= nplist && (*nptr == ',' || *nptr == ' ')) { 0086 ; 0087 } 0088 } else { 0089 if (nptr == nplist || nptr[-1] == ',' || nptr[-1] == ' ') { 0090 return true; 0091 } 0092 if (nptr[-1] == '/' && hptr == host) { // "bugs.kde.org" vs "http://bugs.kde.org", the config UI says URLs are ok 0093 return true; 0094 } 0095 if (hptr == host) { // e.g. revmatch("bugs.kde.org","mybugs.kde.org") 0096 return false; 0097 } 0098 0099 hptr--; 0100 nptr--; 0101 } 0102 } 0103 0104 return false; 0105 } 0106 0107 class KProxyData : public QObject 0108 { 0109 Q_OBJECT 0110 public: 0111 KProxyData(const QString &workerProtocol, const QStringList &proxyAddresses) 0112 : protocol(workerProtocol) 0113 , proxyList(proxyAddresses) 0114 { 0115 } 0116 0117 void removeAddress(const QString &address) 0118 { 0119 proxyList.removeAll(address); 0120 } 0121 0122 QString protocol; 0123 QStringList proxyList; 0124 }; 0125 0126 class KProtocolManagerPrivate 0127 { 0128 public: 0129 KProtocolManagerPrivate(); 0130 ~KProtocolManagerPrivate(); 0131 bool shouldIgnoreProxyFor(const QUrl &url); 0132 void sync(); 0133 KProtocolManager::ProxyType proxyType(); 0134 bool useReverseProxy(); 0135 QString readNoProxyFor(); 0136 QString proxyFor(const QString &protocol); 0137 QStringList getSystemProxyFor(const QUrl &url); 0138 0139 QMutex mutex; // protects all member vars 0140 KSharedConfig::Ptr configPtr; 0141 KSharedConfig::Ptr http_config; 0142 QString modifiers; 0143 QString useragent; 0144 QString noProxyFor; 0145 QList<SubnetPair> noProxySubnets; 0146 QCache<QString, KProxyData> cachedProxyData; 0147 0148 QMap<QString /*mimetype*/, QString /*protocol*/> protocolForArchiveMimetypes; 0149 }; 0150 0151 Q_GLOBAL_STATIC(KProtocolManagerPrivate, kProtocolManagerPrivate) 0152 0153 static void syncOnExit() 0154 { 0155 if (kProtocolManagerPrivate.exists()) { 0156 kProtocolManagerPrivate()->sync(); 0157 } 0158 } 0159 0160 KProtocolManagerPrivate::KProtocolManagerPrivate() 0161 { 0162 // post routine since KConfig::sync() breaks if called too late 0163 qAddPostRoutine(syncOnExit); 0164 cachedProxyData.setMaxCost(200); // double the max cost. 0165 } 0166 0167 KProtocolManagerPrivate::~KProtocolManagerPrivate() 0168 { 0169 } 0170 0171 /* 0172 * Returns true if url is in the no proxy list. 0173 */ 0174 bool KProtocolManagerPrivate::shouldIgnoreProxyFor(const QUrl &url) 0175 { 0176 bool isMatch = false; 0177 const KProtocolManager::ProxyType type = proxyType(); 0178 const bool useRevProxy = ((type == KProtocolManager::ManualProxy) && useReverseProxy()); 0179 const bool useNoProxyList = (type == KProtocolManager::ManualProxy || type == KProtocolManager::EnvVarProxy); 0180 0181 // No proxy only applies to ManualProxy and EnvVarProxy types... 0182 if (useNoProxyList && noProxyFor.isEmpty()) { 0183 QStringList noProxyForList(readNoProxyFor().split(QLatin1Char(','))); 0184 QMutableStringListIterator it(noProxyForList); 0185 while (it.hasNext()) { 0186 SubnetPair subnet = QHostAddress::parseSubnet(it.next()); 0187 if (!subnet.first.isNull()) { 0188 noProxySubnets << subnet; 0189 it.remove(); 0190 } 0191 } 0192 noProxyFor = noProxyForList.join(QLatin1Char(',')); 0193 } 0194 0195 if (!noProxyFor.isEmpty()) { 0196 QString qhost = url.host().toLower(); 0197 QByteArray host = qhost.toLatin1(); 0198 const QString qno_proxy = noProxyFor.trimmed().toLower(); 0199 const QByteArray no_proxy = qno_proxy.toLatin1(); 0200 isMatch = revmatch(host.constData(), no_proxy.constData()); 0201 0202 // If no match is found and the request url has a port 0203 // number, try the combination of "host:port". This allows 0204 // users to enter host:port in the No-proxy-For list. 0205 if (!isMatch && url.port() > 0) { 0206 qhost += QLatin1Char(':') + QString::number(url.port()); 0207 host = qhost.toLatin1(); 0208 isMatch = revmatch(host.constData(), no_proxy.constData()); 0209 } 0210 0211 // If the hostname does not contain a dot, check if 0212 // <local> is part of noProxy. 0213 if (!isMatch && !host.isEmpty() && (strchr(host.constData(), '.') == nullptr)) { 0214 isMatch = revmatch("<local>", no_proxy.constData()); 0215 } 0216 } 0217 0218 const QString host(url.host()); 0219 0220 if (!noProxySubnets.isEmpty() && !host.isEmpty()) { 0221 QHostAddress address(host); 0222 // If request url is not IP address, do a DNS lookup of the hostname. 0223 // TODO: Perhaps we should make configurable ? 0224 if (address.isNull()) { 0225 // qDebug() << "Performing DNS lookup for" << host; 0226 QHostInfo info = KIO::HostInfo::lookupHost(host, 2000); 0227 const QList<QHostAddress> addresses = info.addresses(); 0228 if (!addresses.isEmpty()) { 0229 address = addresses.first(); 0230 } 0231 } 0232 0233 if (!address.isNull()) { 0234 for (const SubnetPair &subnet : std::as_const(noProxySubnets)) { 0235 if (address.isInSubnet(subnet)) { 0236 isMatch = true; 0237 break; 0238 } 0239 } 0240 } 0241 } 0242 0243 return (useRevProxy != isMatch); 0244 } 0245 0246 void KProtocolManagerPrivate::sync() 0247 { 0248 QMutexLocker lock(&mutex); 0249 if (http_config) { 0250 http_config->sync(); 0251 } 0252 if (configPtr) { 0253 configPtr->sync(); 0254 } 0255 } 0256 0257 void KProtocolManager::reparseConfiguration() 0258 { 0259 KProtocolManagerPrivate *d = kProtocolManagerPrivate(); 0260 QMutexLocker lock(&d->mutex); 0261 if (d->http_config) { 0262 d->http_config->reparseConfiguration(); 0263 } 0264 if (d->configPtr) { 0265 d->configPtr->reparseConfiguration(); 0266 } 0267 d->cachedProxyData.clear(); 0268 d->noProxyFor.clear(); 0269 d->modifiers.clear(); 0270 d->useragent.clear(); 0271 lock.unlock(); 0272 0273 // Force the slave config to re-read its config... 0274 KIO::WorkerConfig::self()->reset(); 0275 } 0276 0277 static KSharedConfig::Ptr config() 0278 { 0279 KProtocolManagerPrivate *d = kProtocolManagerPrivate(); 0280 Q_ASSERT(!d->mutex.tryLock()); // the caller must have locked the mutex 0281 if (!d->configPtr) { 0282 d->configPtr = KSharedConfig::openConfig(QStringLiteral("kioslaverc"), KConfig::NoGlobals); 0283 } 0284 return d->configPtr; 0285 } 0286 0287 KProtocolManager::ProxyType KProtocolManagerPrivate::proxyType() 0288 { 0289 KConfigGroup cg(config(), "Proxy Settings"); 0290 return static_cast<KProtocolManager::ProxyType>(cg.readEntry("ProxyType", 0)); 0291 } 0292 0293 bool KProtocolManagerPrivate::useReverseProxy() 0294 { 0295 KConfigGroup cg(config(), "Proxy Settings"); 0296 return cg.readEntry("ReversedException", false); 0297 } 0298 0299 QString KProtocolManagerPrivate::readNoProxyFor() 0300 { 0301 QString noProxy = config()->group("Proxy Settings").readEntry("NoProxyFor"); 0302 if (proxyType() == KProtocolManager::EnvVarProxy) { 0303 noProxy = QString::fromLocal8Bit(qgetenv(noProxy.toLocal8Bit().constData())); 0304 } 0305 return noProxy; 0306 } 0307 0308 QMap<QString, QString> KProtocolManager::entryMap(const QString &group) 0309 { 0310 KProtocolManagerPrivate *d = kProtocolManagerPrivate(); 0311 QMutexLocker lock(&d->mutex); 0312 return config()->entryMap(group); 0313 } 0314 0315 static KConfigGroup http_config() 0316 { 0317 KProtocolManagerPrivate *d = kProtocolManagerPrivate(); 0318 Q_ASSERT(!d->mutex.tryLock()); // the caller must have locked the mutex 0319 if (!d->http_config) { 0320 d->http_config = KSharedConfig::openConfig(QStringLiteral("kio_httprc"), KConfig::NoGlobals); 0321 } 0322 return KConfigGroup(d->http_config, QString()); 0323 } 0324 0325 /*=============================== TIMEOUT SETTINGS ==========================*/ 0326 0327 int KProtocolManager::readTimeout() 0328 { 0329 KProtocolManagerPrivate *d = kProtocolManagerPrivate(); 0330 QMutexLocker lock(&d->mutex); 0331 KConfigGroup cg(config(), QString()); 0332 int val = cg.readEntry("ReadTimeout", DEFAULT_READ_TIMEOUT); 0333 return qMax(MIN_TIMEOUT_VALUE, val); 0334 } 0335 0336 int KProtocolManager::connectTimeout() 0337 { 0338 KProtocolManagerPrivate *d = kProtocolManagerPrivate(); 0339 QMutexLocker lock(&d->mutex); 0340 KConfigGroup cg(config(), QString()); 0341 int val = cg.readEntry("ConnectTimeout", DEFAULT_CONNECT_TIMEOUT); 0342 return qMax(MIN_TIMEOUT_VALUE, val); 0343 } 0344 0345 int KProtocolManager::proxyConnectTimeout() 0346 { 0347 KProtocolManagerPrivate *d = kProtocolManagerPrivate(); 0348 QMutexLocker lock(&d->mutex); 0349 KConfigGroup cg(config(), QString()); 0350 int val = cg.readEntry("ProxyConnectTimeout", DEFAULT_PROXY_CONNECT_TIMEOUT); 0351 return qMax(MIN_TIMEOUT_VALUE, val); 0352 } 0353 0354 int KProtocolManager::responseTimeout() 0355 { 0356 KProtocolManagerPrivate *d = kProtocolManagerPrivate(); 0357 QMutexLocker lock(&d->mutex); 0358 KConfigGroup cg(config(), QString()); 0359 int val = cg.readEntry("ResponseTimeout", DEFAULT_RESPONSE_TIMEOUT); 0360 return qMax(MIN_TIMEOUT_VALUE, val); 0361 } 0362 0363 /*========================== PROXY SETTINGS =================================*/ 0364 0365 bool KProtocolManager::useProxy() 0366 { 0367 return proxyType() != NoProxy; 0368 } 0369 0370 bool KProtocolManager::useReverseProxy() 0371 { 0372 KProtocolManagerPrivate *d = kProtocolManagerPrivate(); 0373 QMutexLocker lock(&d->mutex); 0374 return d->useReverseProxy(); 0375 } 0376 0377 KProtocolManager::ProxyType KProtocolManager::proxyType() 0378 { 0379 KProtocolManagerPrivate *d = kProtocolManagerPrivate(); 0380 QMutexLocker lock(&d->mutex); 0381 return d->proxyType(); 0382 } 0383 0384 KProtocolManager::ProxyAuthMode KProtocolManager::proxyAuthMode() 0385 { 0386 KProtocolManagerPrivate *d = kProtocolManagerPrivate(); 0387 QMutexLocker lock(&d->mutex); 0388 KConfigGroup cg(config(), "Proxy Settings"); 0389 return static_cast<ProxyAuthMode>(cg.readEntry("AuthMode", 0)); 0390 } 0391 0392 /*========================== CACHING =====================================*/ 0393 0394 bool KProtocolManager::useCache() 0395 { 0396 KProtocolManagerPrivate *d = kProtocolManagerPrivate(); 0397 QMutexLocker lock(&d->mutex); 0398 return http_config().readEntry("UseCache", true); 0399 } 0400 0401 KIO::CacheControl KProtocolManager::cacheControl() 0402 { 0403 KProtocolManagerPrivate *d = kProtocolManagerPrivate(); 0404 QMutexLocker lock(&d->mutex); 0405 QString tmp = http_config().readEntry("cache"); 0406 if (tmp.isEmpty()) { 0407 return DEFAULT_CACHE_CONTROL; 0408 } 0409 return KIO::parseCacheControl(tmp); 0410 } 0411 0412 QString KProtocolManager::cacheDir() 0413 { 0414 KProtocolManagerPrivate *d = kProtocolManagerPrivate(); 0415 QMutexLocker lock(&d->mutex); 0416 return http_config().readPathEntry("CacheDir", QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QLatin1String("/kio_http")); 0417 } 0418 0419 int KProtocolManager::maxCacheAge() 0420 { 0421 KProtocolManagerPrivate *d = kProtocolManagerPrivate(); 0422 QMutexLocker lock(&d->mutex); 0423 return http_config().readEntry("MaxCacheAge", DEFAULT_MAX_CACHE_AGE); 0424 } 0425 0426 int KProtocolManager::maxCacheSize() 0427 { 0428 KProtocolManagerPrivate *d = kProtocolManagerPrivate(); 0429 QMutexLocker lock(&d->mutex); 0430 return http_config().readEntry("MaxCacheSize", DEFAULT_MAX_CACHE_SIZE); 0431 } 0432 0433 QString KProtocolManager::noProxyFor() 0434 { 0435 KProtocolManagerPrivate *d = kProtocolManagerPrivate(); 0436 QMutexLocker lock(&d->mutex); 0437 return d->readNoProxyFor(); 0438 } 0439 0440 static QString adjustProtocol(const QString &scheme) 0441 { 0442 if (scheme.compare(QLatin1String("webdav"), Qt::CaseInsensitive) == 0) { 0443 return QStringLiteral("http"); 0444 } 0445 0446 if (scheme.compare(QLatin1String("webdavs"), Qt::CaseInsensitive) == 0) { 0447 return QStringLiteral("https"); 0448 } 0449 0450 return scheme.toLower(); 0451 } 0452 0453 QString KProtocolManager::proxyFor(const QString &protocol) 0454 { 0455 KProtocolManagerPrivate *d = kProtocolManagerPrivate(); 0456 QMutexLocker lock(&d->mutex); 0457 return d->proxyFor(protocol); 0458 } 0459 0460 QString KProtocolManagerPrivate::proxyFor(const QString &protocol) 0461 { 0462 const QString key = adjustProtocol(protocol) + QLatin1String("Proxy"); 0463 QString proxyStr(config()->group("Proxy Settings").readEntry(key)); 0464 const int index = proxyStr.lastIndexOf(QLatin1Char(' ')); 0465 0466 if (index > -1) { 0467 const QStringView portStr = QStringView(proxyStr).right(proxyStr.length() - index - 1); 0468 const bool isDigits = std::all_of(portStr.cbegin(), portStr.cend(), [](const QChar c) { 0469 return c.isDigit(); 0470 }); 0471 0472 if (isDigits) { 0473 proxyStr = QStringView(proxyStr).left(index) + QLatin1Char(':') + portStr; 0474 } else { 0475 proxyStr.clear(); 0476 } 0477 } 0478 0479 return proxyStr; 0480 } 0481 0482 QString KProtocolManager::proxyForUrl(const QUrl &url) 0483 { 0484 const QStringList proxies = proxiesForUrl(url); 0485 0486 if (proxies.isEmpty()) { 0487 return QString(); 0488 } 0489 0490 return proxies.first(); 0491 } 0492 0493 QStringList KProtocolManagerPrivate::getSystemProxyFor(const QUrl &url) 0494 { 0495 QStringList proxies; 0496 0497 #if !defined(QT_NO_NETWORKPROXY) && (defined(Q_OS_WIN32) || defined(Q_OS_MAC)) 0498 QNetworkProxyQuery query(url); 0499 const QList<QNetworkProxy> proxyList = QNetworkProxyFactory::systemProxyForQuery(query); 0500 proxies.reserve(proxyList.size()); 0501 for (const QNetworkProxy &proxy : proxyList) { 0502 QUrl url; 0503 const QNetworkProxy::ProxyType type = proxy.type(); 0504 if (type == QNetworkProxy::NoProxy || type == QNetworkProxy::DefaultProxy) { 0505 proxies << QLatin1String("DIRECT"); 0506 continue; 0507 } 0508 0509 if (type == QNetworkProxy::HttpProxy || type == QNetworkProxy::HttpCachingProxy) { 0510 url.setScheme(QLatin1String("http")); 0511 } else if (type == QNetworkProxy::Socks5Proxy) { 0512 url.setScheme(QLatin1String("socks")); 0513 } else if (type == QNetworkProxy::FtpCachingProxy) { 0514 url.setScheme(QLatin1String("ftp")); 0515 } 0516 0517 url.setHost(proxy.hostName()); 0518 url.setPort(proxy.port()); 0519 url.setUserName(proxy.user()); 0520 proxies << url.url(); 0521 } 0522 #else 0523 // On Unix/Linux use system environment variables if any are set. 0524 QString proxyVar(proxyFor(url.scheme())); 0525 // Check for SOCKS proxy, if not proxy is found for given url. 0526 if (!proxyVar.isEmpty()) { 0527 const QString proxy(QString::fromLocal8Bit(qgetenv(proxyVar.toLocal8Bit().constData())).trimmed()); 0528 if (!proxy.isEmpty()) { 0529 proxies << proxy; 0530 } 0531 } 0532 // Add the socks proxy as an alternate proxy if it exists, 0533 proxyVar = proxyFor(QStringLiteral("socks")); 0534 if (!proxyVar.isEmpty()) { 0535 QString proxy = QString::fromLocal8Bit(qgetenv(proxyVar.toLocal8Bit().constData())).trimmed(); 0536 // Make sure the scheme of SOCKS proxy is always set to "socks://". 0537 const int index = proxy.indexOf(QLatin1String("://")); 0538 const int offset = (index == -1) ? 0 : (index + 3); 0539 proxy = QLatin1String("socks://") + QStringView(proxy).mid(offset); 0540 if (!proxy.isEmpty()) { 0541 proxies << proxy; 0542 } 0543 } 0544 #endif 0545 return proxies; 0546 } 0547 0548 QStringList KProtocolManager::proxiesForUrl(const QUrl &url) 0549 { 0550 QStringList proxyList; 0551 0552 KProtocolManagerPrivate *d = kProtocolManagerPrivate(); 0553 QMutexLocker lock(&d->mutex); 0554 if (!d->shouldIgnoreProxyFor(url)) { 0555 switch (d->proxyType()) { 0556 case PACProxy: 0557 case WPADProxy: { 0558 QUrl u(url); 0559 const QString protocol = adjustProtocol(u.scheme()); 0560 u.setScheme(protocol); 0561 0562 #ifndef KIO_ANDROID_STUB 0563 if (protocol.startsWith(QLatin1String("http")) || protocol.startsWith(QLatin1String("ftp"))) { 0564 QDBusReply<QStringList> reply = 0565 QDBusInterface(QStringLiteral("org.kde.kded5"), QStringLiteral("/modules/proxyscout"), QStringLiteral("org.kde.KPAC.ProxyScout")) 0566 .call(QStringLiteral("proxiesForUrl"), u.toString()); 0567 proxyList = reply; 0568 } 0569 #endif 0570 break; 0571 } 0572 case EnvVarProxy: 0573 proxyList = d->getSystemProxyFor(url); 0574 break; 0575 case ManualProxy: { 0576 QString proxy(d->proxyFor(url.scheme())); 0577 if (!proxy.isEmpty()) { 0578 proxyList << proxy; 0579 } 0580 // Add the socks proxy as an alternate proxy if it exists, 0581 proxy = d->proxyFor(QStringLiteral("socks")); 0582 if (!proxy.isEmpty()) { 0583 // Make sure the scheme of SOCKS proxy is always set to "socks://". 0584 const int index = proxy.indexOf(QLatin1String("://")); 0585 const int offset = (index == -1) ? 0 : (index + 3); 0586 proxy = QLatin1String("socks://") + QStringView(proxy).mid(offset); 0587 proxyList << proxy; 0588 } 0589 break; 0590 } 0591 case NoProxy: 0592 break; 0593 } 0594 } 0595 0596 if (proxyList.isEmpty()) { 0597 proxyList << QStringLiteral("DIRECT"); 0598 } 0599 0600 return proxyList; 0601 } 0602 0603 void KProtocolManager::badProxy(const QString &proxy) 0604 { 0605 #ifndef KIO_ANDROID_STUB 0606 QDBusInterface(QStringLiteral("org.kde.kded5"), QStringLiteral("/modules/proxyscout")).asyncCall(QStringLiteral("blackListProxy"), proxy); 0607 #endif 0608 0609 KProtocolManagerPrivate *d = kProtocolManagerPrivate(); 0610 QMutexLocker lock(&d->mutex); 0611 const QStringList keys(d->cachedProxyData.keys()); 0612 for (const QString &key : keys) { 0613 d->cachedProxyData[key]->removeAddress(proxy); 0614 } 0615 } 0616 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 101) 0617 QString KProtocolManager::slaveProtocol(const QUrl &url, QString &proxy) 0618 { 0619 return workerProtocol(url, proxy); 0620 } 0621 #endif 0622 0623 QString KProtocolManager::workerProtocol(const QUrl &url, QString &proxy) 0624 { 0625 QStringList proxyList; 0626 const QString protocol = KProtocolManager::workerProtocol(url, proxyList); 0627 if (!proxyList.isEmpty()) { 0628 proxy = proxyList.first(); 0629 } 0630 return protocol; 0631 } 0632 0633 // Generates proxy cache key from request given url. 0634 static QString extractProxyCacheKeyFromUrl(const QUrl &u) 0635 { 0636 QString key = u.scheme(); 0637 key += u.host(); 0638 0639 if (u.port() > 0) { 0640 key += QString::number(u.port()); 0641 } 0642 return key; 0643 } 0644 0645 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 101) 0646 QString KProtocolManager::slaveProtocol(const QUrl &url, QStringList &proxyList) 0647 { 0648 return workerProtocol(url, proxyList); 0649 } 0650 #endif 0651 0652 QString KProtocolManager::workerProtocol(const QUrl &url, QStringList &proxyList) 0653 { 0654 proxyList.clear(); 0655 0656 // Do not perform a proxy lookup for any url classified as a ":local" url or 0657 // one that does not have a host component or if proxy is disabled. 0658 QString protocol(url.scheme()); 0659 if (url.host().isEmpty() || KProtocolInfo::protocolClass(protocol) == QLatin1String(":local") 0660 || KProtocolManager::proxyType() == KProtocolManager::NoProxy) { 0661 return protocol; 0662 } 0663 0664 const QString proxyCacheKey = extractProxyCacheKeyFromUrl(url); 0665 0666 KProtocolManagerPrivate *d = kProtocolManagerPrivate(); 0667 QMutexLocker lock(&d->mutex); 0668 // Look for cached proxy information to avoid more work. 0669 if (d->cachedProxyData.contains(proxyCacheKey)) { 0670 KProxyData *data = d->cachedProxyData.object(proxyCacheKey); 0671 proxyList = data->proxyList; 0672 return data->protocol; 0673 } 0674 lock.unlock(); 0675 0676 const QStringList proxies = proxiesForUrl(url); 0677 const int count = proxies.count(); 0678 0679 if (count > 0 && !(count == 1 && proxies.first() == QLatin1String("DIRECT"))) { 0680 for (const QString &proxy : proxies) { 0681 if (proxy == QLatin1String("DIRECT")) { 0682 proxyList << proxy; 0683 } else { 0684 QUrl u(proxy); 0685 if (!u.isEmpty() && u.isValid() && !u.scheme().isEmpty()) { 0686 proxyList << proxy; 0687 } 0688 } 0689 } 0690 } 0691 0692 // The idea behind worker protocols is not applicable to http 0693 // and webdav protocols as well as protocols unknown to KDE. 0694 /* clang-format off */ 0695 if (!proxyList.isEmpty() 0696 && !protocol.startsWith(QLatin1String("http")) 0697 && !protocol.startsWith(QLatin1String("webdav")) 0698 && KProtocolInfo::isKnownProtocol(protocol)) { /* clang-format on */ 0699 for (const QString &proxy : std::as_const(proxyList)) { 0700 QUrl u(proxy); 0701 if (u.isValid() && KProtocolInfo::isKnownProtocol(u.scheme())) { 0702 protocol = u.scheme(); 0703 break; 0704 } 0705 } 0706 } 0707 0708 lock.relock(); 0709 // cache the proxy information... 0710 d->cachedProxyData.insert(proxyCacheKey, new KProxyData(protocol, proxyList)); 0711 return protocol; 0712 } 0713 0714 /*================================= USER-AGENT SETTINGS =====================*/ 0715 0716 QString KProtocolManager::userAgentForHost(const QString &hostname) 0717 { 0718 const QString sendUserAgent = KIO::WorkerConfig::self()->configData(QStringLiteral("http"), hostname.toLower(), QStringLiteral("SendUserAgent")).toLower(); 0719 if (sendUserAgent == QLatin1String("false")) { 0720 return QString(); 0721 } 0722 0723 const QString useragent = KIO::WorkerConfig::self()->configData(QStringLiteral("http"), hostname.toLower(), QStringLiteral("UserAgent")); 0724 0725 // Return the default user-agent if none is specified 0726 // for the requested host. 0727 if (useragent.isEmpty()) { 0728 return defaultUserAgent(); 0729 } 0730 0731 return useragent; 0732 } 0733 0734 QString KProtocolManager::defaultUserAgent() 0735 { 0736 const QString modifiers = KIO::WorkerConfig::self()->configData(QStringLiteral("http"), QString(), QStringLiteral("UserAgentKeys")); 0737 return defaultUserAgent(modifiers); 0738 } 0739 0740 // This is not the OS, but the windowing system, e.g. X11 on Unix/Linux. 0741 static QString platform() 0742 { 0743 #if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) 0744 return QStringLiteral("X11"); 0745 #elif defined(Q_OS_MAC) 0746 return QStringLiteral("Macintosh"); 0747 #elif defined(Q_OS_WIN) 0748 return QStringLiteral("Windows"); 0749 #else 0750 return QStringLiteral("Unknown"); 0751 #endif 0752 } 0753 0754 QString KProtocolManager::defaultUserAgent(const QString &_modifiers) 0755 { 0756 KProtocolManagerPrivate *d = kProtocolManagerPrivate(); 0757 QMutexLocker lock(&d->mutex); 0758 QString modifiers = _modifiers.toLower(); 0759 if (modifiers.isEmpty()) { 0760 modifiers = QString::fromLatin1(DEFAULT_USER_AGENT_KEYS); 0761 } 0762 0763 if (d->modifiers == modifiers && !d->useragent.isEmpty()) { 0764 return d->useragent; 0765 } 0766 0767 d->modifiers = modifiers; 0768 0769 QString systemName; 0770 QString systemVersion; 0771 QString machine; 0772 QString supp; 0773 const bool sysInfoFound = getSystemNameVersionAndMachine(systemName, systemVersion, machine); 0774 0775 supp += platform(); 0776 0777 if (sysInfoFound) { 0778 if (modifiers.contains(QLatin1Char('o'))) { 0779 supp += QLatin1String("; ") + systemName; 0780 if (modifiers.contains(QLatin1Char('v'))) { 0781 supp += QLatin1Char(' ') + systemVersion; 0782 } 0783 0784 if (modifiers.contains(QLatin1Char('m'))) { 0785 supp += QLatin1Char(' ') + machine; 0786 } 0787 } 0788 0789 if (modifiers.contains(QLatin1Char('l'))) { 0790 supp += QLatin1String("; ") + QLocale::languageToString(QLocale().language()); 0791 } 0792 } 0793 0794 QString appName = QCoreApplication::applicationName(); 0795 if (appName.isEmpty() || appName.startsWith(QLatin1String("kcmshell"), Qt::CaseInsensitive)) { 0796 appName = QStringLiteral("KDE"); 0797 } 0798 QString appVersion = QCoreApplication::applicationVersion(); 0799 if (appVersion.isEmpty()) { 0800 appVersion += QLatin1String(KIO_VERSION_STRING); 0801 } 0802 0803 d->useragent = QLatin1String("Mozilla/5.0 (%1) ").arg(supp) 0804 + QLatin1String("KIO/%1.%2 ").arg(QString::number(KIO_VERSION_MAJOR), QString::number(KIO_VERSION_MINOR)) 0805 + QLatin1String("%1/%2").arg(appName, appVersion); 0806 0807 // qDebug() << "USERAGENT STRING:" << d->useragent; 0808 return d->useragent; 0809 } 0810 0811 QString KProtocolManager::userAgentForApplication(const QString &appName, const QString &appVersion, const QStringList &extraInfo) 0812 { 0813 QString systemName; 0814 QString systemVersion; 0815 QString machine; 0816 QString info; 0817 0818 if (getSystemNameVersionAndMachine(systemName, systemVersion, machine)) { 0819 info += systemName + QLatin1Char('/') + systemVersion + QLatin1String("; "); 0820 } 0821 0822 info += QLatin1String("KDE/") + QStringLiteral(KIO_VERSION_STRING); 0823 0824 if (!machine.isEmpty()) { 0825 info += QLatin1String("; ") + machine; 0826 } 0827 0828 info += QLatin1String("; ") + extraInfo.join(QLatin1String("; ")); 0829 0830 return (appName + QLatin1Char('/') + appVersion + QStringLiteral(" (") + info + QLatin1Char(')')); 0831 } 0832 0833 bool KProtocolManager::getSystemNameVersionAndMachine(QString &systemName, QString &systemVersion, QString &machine) 0834 { 0835 #if defined(Q_OS_WIN) && !defined(_WIN32_WCE) 0836 // we do not use unameBuf.sysname information constructed in kdewin32 0837 // because we want to get separate name and version 0838 systemName = QStringLiteral("Windows"); 0839 OSVERSIONINFOEX versioninfo; 0840 ZeroMemory(&versioninfo, sizeof(OSVERSIONINFOEX)); 0841 // try calling GetVersionEx using the OSVERSIONINFOEX, if that fails, try using the OSVERSIONINFO 0842 versioninfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); 0843 bool ok = GetVersionEx((OSVERSIONINFO *)&versioninfo); 0844 if (!ok) { 0845 versioninfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); 0846 ok = GetVersionEx((OSVERSIONINFO *)&versioninfo); 0847 } 0848 if (ok) { 0849 systemVersion = QString::number(versioninfo.dwMajorVersion); 0850 systemVersion += QLatin1Char('.'); 0851 systemVersion += QString::number(versioninfo.dwMinorVersion); 0852 } 0853 #else 0854 struct utsname unameBuf; 0855 if (0 != uname(&unameBuf)) { 0856 return false; 0857 } 0858 systemName = QString::fromUtf8(unameBuf.sysname); 0859 systemVersion = QString::fromUtf8(unameBuf.release); 0860 machine = QString::fromUtf8(unameBuf.machine); 0861 #endif 0862 return true; 0863 } 0864 0865 QString KProtocolManager::acceptLanguagesHeader() 0866 { 0867 const QLatin1String english("en"); 0868 0869 // User's desktop language preference. 0870 QStringList languageList = QLocale().uiLanguages(); 0871 0872 // Replace possible "C" in the language list with "en", unless "en" is 0873 // already present. This is to keep user's priorities in order. 0874 // If afterwards "en" is still not present, append it. 0875 int idx = languageList.indexOf(QLatin1String("C")); 0876 if (idx != -1) { 0877 if (languageList.contains(english)) { 0878 languageList.removeAt(idx); 0879 } else { 0880 languageList[idx] = english; 0881 } 0882 } 0883 if (!languageList.contains(english)) { 0884 languageList += english; 0885 } 0886 0887 // Some languages may have web codes different from locale codes, 0888 // read them from the config and insert in proper order. 0889 KConfig acclangConf(QStringLiteral("accept-languages.codes"), KConfig::NoGlobals); 0890 KConfigGroup replacementCodes(&acclangConf, "ReplacementCodes"); 0891 QStringList languageListFinal; 0892 for (const QString &lang : std::as_const(languageList)) { 0893 const QStringList langs = replacementCodes.readEntry(lang, QStringList()); 0894 if (langs.isEmpty()) { 0895 languageListFinal += lang; 0896 } else { 0897 languageListFinal += langs; 0898 } 0899 } 0900 0901 // The header is composed of comma separated languages, with an optional 0902 // associated priority estimate (q=1..0) defaulting to 1. 0903 // As our language tags are already sorted by priority, we'll just decrease 0904 // the value evenly 0905 int prio = 10; 0906 QString header; 0907 for (const QString &lang : std::as_const(languageListFinal)) { 0908 header += lang; 0909 if (prio < 10) { 0910 header += QLatin1String(";q=0.") + QString::number(prio); 0911 } 0912 // do not add cosmetic whitespace in here : it is less compatible (#220677) 0913 header += QLatin1Char(','); 0914 if (prio > 1) { 0915 --prio; 0916 } 0917 } 0918 header.chop(1); 0919 0920 // Some of the languages may have country specifier delimited by 0921 // underscore, or modifier delimited by at-sign. 0922 // The header should use dashes instead. 0923 header.replace(QLatin1Char('_'), QLatin1Char('-')); 0924 header.replace(QLatin1Char('@'), QLatin1Char('-')); 0925 0926 return header; 0927 } 0928 0929 /*==================================== OTHERS ===============================*/ 0930 0931 bool KProtocolManager::markPartial() 0932 { 0933 KProtocolManagerPrivate *d = kProtocolManagerPrivate(); 0934 QMutexLocker lock(&d->mutex); 0935 return config()->group(QByteArray()).readEntry("MarkPartial", true); 0936 } 0937 0938 int KProtocolManager::minimumKeepSize() 0939 { 0940 KProtocolManagerPrivate *d = kProtocolManagerPrivate(); 0941 QMutexLocker lock(&d->mutex); 0942 return config()->group(QByteArray()).readEntry("MinimumKeepSize", 0943 DEFAULT_MINIMUM_KEEP_SIZE); // 5000 byte 0944 } 0945 0946 bool KProtocolManager::autoResume() 0947 { 0948 KProtocolManagerPrivate *d = kProtocolManagerPrivate(); 0949 QMutexLocker lock(&d->mutex); 0950 return config()->group(QByteArray()).readEntry("AutoResume", false); 0951 } 0952 0953 bool KProtocolManager::persistentConnections() 0954 { 0955 KProtocolManagerPrivate *d = kProtocolManagerPrivate(); 0956 QMutexLocker lock(&d->mutex); 0957 return config()->group(QByteArray()).readEntry("PersistentConnections", true); 0958 } 0959 0960 bool KProtocolManager::persistentProxyConnection() 0961 { 0962 KProtocolManagerPrivate *d = kProtocolManagerPrivate(); 0963 QMutexLocker lock(&d->mutex); 0964 return config()->group(QByteArray()).readEntry("PersistentProxyConnection", false); 0965 } 0966 0967 QString KProtocolManager::proxyConfigScript() 0968 { 0969 KProtocolManagerPrivate *d = kProtocolManagerPrivate(); 0970 QMutexLocker lock(&d->mutex); 0971 return config()->group("Proxy Settings").readEntry("Proxy Config Script"); 0972 } 0973 0974 /* =========================== PROTOCOL CAPABILITIES ============== */ 0975 0976 static KProtocolInfoPrivate *findProtocol(const QUrl &url) 0977 { 0978 if (!url.isValid()) { 0979 return nullptr; 0980 } 0981 QString protocol = url.scheme(); 0982 if (!KProtocolInfo::proxiedBy(protocol).isEmpty()) { 0983 QString dummy; 0984 protocol = KProtocolManager::workerProtocol(url, dummy); 0985 } 0986 0987 return KProtocolInfoFactory::self()->findProtocol(protocol); 0988 } 0989 0990 KProtocolInfo::Type KProtocolManager::inputType(const QUrl &url) 0991 { 0992 KProtocolInfoPrivate *prot = findProtocol(url); 0993 if (!prot) { 0994 return KProtocolInfo::T_NONE; 0995 } 0996 0997 return prot->m_inputType; 0998 } 0999 1000 KProtocolInfo::Type KProtocolManager::outputType(const QUrl &url) 1001 { 1002 KProtocolInfoPrivate *prot = findProtocol(url); 1003 if (!prot) { 1004 return KProtocolInfo::T_NONE; 1005 } 1006 1007 return prot->m_outputType; 1008 } 1009 1010 bool KProtocolManager::isSourceProtocol(const QUrl &url) 1011 { 1012 KProtocolInfoPrivate *prot = findProtocol(url); 1013 if (!prot) { 1014 return false; 1015 } 1016 1017 return prot->m_isSourceProtocol; 1018 } 1019 1020 bool KProtocolManager::supportsListing(const QUrl &url) 1021 { 1022 KProtocolInfoPrivate *prot = findProtocol(url); 1023 if (!prot) { 1024 return false; 1025 } 1026 1027 return prot->m_supportsListing; 1028 } 1029 1030 QStringList KProtocolManager::listing(const QUrl &url) 1031 { 1032 KProtocolInfoPrivate *prot = findProtocol(url); 1033 if (!prot) { 1034 return QStringList(); 1035 } 1036 1037 return prot->m_listing; 1038 } 1039 1040 bool KProtocolManager::supportsReading(const QUrl &url) 1041 { 1042 KProtocolInfoPrivate *prot = findProtocol(url); 1043 if (!prot) { 1044 return false; 1045 } 1046 1047 return prot->m_supportsReading; 1048 } 1049 1050 bool KProtocolManager::supportsWriting(const QUrl &url) 1051 { 1052 KProtocolInfoPrivate *prot = findProtocol(url); 1053 if (!prot) { 1054 return false; 1055 } 1056 1057 return prot->m_supportsWriting; 1058 } 1059 1060 bool KProtocolManager::supportsMakeDir(const QUrl &url) 1061 { 1062 KProtocolInfoPrivate *prot = findProtocol(url); 1063 if (!prot) { 1064 return false; 1065 } 1066 1067 return prot->m_supportsMakeDir; 1068 } 1069 1070 bool KProtocolManager::supportsDeleting(const QUrl &url) 1071 { 1072 KProtocolInfoPrivate *prot = findProtocol(url); 1073 if (!prot) { 1074 return false; 1075 } 1076 1077 return prot->m_supportsDeleting; 1078 } 1079 1080 bool KProtocolManager::supportsLinking(const QUrl &url) 1081 { 1082 KProtocolInfoPrivate *prot = findProtocol(url); 1083 if (!prot) { 1084 return false; 1085 } 1086 1087 return prot->m_supportsLinking; 1088 } 1089 1090 bool KProtocolManager::supportsMoving(const QUrl &url) 1091 { 1092 KProtocolInfoPrivate *prot = findProtocol(url); 1093 if (!prot) { 1094 return false; 1095 } 1096 1097 return prot->m_supportsMoving; 1098 } 1099 1100 bool KProtocolManager::supportsOpening(const QUrl &url) 1101 { 1102 KProtocolInfoPrivate *prot = findProtocol(url); 1103 if (!prot) { 1104 return false; 1105 } 1106 1107 return prot->m_supportsOpening; 1108 } 1109 1110 bool KProtocolManager::supportsTruncating(const QUrl &url) 1111 { 1112 KProtocolInfoPrivate *prot = findProtocol(url); 1113 if (!prot) { 1114 return false; 1115 } 1116 1117 return prot->m_supportsTruncating; 1118 } 1119 1120 bool KProtocolManager::canCopyFromFile(const QUrl &url) 1121 { 1122 KProtocolInfoPrivate *prot = findProtocol(url); 1123 if (!prot) { 1124 return false; 1125 } 1126 1127 return prot->m_canCopyFromFile; 1128 } 1129 1130 bool KProtocolManager::canCopyToFile(const QUrl &url) 1131 { 1132 KProtocolInfoPrivate *prot = findProtocol(url); 1133 if (!prot) { 1134 return false; 1135 } 1136 1137 return prot->m_canCopyToFile; 1138 } 1139 1140 bool KProtocolManager::canRenameFromFile(const QUrl &url) 1141 { 1142 KProtocolInfoPrivate *prot = findProtocol(url); 1143 if (!prot) { 1144 return false; 1145 } 1146 1147 return prot->m_canRenameFromFile; 1148 } 1149 1150 bool KProtocolManager::canRenameToFile(const QUrl &url) 1151 { 1152 KProtocolInfoPrivate *prot = findProtocol(url); 1153 if (!prot) { 1154 return false; 1155 } 1156 1157 return prot->m_canRenameToFile; 1158 } 1159 1160 bool KProtocolManager::canDeleteRecursive(const QUrl &url) 1161 { 1162 KProtocolInfoPrivate *prot = findProtocol(url); 1163 if (!prot) { 1164 return false; 1165 } 1166 1167 return prot->m_canDeleteRecursive; 1168 } 1169 1170 KProtocolInfo::FileNameUsedForCopying KProtocolManager::fileNameUsedForCopying(const QUrl &url) 1171 { 1172 KProtocolInfoPrivate *prot = findProtocol(url); 1173 if (!prot) { 1174 return KProtocolInfo::FromUrl; 1175 } 1176 1177 return prot->m_fileNameUsedForCopying; 1178 } 1179 1180 QString KProtocolManager::defaultMimetype(const QUrl &url) 1181 { 1182 KProtocolInfoPrivate *prot = findProtocol(url); 1183 if (!prot) { 1184 return QString(); 1185 } 1186 1187 return prot->m_defaultMimetype; 1188 } 1189 1190 QString KProtocolManager::protocolForArchiveMimetype(const QString &mimeType) 1191 { 1192 KProtocolManagerPrivate *d = kProtocolManagerPrivate(); 1193 QMutexLocker lock(&d->mutex); 1194 if (d->protocolForArchiveMimetypes.isEmpty()) { 1195 const QList<KProtocolInfoPrivate *> allProtocols = KProtocolInfoFactory::self()->allProtocols(); 1196 for (KProtocolInfoPrivate *allProtocol : allProtocols) { 1197 const QStringList archiveMimetypes = allProtocol->m_archiveMimeTypes; 1198 for (const QString &mime : archiveMimetypes) { 1199 d->protocolForArchiveMimetypes.insert(mime, allProtocol->m_name); 1200 } 1201 } 1202 } 1203 return d->protocolForArchiveMimetypes.value(mimeType); 1204 } 1205 1206 QString KProtocolManager::charsetFor(const QUrl &url) 1207 { 1208 return KIO::WorkerConfig::self()->configData(url.scheme(), url.host(), QStringLiteral("Charset")); 1209 } 1210 1211 bool KProtocolManager::supportsPermissions(const QUrl &url) 1212 { 1213 KProtocolInfoPrivate *prot = findProtocol(url); 1214 if (!prot) { 1215 return true; 1216 } 1217 1218 return prot->m_supportsPermissions; 1219 } 1220 1221 #undef PRIVATE_DATA 1222 1223 #include "kprotocolmanager.moc"