File indexing completed on 2024-04-28 11:41:03

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"