File indexing completed on 2024-04-28 07:43:53

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 #include "kprotocolmanager_p.h"
0015 
0016 #include "hostinfo.h"
0017 
0018 #include <config-kiocore.h>
0019 
0020 #include <qplatformdefs.h>
0021 #include <string.h>
0022 #ifdef Q_OS_WIN
0023 #include <qt_windows.h>
0024 #undef interface // windows.h defines this, breaks QtDBus since it has parameters named interface
0025 #else
0026 #include <sys/utsname.h>
0027 #endif
0028 
0029 #include <QCache>
0030 #include <QCoreApplication>
0031 #ifndef KIO_ANDROID_STUB
0032 #include <QDBusInterface>
0033 #include <QDBusReply>
0034 #endif
0035 #include <QHostAddress>
0036 #include <QHostInfo>
0037 #include <QLocale>
0038 #include <QMimeDatabase>
0039 #include <QRegularExpression>
0040 #include <QSslSocket>
0041 #include <QStandardPaths>
0042 #include <QUrl>
0043 
0044 #if !defined(QT_NO_NETWORKPROXY) && (defined(Q_OS_WIN32) || defined(Q_OS_MAC))
0045 #include <QNetworkProxyFactory>
0046 #include <QNetworkProxyQuery>
0047 #endif
0048 
0049 #include <KConfigGroup>
0050 #include <KSharedConfig>
0051 #include <kio_version.h>
0052 
0053 #include <kprotocolinfofactory_p.h>
0054 
0055 #include "ioworker_defaults.h"
0056 #include "workerconfig.h"
0057 
0058 /*
0059     Domain suffix match. E.g. return true if host is "cuzco.inka.de" and
0060     nplist is "inka.de,hadiko.de" or if host is "localhost" and nplist is
0061     "localhost".
0062 */
0063 static bool revmatch(const char *host, const char *nplist)
0064 {
0065     if (host == nullptr) {
0066         return false;
0067     }
0068 
0069     const char *hptr = host + strlen(host) - 1;
0070     const char *nptr = nplist + strlen(nplist) - 1;
0071     const char *shptr = hptr;
0072 
0073     while (nptr >= nplist) {
0074         if (*hptr != *nptr) {
0075             hptr = shptr;
0076 
0077             // Try to find another domain or host in the list
0078             while (--nptr >= nplist && *nptr != ',' && *nptr != ' ') {
0079                 ;
0080             }
0081 
0082             // Strip out multiple spaces and commas
0083             while (--nptr >= nplist && (*nptr == ',' || *nptr == ' ')) {
0084                 ;
0085             }
0086         } else {
0087             if (nptr == nplist || nptr[-1] == ',' || nptr[-1] == ' ') {
0088                 return true;
0089             }
0090             if (nptr[-1] == '/' && hptr == host) { // "bugs.kde.org" vs "http://bugs.kde.org", the config UI says URLs are ok
0091                 return true;
0092             }
0093             if (hptr == host) { // e.g. revmatch("bugs.kde.org","mybugs.kde.org")
0094                 return false;
0095             }
0096 
0097             hptr--;
0098             nptr--;
0099         }
0100     }
0101 
0102     return false;
0103 }
0104 
0105 Q_GLOBAL_STATIC(KProtocolManagerPrivate, kProtocolManagerPrivate)
0106 
0107 static void syncOnExit()
0108 {
0109     if (kProtocolManagerPrivate.exists()) {
0110         kProtocolManagerPrivate()->sync();
0111     }
0112 }
0113 
0114 KProtocolManagerPrivate::KProtocolManagerPrivate()
0115 {
0116     // post routine since KConfig::sync() breaks if called too late
0117     qAddPostRoutine(syncOnExit);
0118     cachedProxyData.setMaxCost(200); // double the max cost.
0119 }
0120 
0121 KProtocolManagerPrivate::~KProtocolManagerPrivate()
0122 {
0123 }
0124 
0125 /*
0126  * Returns true if url is in the no proxy list.
0127  */
0128 bool KProtocolManagerPrivate::shouldIgnoreProxyFor(const QUrl &url)
0129 {
0130     bool isMatch = false;
0131     const ProxyType type = proxyType();
0132     const bool useRevProxy = ((type == ManualProxy) && useReverseProxy());
0133     const bool useNoProxyList = (type == ManualProxy || type == EnvVarProxy);
0134 
0135     // No proxy only applies to ManualProxy and EnvVarProxy types...
0136     if (useNoProxyList && noProxyFor.isEmpty()) {
0137         QStringList noProxyForList(readNoProxyFor().split(QLatin1Char(',')));
0138         QMutableStringListIterator it(noProxyForList);
0139         while (it.hasNext()) {
0140             SubnetPair subnet = QHostAddress::parseSubnet(it.next());
0141             if (!subnet.first.isNull()) {
0142                 noProxySubnets << subnet;
0143                 it.remove();
0144             }
0145         }
0146         noProxyFor = noProxyForList.join(QLatin1Char(','));
0147     }
0148 
0149     if (!noProxyFor.isEmpty()) {
0150         QString qhost = url.host().toLower();
0151         QByteArray host = qhost.toLatin1();
0152         const QString qno_proxy = noProxyFor.trimmed().toLower();
0153         const QByteArray no_proxy = qno_proxy.toLatin1();
0154         isMatch = revmatch(host.constData(), no_proxy.constData());
0155 
0156         // If no match is found and the request url has a port
0157         // number, try the combination of "host:port". This allows
0158         // users to enter host:port in the No-proxy-For list.
0159         if (!isMatch && url.port() > 0) {
0160             qhost += QLatin1Char(':') + QString::number(url.port());
0161             host = qhost.toLatin1();
0162             isMatch = revmatch(host.constData(), no_proxy.constData());
0163         }
0164 
0165         // If the hostname does not contain a dot, check if
0166         // <local> is part of noProxy.
0167         if (!isMatch && !host.isEmpty() && (strchr(host.constData(), '.') == nullptr)) {
0168             isMatch = revmatch("<local>", no_proxy.constData());
0169         }
0170     }
0171 
0172     const QString host(url.host());
0173 
0174     if (!noProxySubnets.isEmpty() && !host.isEmpty()) {
0175         QHostAddress address(host);
0176         // If request url is not IP address, do a DNS lookup of the hostname.
0177         // TODO: Perhaps we should make configurable ?
0178         if (address.isNull()) {
0179             // qDebug() << "Performing DNS lookup for" << host;
0180             QHostInfo info = KIO::HostInfo::lookupHost(host, 2000);
0181             const QList<QHostAddress> addresses = info.addresses();
0182             if (!addresses.isEmpty()) {
0183                 address = addresses.first();
0184             }
0185         }
0186 
0187         if (!address.isNull()) {
0188             for (const SubnetPair &subnet : std::as_const(noProxySubnets)) {
0189                 if (address.isInSubnet(subnet)) {
0190                     isMatch = true;
0191                     break;
0192                 }
0193             }
0194         }
0195     }
0196 
0197     return (useRevProxy != isMatch);
0198 }
0199 
0200 void KProtocolManagerPrivate::sync()
0201 {
0202     QMutexLocker lock(&mutex);
0203     if (http_config) {
0204         http_config->sync();
0205     }
0206     if (configPtr) {
0207         configPtr->sync();
0208     }
0209 }
0210 
0211 void KProtocolManager::reparseConfiguration()
0212 {
0213     KProtocolManagerPrivate *d = kProtocolManagerPrivate();
0214     QMutexLocker lock(&d->mutex);
0215     if (d->http_config) {
0216         d->http_config->reparseConfiguration();
0217     }
0218     if (d->configPtr) {
0219         d->configPtr->reparseConfiguration();
0220     }
0221     d->cachedProxyData.clear();
0222     d->noProxyFor.clear();
0223     d->modifiers.clear();
0224     d->useragent.clear();
0225     lock.unlock();
0226 
0227     // Force the slave config to re-read its config...
0228     KIO::WorkerConfig::self()->reset();
0229 }
0230 
0231 static KSharedConfig::Ptr config()
0232 {
0233     KProtocolManagerPrivate *d = kProtocolManagerPrivate();
0234     Q_ASSERT(!d->mutex.tryLock()); // the caller must have locked the mutex
0235     if (!d->configPtr) {
0236         d->configPtr = KSharedConfig::openConfig(QStringLiteral("kioslaverc"), KConfig::NoGlobals);
0237     }
0238     return d->configPtr;
0239 }
0240 
0241 KProtocolManagerPrivate::ProxyType KProtocolManagerPrivate::proxyType()
0242 {
0243     KConfigGroup cg(config(), QStringLiteral("Proxy Settings"));
0244     return static_cast<ProxyType>(cg.readEntry("ProxyType", 0));
0245 }
0246 
0247 bool KProtocolManagerPrivate::useReverseProxy()
0248 {
0249     KConfigGroup cg(config(), QStringLiteral("Proxy Settings"));
0250     return cg.readEntry("ReversedException", false);
0251 }
0252 
0253 QString KProtocolManagerPrivate::readNoProxyFor()
0254 {
0255     QString noProxy = config()->group(QStringLiteral("Proxy Settings")).readEntry("NoProxyFor");
0256     if (proxyType() == EnvVarProxy) {
0257         noProxy = QString::fromLocal8Bit(qgetenv(noProxy.toLocal8Bit().constData()));
0258     }
0259     return noProxy;
0260 }
0261 
0262 QMap<QString, QString> KProtocolManager::entryMap(const QString &group)
0263 {
0264     KProtocolManagerPrivate *d = kProtocolManagerPrivate();
0265     QMutexLocker lock(&d->mutex);
0266     return config()->entryMap(group);
0267 }
0268 
0269 /*=============================== TIMEOUT SETTINGS ==========================*/
0270 
0271 int KProtocolManager::readTimeout()
0272 {
0273     KProtocolManagerPrivate *d = kProtocolManagerPrivate();
0274     QMutexLocker lock(&d->mutex);
0275     KConfigGroup cg(config(), QString());
0276     int val = cg.readEntry("ReadTimeout", DEFAULT_READ_TIMEOUT);
0277     return qMax(MIN_TIMEOUT_VALUE, val);
0278 }
0279 
0280 int KProtocolManager::connectTimeout()
0281 {
0282     KProtocolManagerPrivate *d = kProtocolManagerPrivate();
0283     QMutexLocker lock(&d->mutex);
0284     KConfigGroup cg(config(), QString());
0285     int val = cg.readEntry("ConnectTimeout", DEFAULT_CONNECT_TIMEOUT);
0286     return qMax(MIN_TIMEOUT_VALUE, val);
0287 }
0288 
0289 int KProtocolManager::proxyConnectTimeout()
0290 {
0291     KProtocolManagerPrivate *d = kProtocolManagerPrivate();
0292     QMutexLocker lock(&d->mutex);
0293     KConfigGroup cg(config(), QString());
0294     int val = cg.readEntry("ProxyConnectTimeout", DEFAULT_PROXY_CONNECT_TIMEOUT);
0295     return qMax(MIN_TIMEOUT_VALUE, val);
0296 }
0297 
0298 int KProtocolManager::responseTimeout()
0299 {
0300     KProtocolManagerPrivate *d = kProtocolManagerPrivate();
0301     QMutexLocker lock(&d->mutex);
0302     KConfigGroup cg(config(), QString());
0303     int val = cg.readEntry("ResponseTimeout", DEFAULT_RESPONSE_TIMEOUT);
0304     return qMax(MIN_TIMEOUT_VALUE, val);
0305 }
0306 
0307 static QString adjustProtocol(const QString &scheme)
0308 {
0309     if (scheme.compare(QLatin1String("webdav"), Qt::CaseInsensitive) == 0) {
0310         return QStringLiteral("http");
0311     }
0312 
0313     if (scheme.compare(QLatin1String("webdavs"), Qt::CaseInsensitive) == 0) {
0314         return QStringLiteral("https");
0315     }
0316 
0317     return scheme.toLower();
0318 }
0319 
0320 QString KProtocolManagerPrivate::proxyFor(const QString &protocol)
0321 {
0322     const QString key = adjustProtocol(protocol) + QLatin1String("Proxy");
0323     QString proxyStr(config()->group(QStringLiteral("Proxy Settings")).readEntry(key));
0324     const int index = proxyStr.lastIndexOf(QLatin1Char(' '));
0325 
0326     if (index > -1) {
0327         const QStringView portStr = QStringView(proxyStr).right(proxyStr.length() - index - 1);
0328         const bool isDigits = std::all_of(portStr.cbegin(), portStr.cend(), [](const QChar c) {
0329             return c.isDigit();
0330         });
0331 
0332         if (isDigits) {
0333             proxyStr = QStringView(proxyStr).left(index) + QLatin1Char(':') + portStr;
0334         } else {
0335             proxyStr.clear();
0336         }
0337     }
0338 
0339     return proxyStr;
0340 }
0341 
0342 QStringList KProtocolManagerPrivate::getSystemProxyFor(const QUrl &url)
0343 {
0344     QStringList proxies;
0345 
0346 #if !defined(QT_NO_NETWORKPROXY) && (defined(Q_OS_WIN32) || defined(Q_OS_MAC))
0347     QNetworkProxyQuery query(url);
0348     const QList<QNetworkProxy> proxyList = QNetworkProxyFactory::systemProxyForQuery(query);
0349     proxies.reserve(proxyList.size());
0350     for (const QNetworkProxy &proxy : proxyList) {
0351         QUrl url;
0352         const QNetworkProxy::ProxyType type = proxy.type();
0353         if (type == QNetworkProxy::NoProxy || type == QNetworkProxy::DefaultProxy) {
0354             proxies << QLatin1String("DIRECT");
0355             continue;
0356         }
0357 
0358         if (type == QNetworkProxy::HttpProxy || type == QNetworkProxy::HttpCachingProxy) {
0359             url.setScheme(QLatin1String("http"));
0360         } else if (type == QNetworkProxy::Socks5Proxy) {
0361             url.setScheme(QLatin1String("socks"));
0362         } else if (type == QNetworkProxy::FtpCachingProxy) {
0363             url.setScheme(QLatin1String("ftp"));
0364         }
0365 
0366         url.setHost(proxy.hostName());
0367         url.setPort(proxy.port());
0368         url.setUserName(proxy.user());
0369         proxies << url.url();
0370     }
0371 #else
0372     // On Unix/Linux use system environment variables if any are set.
0373     QString proxyVar(proxyFor(url.scheme()));
0374     // Check for SOCKS proxy, if not proxy is found for given url.
0375     if (!proxyVar.isEmpty()) {
0376         const QString proxy(QString::fromLocal8Bit(qgetenv(proxyVar.toLocal8Bit().constData())).trimmed());
0377         if (!proxy.isEmpty()) {
0378             proxies << proxy;
0379         }
0380     }
0381     // Add the socks proxy as an alternate proxy if it exists,
0382     proxyVar = proxyFor(QStringLiteral("socks"));
0383     if (!proxyVar.isEmpty()) {
0384         QString proxy = QString::fromLocal8Bit(qgetenv(proxyVar.toLocal8Bit().constData())).trimmed();
0385         // Make sure the scheme of SOCKS proxy is always set to "socks://".
0386         const int index = proxy.indexOf(QLatin1String("://"));
0387         const int offset = (index == -1) ? 0 : (index + 3);
0388         proxy = QLatin1String("socks://") + QStringView(proxy).mid(offset);
0389         if (!proxy.isEmpty()) {
0390             proxies << proxy;
0391         }
0392     }
0393 #endif
0394     return proxies;
0395 }
0396 
0397 QStringList KProtocolManagerPrivate::proxiesForUrl(const QUrl &url)
0398 {
0399     QStringList proxyList;
0400 
0401     KProtocolManagerPrivate *d = kProtocolManagerPrivate();
0402     QMutexLocker lock(&d->mutex);
0403     if (!d->shouldIgnoreProxyFor(url)) {
0404         switch (d->proxyType()) {
0405         case PACProxy:
0406         case WPADProxy: {
0407             QUrl u(url);
0408             const QString protocol = adjustProtocol(u.scheme());
0409             u.setScheme(protocol);
0410 
0411 #ifndef KIO_ANDROID_STUB
0412             if (protocol.startsWith(QLatin1String("http")) || protocol.startsWith(QLatin1String("ftp"))) {
0413                 QDBusReply<QStringList> reply =
0414                     QDBusInterface(QStringLiteral("org.kde.kded6"), QStringLiteral("/modules/proxyscout"), QStringLiteral("org.kde.KPAC.ProxyScout"))
0415                         .call(QStringLiteral("proxiesForUrl"), u.toString());
0416                 proxyList = reply;
0417             }
0418 #endif
0419             break;
0420         }
0421         case EnvVarProxy:
0422             proxyList = d->getSystemProxyFor(url);
0423             break;
0424         case ManualProxy: {
0425             QString proxy(d->proxyFor(url.scheme()));
0426             if (!proxy.isEmpty()) {
0427                 proxyList << proxy;
0428             }
0429             // Add the socks proxy as an alternate proxy if it exists,
0430             proxy = d->proxyFor(QStringLiteral("socks"));
0431             if (!proxy.isEmpty()) {
0432                 // Make sure the scheme of SOCKS proxy is always set to "socks://".
0433                 const int index = proxy.indexOf(QLatin1String("://"));
0434                 const int offset = (index == -1) ? 0 : (index + 3);
0435                 proxy = QLatin1String("socks://") + QStringView(proxy).mid(offset);
0436                 proxyList << proxy;
0437             }
0438             break;
0439         }
0440         case NoProxy:
0441             break;
0442         }
0443     }
0444 
0445     if (proxyList.isEmpty()) {
0446         proxyList << QStringLiteral("DIRECT");
0447     }
0448 
0449     return proxyList;
0450 }
0451 
0452 // Generates proxy cache key from request given url.
0453 static QString extractProxyCacheKeyFromUrl(const QUrl &u)
0454 {
0455     QString key = u.scheme();
0456     key += u.host();
0457 
0458     if (u.port() > 0) {
0459         key += QString::number(u.port());
0460     }
0461     return key;
0462 }
0463 
0464 QString KProtocolManagerPrivate::workerProtocol(const QUrl &url, QStringList &proxyList)
0465 {
0466     proxyList.clear();
0467     KProtocolManagerPrivate *d = kProtocolManagerPrivate();
0468     QMutexLocker lock(&d->mutex);
0469     // Do not perform a proxy lookup for any url classified as a ":local" url or
0470     // one that does not have a host component or if proxy is disabled.
0471     QString protocol(url.scheme());
0472     if (url.host().isEmpty() || KProtocolInfo::protocolClass(protocol) == QLatin1String(":local") || kProtocolManagerPrivate->proxyType() == NoProxy) {
0473         return protocol;
0474     }
0475 
0476     const QString proxyCacheKey = extractProxyCacheKeyFromUrl(url);
0477 
0478     // Look for cached proxy information to avoid more work.
0479     if (d->cachedProxyData.contains(proxyCacheKey)) {
0480         KProxyData *data = d->cachedProxyData.object(proxyCacheKey);
0481         proxyList = data->proxyList;
0482         return data->protocol;
0483     }
0484     lock.unlock();
0485 
0486     const QStringList proxies = KProtocolManagerPrivate::proxiesForUrl(url);
0487     const int count = proxies.count();
0488 
0489     if (count > 0 && !(count == 1 && proxies.first() == QLatin1String("DIRECT"))) {
0490         for (const QString &proxy : proxies) {
0491             if (proxy == QLatin1String("DIRECT")) {
0492                 proxyList << proxy;
0493             } else {
0494                 QUrl u(proxy);
0495                 if (!u.isEmpty() && u.isValid() && !u.scheme().isEmpty()) {
0496                     proxyList << proxy;
0497                 }
0498             }
0499         }
0500     }
0501 
0502     // The idea behind worker protocols is not applicable to http
0503     // and webdav protocols as well as protocols unknown to KDE.
0504     /* clang-format off */
0505     if (!proxyList.isEmpty()
0506         && !protocol.startsWith(QLatin1String("http"))
0507         && !protocol.startsWith(QLatin1String("webdav"))
0508         && KProtocolInfo::isKnownProtocol(protocol)) { /* clang-format on */
0509         for (const QString &proxy : std::as_const(proxyList)) {
0510             QUrl u(proxy);
0511             if (u.isValid() && KProtocolInfo::isKnownProtocol(u.scheme())) {
0512                 protocol = u.scheme();
0513                 break;
0514             }
0515         }
0516     }
0517 
0518     lock.relock();
0519     // cache the proxy information...
0520     d->cachedProxyData.insert(proxyCacheKey, new KProxyData(protocol, proxyList));
0521     return protocol;
0522 }
0523 
0524 /*================================= USER-AGENT SETTINGS =====================*/
0525 
0526 // This is not the OS, but the windowing system, e.g. X11 on Unix/Linux.
0527 static QString platform()
0528 {
0529 #if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN)
0530     return QStringLiteral("X11");
0531 #elif defined(Q_OS_MAC)
0532     return QStringLiteral("Macintosh");
0533 #elif defined(Q_OS_WIN)
0534     return QStringLiteral("Windows");
0535 #else
0536     return QStringLiteral("Unknown");
0537 #endif
0538 }
0539 
0540 QString KProtocolManagerPrivate::defaultUserAgent(const QString &_modifiers)
0541 {
0542     KProtocolManagerPrivate *d = kProtocolManagerPrivate();
0543     QMutexLocker lock(&d->mutex);
0544     QString modifiers = _modifiers.toLower();
0545     if (modifiers.isEmpty()) {
0546         modifiers = QStringLiteral("om"); // Show OS, Machine
0547     }
0548 
0549     if (d->modifiers == modifiers && !d->useragent.isEmpty()) {
0550         return d->useragent;
0551     }
0552 
0553     d->modifiers = modifiers;
0554 
0555     QString systemName;
0556     QString systemVersion;
0557     QString machine;
0558     QString supp;
0559     const bool sysInfoFound = KProtocolManagerPrivate::getSystemNameVersionAndMachine(systemName, systemVersion, machine);
0560 
0561     supp += platform();
0562 
0563     if (sysInfoFound) {
0564         if (modifiers.contains(QLatin1Char('o'))) {
0565             supp += QLatin1String("; ") + systemName;
0566             if (modifiers.contains(QLatin1Char('v'))) {
0567                 supp += QLatin1Char(' ') + systemVersion;
0568             }
0569 
0570             if (modifiers.contains(QLatin1Char('m'))) {
0571                 supp += QLatin1Char(' ') + machine;
0572             }
0573         }
0574 
0575         if (modifiers.contains(QLatin1Char('l'))) {
0576             supp += QLatin1String("; ") + QLocale::languageToString(QLocale().language());
0577         }
0578     }
0579 
0580     QString appName = QCoreApplication::applicationName();
0581     if (appName.isEmpty() || appName.startsWith(QLatin1String("kcmshell"), Qt::CaseInsensitive)) {
0582         appName = QStringLiteral("KDE");
0583     }
0584     QString appVersion = QCoreApplication::applicationVersion();
0585     if (appVersion.isEmpty()) {
0586         appVersion += QLatin1String(KIO_VERSION_STRING);
0587     }
0588 
0589     d->useragent = QLatin1String("Mozilla/5.0 (%1) ").arg(supp)
0590         + QLatin1String("KIO/%1.%2 ").arg(QString::number(KIO_VERSION_MAJOR), QString::number(KIO_VERSION_MINOR))
0591         + QLatin1String("%1/%2").arg(appName, appVersion);
0592 
0593     // qDebug() << "USERAGENT STRING:" << d->useragent;
0594     return d->useragent;
0595 }
0596 
0597 bool KProtocolManagerPrivate::getSystemNameVersionAndMachine(QString &systemName, QString &systemVersion, QString &machine)
0598 {
0599 #if defined(Q_OS_WIN)
0600     // we do not use unameBuf.sysname information constructed in kdewin32
0601     // because we want to get separate name and version
0602     systemName = QStringLiteral("Windows");
0603     OSVERSIONINFOEX versioninfo;
0604     ZeroMemory(&versioninfo, sizeof(OSVERSIONINFOEX));
0605     // try calling GetVersionEx using the OSVERSIONINFOEX, if that fails, try using the OSVERSIONINFO
0606     versioninfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
0607     bool ok = GetVersionEx((OSVERSIONINFO *)&versioninfo);
0608     if (!ok) {
0609         versioninfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
0610         ok = GetVersionEx((OSVERSIONINFO *)&versioninfo);
0611     }
0612     if (ok) {
0613         systemVersion = QString::number(versioninfo.dwMajorVersion);
0614         systemVersion += QLatin1Char('.');
0615         systemVersion += QString::number(versioninfo.dwMinorVersion);
0616     }
0617 #else
0618     struct utsname unameBuf;
0619     if (0 != uname(&unameBuf)) {
0620         return false;
0621     }
0622     systemName = QString::fromUtf8(unameBuf.sysname);
0623     systemVersion = QString::fromUtf8(unameBuf.release);
0624     machine = QString::fromUtf8(unameBuf.machine);
0625 #endif
0626     return true;
0627 }
0628 
0629 /*==================================== OTHERS ===============================*/
0630 
0631 bool KProtocolManager::markPartial()
0632 {
0633     KProtocolManagerPrivate *d = kProtocolManagerPrivate();
0634     QMutexLocker lock(&d->mutex);
0635     return config()->group(QString()).readEntry("MarkPartial", true);
0636 }
0637 
0638 int KProtocolManager::minimumKeepSize()
0639 {
0640     KProtocolManagerPrivate *d = kProtocolManagerPrivate();
0641     QMutexLocker lock(&d->mutex);
0642     return config()->group(QString()).readEntry("MinimumKeepSize",
0643                                                 DEFAULT_MINIMUM_KEEP_SIZE); // 5000 byte
0644 }
0645 
0646 bool KProtocolManager::autoResume()
0647 {
0648     KProtocolManagerPrivate *d = kProtocolManagerPrivate();
0649     QMutexLocker lock(&d->mutex);
0650     return config()->group(QString()).readEntry("AutoResume", false);
0651 }
0652 
0653 /* =========================== PROTOCOL CAPABILITIES ============== */
0654 
0655 static KProtocolInfoPrivate *findProtocol(const QUrl &url)
0656 {
0657     if (!url.isValid()) {
0658         return nullptr;
0659     }
0660     QString protocol = url.scheme();
0661     if (!KProtocolInfo::proxiedBy(protocol).isEmpty()) {
0662         QStringList dummy;
0663         protocol = KProtocolManagerPrivate::workerProtocol(url, dummy);
0664     }
0665 
0666     return KProtocolInfoFactory::self()->findProtocol(protocol);
0667 }
0668 
0669 KProtocolInfo::Type KProtocolManager::inputType(const QUrl &url)
0670 {
0671     KProtocolInfoPrivate *prot = findProtocol(url);
0672     if (!prot) {
0673         return KProtocolInfo::T_NONE;
0674     }
0675 
0676     return prot->m_inputType;
0677 }
0678 
0679 KProtocolInfo::Type KProtocolManager::outputType(const QUrl &url)
0680 {
0681     KProtocolInfoPrivate *prot = findProtocol(url);
0682     if (!prot) {
0683         return KProtocolInfo::T_NONE;
0684     }
0685 
0686     return prot->m_outputType;
0687 }
0688 
0689 bool KProtocolManager::isSourceProtocol(const QUrl &url)
0690 {
0691     KProtocolInfoPrivate *prot = findProtocol(url);
0692     if (!prot) {
0693         return false;
0694     }
0695 
0696     return prot->m_isSourceProtocol;
0697 }
0698 
0699 bool KProtocolManager::supportsListing(const QUrl &url)
0700 {
0701     KProtocolInfoPrivate *prot = findProtocol(url);
0702     if (!prot) {
0703         return false;
0704     }
0705 
0706     return prot->m_supportsListing;
0707 }
0708 
0709 QStringList KProtocolManager::listing(const QUrl &url)
0710 {
0711     KProtocolInfoPrivate *prot = findProtocol(url);
0712     if (!prot) {
0713         return QStringList();
0714     }
0715 
0716     return prot->m_listing;
0717 }
0718 
0719 bool KProtocolManager::supportsReading(const QUrl &url)
0720 {
0721     KProtocolInfoPrivate *prot = findProtocol(url);
0722     if (!prot) {
0723         return false;
0724     }
0725 
0726     return prot->m_supportsReading;
0727 }
0728 
0729 bool KProtocolManager::supportsWriting(const QUrl &url)
0730 {
0731     KProtocolInfoPrivate *prot = findProtocol(url);
0732     if (!prot) {
0733         return false;
0734     }
0735 
0736     return prot->m_supportsWriting;
0737 }
0738 
0739 bool KProtocolManager::supportsMakeDir(const QUrl &url)
0740 {
0741     KProtocolInfoPrivate *prot = findProtocol(url);
0742     if (!prot) {
0743         return false;
0744     }
0745 
0746     return prot->m_supportsMakeDir;
0747 }
0748 
0749 bool KProtocolManager::supportsDeleting(const QUrl &url)
0750 {
0751     KProtocolInfoPrivate *prot = findProtocol(url);
0752     if (!prot) {
0753         return false;
0754     }
0755 
0756     return prot->m_supportsDeleting;
0757 }
0758 
0759 bool KProtocolManager::supportsLinking(const QUrl &url)
0760 {
0761     KProtocolInfoPrivate *prot = findProtocol(url);
0762     if (!prot) {
0763         return false;
0764     }
0765 
0766     return prot->m_supportsLinking;
0767 }
0768 
0769 bool KProtocolManager::supportsMoving(const QUrl &url)
0770 {
0771     KProtocolInfoPrivate *prot = findProtocol(url);
0772     if (!prot) {
0773         return false;
0774     }
0775 
0776     return prot->m_supportsMoving;
0777 }
0778 
0779 bool KProtocolManager::supportsOpening(const QUrl &url)
0780 {
0781     KProtocolInfoPrivate *prot = findProtocol(url);
0782     if (!prot) {
0783         return false;
0784     }
0785 
0786     return prot->m_supportsOpening;
0787 }
0788 
0789 bool KProtocolManager::supportsTruncating(const QUrl &url)
0790 {
0791     KProtocolInfoPrivate *prot = findProtocol(url);
0792     if (!prot) {
0793         return false;
0794     }
0795 
0796     return prot->m_supportsTruncating;
0797 }
0798 
0799 bool KProtocolManager::canCopyFromFile(const QUrl &url)
0800 {
0801     KProtocolInfoPrivate *prot = findProtocol(url);
0802     if (!prot) {
0803         return false;
0804     }
0805 
0806     return prot->m_canCopyFromFile;
0807 }
0808 
0809 bool KProtocolManager::canCopyToFile(const QUrl &url)
0810 {
0811     KProtocolInfoPrivate *prot = findProtocol(url);
0812     if (!prot) {
0813         return false;
0814     }
0815 
0816     return prot->m_canCopyToFile;
0817 }
0818 
0819 bool KProtocolManager::canRenameFromFile(const QUrl &url)
0820 {
0821     KProtocolInfoPrivate *prot = findProtocol(url);
0822     if (!prot) {
0823         return false;
0824     }
0825 
0826     return prot->m_canRenameFromFile;
0827 }
0828 
0829 bool KProtocolManager::canRenameToFile(const QUrl &url)
0830 {
0831     KProtocolInfoPrivate *prot = findProtocol(url);
0832     if (!prot) {
0833         return false;
0834     }
0835 
0836     return prot->m_canRenameToFile;
0837 }
0838 
0839 bool KProtocolManager::canDeleteRecursive(const QUrl &url)
0840 {
0841     KProtocolInfoPrivate *prot = findProtocol(url);
0842     if (!prot) {
0843         return false;
0844     }
0845 
0846     return prot->m_canDeleteRecursive;
0847 }
0848 
0849 KProtocolInfo::FileNameUsedForCopying KProtocolManager::fileNameUsedForCopying(const QUrl &url)
0850 {
0851     KProtocolInfoPrivate *prot = findProtocol(url);
0852     if (!prot) {
0853         return KProtocolInfo::FromUrl;
0854     }
0855 
0856     return prot->m_fileNameUsedForCopying;
0857 }
0858 
0859 QString KProtocolManager::defaultMimetype(const QUrl &url)
0860 {
0861     KProtocolInfoPrivate *prot = findProtocol(url);
0862     if (!prot) {
0863         return QString();
0864     }
0865 
0866     return prot->m_defaultMimetype;
0867 }
0868 
0869 QString KProtocolManager::protocolForArchiveMimetype(const QString &mimeType)
0870 {
0871     KProtocolManagerPrivate *d = kProtocolManagerPrivate();
0872     QMutexLocker lock(&d->mutex);
0873     if (d->protocolForArchiveMimetypes.isEmpty()) {
0874         const QList<KProtocolInfoPrivate *> allProtocols = KProtocolInfoFactory::self()->allProtocols();
0875         for (KProtocolInfoPrivate *allProtocol : allProtocols) {
0876             const QStringList archiveMimetypes = allProtocol->m_archiveMimeTypes;
0877             for (const QString &mime : archiveMimetypes) {
0878                 d->protocolForArchiveMimetypes.insert(mime, allProtocol->m_name);
0879             }
0880         }
0881     }
0882     return d->protocolForArchiveMimetypes.value(mimeType);
0883 }
0884 
0885 QString KProtocolManager::charsetFor(const QUrl &url)
0886 {
0887     return KIO::WorkerConfig::self()->configData(url.scheme(), url.host(), QStringLiteral("Charset"));
0888 }
0889 
0890 bool KProtocolManager::supportsPermissions(const QUrl &url)
0891 {
0892     KProtocolInfoPrivate *prot = findProtocol(url);
0893     if (!prot) {
0894         return true;
0895     }
0896 
0897     return prot->m_supportsPermissions;
0898 }
0899 
0900 #undef PRIVATE_DATA
0901 
0902 #include "moc_kprotocolmanager_p.cpp"