1 | /* |
2 | This file is part of the KDE libraries |
3 | SPDX-FileCopyrightText: 1999 Torben Weis <weis@kde.org> |
4 | SPDX-FileCopyrightText: 2000 Waldo Bastain <bastain@kde.org> |
5 | SPDX-FileCopyrightText: 2000 Dawit Alemayehu <adawit@kde.org> |
6 | SPDX-FileCopyrightText: 2008 Jarosław Staniek <staniek@kde.org> |
7 | SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org> |
8 | |
9 | SPDX-License-Identifier: LGPL-2.0-only |
10 | */ |
11 | |
12 | #include "kprotocolmanager.h" |
13 | #include "kprotocolinfo_p.h" |
14 | #include "kprotocolmanager_p.h" |
15 | |
16 | #include "hostinfo.h" |
17 | |
18 | #include <config-kiocore.h> |
19 | |
20 | #include <qplatformdefs.h> |
21 | #include <string.h> |
22 | #ifdef Q_OS_WIN |
23 | #include <qt_windows.h> |
24 | #undef interface // windows.h defines this, breaks QtDBus since it has parameters named interface |
25 | #else |
26 | #include <sys/utsname.h> |
27 | #endif |
28 | |
29 | #include <QCache> |
30 | #include <QCoreApplication> |
31 | #ifndef KIO_ANDROID_STUB |
32 | #include <QDBusInterface> |
33 | #include <QDBusReply> |
34 | #endif |
35 | #include <QHostAddress> |
36 | #include <QHostInfo> |
37 | #include <QLocale> |
38 | #include <QMimeDatabase> |
39 | #include <QRegularExpression> |
40 | #include <QSslSocket> |
41 | #include <QStandardPaths> |
42 | #include <QUrl> |
43 | |
44 | #if !defined(QT_NO_NETWORKPROXY) && (defined(Q_OS_WIN32) || defined(Q_OS_MAC)) |
45 | #include <QNetworkProxyFactory> |
46 | #include <QNetworkProxyQuery> |
47 | #endif |
48 | |
49 | #include <KConfigGroup> |
50 | #include <KSharedConfig> |
51 | #include <kio_version.h> |
52 | |
53 | #include <kprotocolinfofactory_p.h> |
54 | |
55 | #include "ioworker_defaults.h" |
56 | #include "workerconfig.h" |
57 | |
58 | /* |
59 | Domain suffix match. E.g. return true if host is "cuzco.inka.de" and |
60 | nplist is "inka.de,hadiko.de" or if host is "localhost" and nplist is |
61 | "localhost". |
62 | */ |
63 | static bool revmatch(const char *host, const char *nplist) |
64 | { |
65 | if (host == nullptr) { |
66 | return false; |
67 | } |
68 | |
69 | const char *hptr = host + strlen(s: host) - 1; |
70 | const char *nptr = nplist + strlen(s: nplist) - 1; |
71 | const char *shptr = hptr; |
72 | |
73 | while (nptr >= nplist) { |
74 | if (*hptr != *nptr) { |
75 | hptr = shptr; |
76 | |
77 | // Try to find another domain or host in the list |
78 | while (--nptr >= nplist && *nptr != ',' && *nptr != ' ') { |
79 | ; |
80 | } |
81 | |
82 | // Strip out multiple spaces and commas |
83 | while (--nptr >= nplist && (*nptr == ',' || *nptr == ' ')) { |
84 | ; |
85 | } |
86 | } else { |
87 | if (nptr == nplist || nptr[-1] == ',' || nptr[-1] == ' ') { |
88 | return true; |
89 | } |
90 | if (nptr[-1] == '/' && hptr == host) { // "bugs.kde.org" vs "http://bugs.kde.org", the config UI says URLs are ok |
91 | return true; |
92 | } |
93 | if (hptr == host) { // e.g. revmatch("bugs.kde.org","mybugs.kde.org") |
94 | return false; |
95 | } |
96 | |
97 | hptr--; |
98 | nptr--; |
99 | } |
100 | } |
101 | |
102 | return false; |
103 | } |
104 | |
105 | Q_GLOBAL_STATIC(KProtocolManagerPrivate, kProtocolManagerPrivate) |
106 | |
107 | static void syncOnExit() |
108 | { |
109 | if (kProtocolManagerPrivate.exists()) { |
110 | kProtocolManagerPrivate()->sync(); |
111 | } |
112 | } |
113 | |
114 | KProtocolManagerPrivate::KProtocolManagerPrivate() |
115 | { |
116 | // post routine since KConfig::sync() breaks if called too late |
117 | qAddPostRoutine(syncOnExit); |
118 | cachedProxyData.setMaxCost(200); // double the max cost. |
119 | } |
120 | |
121 | KProtocolManagerPrivate::~KProtocolManagerPrivate() |
122 | { |
123 | } |
124 | |
125 | /* |
126 | * Returns true if url is in the no proxy list. |
127 | */ |
128 | bool KProtocolManagerPrivate::shouldIgnoreProxyFor(const QUrl &url) |
129 | { |
130 | bool isMatch = false; |
131 | const ProxyType type = proxyType(); |
132 | const bool useRevProxy = ((type == ManualProxy) && useReverseProxy()); |
133 | const bool useNoProxyList = (type == ManualProxy || type == EnvVarProxy); |
134 | |
135 | // No proxy only applies to ManualProxy and EnvVarProxy types... |
136 | if (useNoProxyList && noProxyFor.isEmpty()) { |
137 | QStringList noProxyForList(readNoProxyFor().split(sep: QLatin1Char(','))); |
138 | QMutableStringListIterator it(noProxyForList); |
139 | while (it.hasNext()) { |
140 | SubnetPair subnet = QHostAddress::parseSubnet(subnet: it.next()); |
141 | if (!subnet.first.isNull()) { |
142 | noProxySubnets << subnet; |
143 | it.remove(); |
144 | } |
145 | } |
146 | noProxyFor = noProxyForList.join(sep: QLatin1Char(',')); |
147 | } |
148 | |
149 | if (!noProxyFor.isEmpty()) { |
150 | QString qhost = url.host().toLower(); |
151 | QByteArray host = qhost.toLatin1(); |
152 | const QString qno_proxy = noProxyFor.trimmed().toLower(); |
153 | const QByteArray no_proxy = qno_proxy.toLatin1(); |
154 | isMatch = revmatch(host: host.constData(), nplist: no_proxy.constData()); |
155 | |
156 | // If no match is found and the request url has a port |
157 | // number, try the combination of "host:port". This allows |
158 | // users to enter host:port in the No-proxy-For list. |
159 | if (!isMatch && url.port() > 0) { |
160 | qhost += QLatin1Char(':') + QString::number(url.port()); |
161 | host = qhost.toLatin1(); |
162 | isMatch = revmatch(host: host.constData(), nplist: no_proxy.constData()); |
163 | } |
164 | |
165 | // If the hostname does not contain a dot, check if |
166 | // <local> is part of noProxy. |
167 | if (!isMatch && !host.isEmpty() && (strchr(s: host.constData(), c: '.') == nullptr)) { |
168 | isMatch = revmatch(host: "<local>" , nplist: no_proxy.constData()); |
169 | } |
170 | } |
171 | |
172 | const QString host(url.host()); |
173 | |
174 | if (!noProxySubnets.isEmpty() && !host.isEmpty()) { |
175 | QHostAddress address(host); |
176 | // If request url is not IP address, do a DNS lookup of the hostname. |
177 | // TODO: Perhaps we should make configurable ? |
178 | if (address.isNull()) { |
179 | // qDebug() << "Performing DNS lookup for" << host; |
180 | QHostInfo info = KIO::HostInfo::lookupHost(hostName: host, timeout: 2000); |
181 | const QList<QHostAddress> addresses = info.addresses(); |
182 | if (!addresses.isEmpty()) { |
183 | address = addresses.first(); |
184 | } |
185 | } |
186 | |
187 | if (!address.isNull()) { |
188 | for (const SubnetPair &subnet : std::as_const(t&: noProxySubnets)) { |
189 | if (address.isInSubnet(subnet)) { |
190 | isMatch = true; |
191 | break; |
192 | } |
193 | } |
194 | } |
195 | } |
196 | |
197 | return (useRevProxy != isMatch); |
198 | } |
199 | |
200 | void KProtocolManagerPrivate::sync() |
201 | { |
202 | QMutexLocker lock(&mutex); |
203 | if (http_config) { |
204 | http_config->sync(); |
205 | } |
206 | if (configPtr) { |
207 | configPtr->sync(); |
208 | } |
209 | } |
210 | |
211 | void KProtocolManager::reparseConfiguration() |
212 | { |
213 | KProtocolManagerPrivate *d = kProtocolManagerPrivate(); |
214 | QMutexLocker lock(&d->mutex); |
215 | if (d->http_config) { |
216 | d->http_config->reparseConfiguration(); |
217 | } |
218 | if (d->configPtr) { |
219 | d->configPtr->reparseConfiguration(); |
220 | } |
221 | d->cachedProxyData.clear(); |
222 | d->noProxyFor.clear(); |
223 | d->modifiers.clear(); |
224 | d->useragent.clear(); |
225 | lock.unlock(); |
226 | |
227 | // Force the slave config to re-read its config... |
228 | KIO::WorkerConfig::self()->reset(); |
229 | } |
230 | |
231 | static KSharedConfig::Ptr config() |
232 | { |
233 | KProtocolManagerPrivate *d = kProtocolManagerPrivate(); |
234 | Q_ASSERT(!d->mutex.tryLock()); // the caller must have locked the mutex |
235 | if (!d->configPtr) { |
236 | d->configPtr = KSharedConfig::openConfig(QStringLiteral("kioslaverc" ), mode: KConfig::NoGlobals); |
237 | } |
238 | return d->configPtr; |
239 | } |
240 | |
241 | KProtocolManagerPrivate::ProxyType KProtocolManagerPrivate::proxyType() |
242 | { |
243 | KConfigGroup cg(config(), QStringLiteral("Proxy Settings" )); |
244 | return static_cast<ProxyType>(cg.readEntry(key: "ProxyType" , defaultValue: 0)); |
245 | } |
246 | |
247 | bool KProtocolManagerPrivate::useReverseProxy() |
248 | { |
249 | KConfigGroup cg(config(), QStringLiteral("Proxy Settings" )); |
250 | return cg.readEntry(key: "ReversedException" , defaultValue: false); |
251 | } |
252 | |
253 | QString KProtocolManagerPrivate::readNoProxyFor() |
254 | { |
255 | QString noProxy = config()->group(QStringLiteral("Proxy Settings" )).readEntry(key: "NoProxyFor" ); |
256 | if (proxyType() == EnvVarProxy) { |
257 | noProxy = QString::fromLocal8Bit(ba: qgetenv(varName: noProxy.toLocal8Bit().constData())); |
258 | } |
259 | return noProxy; |
260 | } |
261 | |
262 | QMap<QString, QString> KProtocolManager::entryMap(const QString &group) |
263 | { |
264 | KProtocolManagerPrivate *d = kProtocolManagerPrivate(); |
265 | QMutexLocker lock(&d->mutex); |
266 | return config()->entryMap(aGroup: group); |
267 | } |
268 | |
269 | /*=============================== TIMEOUT SETTINGS ==========================*/ |
270 | |
271 | int KProtocolManager::readTimeout() |
272 | { |
273 | KProtocolManagerPrivate *d = kProtocolManagerPrivate(); |
274 | QMutexLocker lock(&d->mutex); |
275 | KConfigGroup cg(config(), QString()); |
276 | int val = cg.readEntry(key: "ReadTimeout" , defaultValue: DEFAULT_READ_TIMEOUT); |
277 | return qMax(a: MIN_TIMEOUT_VALUE, b: val); |
278 | } |
279 | |
280 | int KProtocolManager::connectTimeout() |
281 | { |
282 | KProtocolManagerPrivate *d = kProtocolManagerPrivate(); |
283 | QMutexLocker lock(&d->mutex); |
284 | KConfigGroup cg(config(), QString()); |
285 | int val = cg.readEntry(key: "ConnectTimeout" , defaultValue: DEFAULT_CONNECT_TIMEOUT); |
286 | return qMax(a: MIN_TIMEOUT_VALUE, b: val); |
287 | } |
288 | |
289 | int KProtocolManager::proxyConnectTimeout() |
290 | { |
291 | KProtocolManagerPrivate *d = kProtocolManagerPrivate(); |
292 | QMutexLocker lock(&d->mutex); |
293 | KConfigGroup cg(config(), QString()); |
294 | int val = cg.readEntry(key: "ProxyConnectTimeout" , defaultValue: DEFAULT_PROXY_CONNECT_TIMEOUT); |
295 | return qMax(a: MIN_TIMEOUT_VALUE, b: val); |
296 | } |
297 | |
298 | int KProtocolManager::responseTimeout() |
299 | { |
300 | KProtocolManagerPrivate *d = kProtocolManagerPrivate(); |
301 | QMutexLocker lock(&d->mutex); |
302 | KConfigGroup cg(config(), QString()); |
303 | int val = cg.readEntry(key: "ResponseTimeout" , defaultValue: DEFAULT_RESPONSE_TIMEOUT); |
304 | return qMax(a: MIN_TIMEOUT_VALUE, b: val); |
305 | } |
306 | |
307 | static QString adjustProtocol(const QString &scheme) |
308 | { |
309 | if (scheme.compare(other: QLatin1String("webdav" ), cs: Qt::CaseInsensitive) == 0) { |
310 | return QStringLiteral("http" ); |
311 | } |
312 | |
313 | if (scheme.compare(other: QLatin1String("webdavs" ), cs: Qt::CaseInsensitive) == 0) { |
314 | return QStringLiteral("https" ); |
315 | } |
316 | |
317 | return scheme.toLower(); |
318 | } |
319 | |
320 | QString KProtocolManagerPrivate::proxyFor(const QString &protocol) |
321 | { |
322 | const QString key = adjustProtocol(scheme: protocol) + QLatin1String("Proxy" ); |
323 | QString proxyStr(config()->group(QStringLiteral("Proxy Settings" )).readEntry(key)); |
324 | const int index = proxyStr.lastIndexOf(c: QLatin1Char(' ')); |
325 | |
326 | if (index > -1) { |
327 | const QStringView portStr = QStringView(proxyStr).right(n: proxyStr.length() - index - 1); |
328 | const bool isDigits = std::all_of(first: portStr.cbegin(), last: portStr.cend(), pred: [](const QChar c) { |
329 | return c.isDigit(); |
330 | }); |
331 | |
332 | if (isDigits) { |
333 | proxyStr = QStringView(proxyStr).left(n: index) + QLatin1Char(':') + portStr; |
334 | } else { |
335 | proxyStr.clear(); |
336 | } |
337 | } |
338 | |
339 | return proxyStr; |
340 | } |
341 | |
342 | QStringList KProtocolManagerPrivate::getSystemProxyFor(const QUrl &url) |
343 | { |
344 | QStringList proxies; |
345 | |
346 | #if !defined(QT_NO_NETWORKPROXY) && (defined(Q_OS_WIN32) || defined(Q_OS_MAC)) |
347 | QNetworkProxyQuery query(url); |
348 | const QList<QNetworkProxy> proxyList = QNetworkProxyFactory::systemProxyForQuery(query); |
349 | proxies.reserve(proxyList.size()); |
350 | for (const QNetworkProxy &proxy : proxyList) { |
351 | QUrl url; |
352 | const QNetworkProxy::ProxyType type = proxy.type(); |
353 | if (type == QNetworkProxy::NoProxy || type == QNetworkProxy::DefaultProxy) { |
354 | proxies << QLatin1String("DIRECT" ); |
355 | continue; |
356 | } |
357 | |
358 | if (type == QNetworkProxy::HttpProxy || type == QNetworkProxy::HttpCachingProxy) { |
359 | url.setScheme(QLatin1String("http" )); |
360 | } else if (type == QNetworkProxy::Socks5Proxy) { |
361 | url.setScheme(QLatin1String("socks" )); |
362 | } else if (type == QNetworkProxy::FtpCachingProxy) { |
363 | url.setScheme(QLatin1String("ftp" )); |
364 | } |
365 | |
366 | url.setHost(proxy.hostName()); |
367 | url.setPort(proxy.port()); |
368 | url.setUserName(proxy.user()); |
369 | proxies << url.url(); |
370 | } |
371 | #else |
372 | // On Unix/Linux use system environment variables if any are set. |
373 | QString proxyVar(proxyFor(protocol: url.scheme())); |
374 | // Check for SOCKS proxy, if not proxy is found for given url. |
375 | if (!proxyVar.isEmpty()) { |
376 | const QString proxy(QString::fromLocal8Bit(ba: qgetenv(varName: proxyVar.toLocal8Bit().constData())).trimmed()); |
377 | if (!proxy.isEmpty()) { |
378 | proxies << proxy; |
379 | } |
380 | } |
381 | // Add the socks proxy as an alternate proxy if it exists, |
382 | proxyVar = proxyFor(QStringLiteral("socks" )); |
383 | if (!proxyVar.isEmpty()) { |
384 | QString proxy = QString::fromLocal8Bit(ba: qgetenv(varName: proxyVar.toLocal8Bit().constData())).trimmed(); |
385 | // Make sure the scheme of SOCKS proxy is always set to "socks://". |
386 | const int index = proxy.indexOf(s: QLatin1String("://" )); |
387 | const int offset = (index == -1) ? 0 : (index + 3); |
388 | proxy = QLatin1String("socks://" ) + QStringView(proxy).mid(pos: offset); |
389 | if (!proxy.isEmpty()) { |
390 | proxies << proxy; |
391 | } |
392 | } |
393 | #endif |
394 | return proxies; |
395 | } |
396 | |
397 | QStringList KProtocolManagerPrivate::proxiesForUrl(const QUrl &url) |
398 | { |
399 | QStringList proxyList; |
400 | |
401 | KProtocolManagerPrivate *d = kProtocolManagerPrivate(); |
402 | QMutexLocker lock(&d->mutex); |
403 | if (!d->shouldIgnoreProxyFor(url)) { |
404 | switch (d->proxyType()) { |
405 | case PACProxy: |
406 | case WPADProxy: { |
407 | QUrl u(url); |
408 | const QString protocol = adjustProtocol(scheme: u.scheme()); |
409 | u.setScheme(protocol); |
410 | |
411 | #ifndef KIO_ANDROID_STUB |
412 | if (protocol.startsWith(s: QLatin1String("http" )) || protocol.startsWith(s: QLatin1String("ftp" ))) { |
413 | QDBusReply<QStringList> reply = |
414 | QDBusInterface(QStringLiteral("org.kde.kded6" ), QStringLiteral("/modules/proxyscout" ), QStringLiteral("org.kde.KPAC.ProxyScout" )) |
415 | .call(QStringLiteral("proxiesForUrl" ), args: u.toString()); |
416 | proxyList = reply; |
417 | } |
418 | #endif |
419 | break; |
420 | } |
421 | case EnvVarProxy: |
422 | proxyList = d->getSystemProxyFor(url); |
423 | break; |
424 | case ManualProxy: { |
425 | QString proxy(d->proxyFor(protocol: url.scheme())); |
426 | if (!proxy.isEmpty()) { |
427 | proxyList << proxy; |
428 | } |
429 | // Add the socks proxy as an alternate proxy if it exists, |
430 | proxy = d->proxyFor(QStringLiteral("socks" )); |
431 | if (!proxy.isEmpty()) { |
432 | // Make sure the scheme of SOCKS proxy is always set to "socks://". |
433 | const int index = proxy.indexOf(s: QLatin1String("://" )); |
434 | const int offset = (index == -1) ? 0 : (index + 3); |
435 | proxy = QLatin1String("socks://" ) + QStringView(proxy).mid(pos: offset); |
436 | proxyList << proxy; |
437 | } |
438 | break; |
439 | } |
440 | case NoProxy: |
441 | break; |
442 | } |
443 | } |
444 | |
445 | if (proxyList.isEmpty()) { |
446 | proxyList << QStringLiteral("DIRECT" ); |
447 | } |
448 | |
449 | return proxyList; |
450 | } |
451 | |
452 | // Generates proxy cache key from request given url. |
453 | static QString (const QUrl &u) |
454 | { |
455 | QString key = u.scheme(); |
456 | key += u.host(); |
457 | |
458 | if (u.port() > 0) { |
459 | key += QString::number(u.port()); |
460 | } |
461 | return key; |
462 | } |
463 | |
464 | QString KProtocolManagerPrivate::workerProtocol(const QUrl &url, QStringList &proxyList) |
465 | { |
466 | proxyList.clear(); |
467 | KProtocolManagerPrivate *d = kProtocolManagerPrivate(); |
468 | QMutexLocker lock(&d->mutex); |
469 | // Do not perform a proxy lookup for any url classified as a ":local" url or |
470 | // one that does not have a host component or if proxy is disabled. |
471 | QString protocol(url.scheme()); |
472 | if (url.host().isEmpty() || KProtocolInfo::protocolClass(protocol) == QLatin1String(":local" ) || kProtocolManagerPrivate->proxyType() == NoProxy) { |
473 | return protocol; |
474 | } |
475 | |
476 | const QString proxyCacheKey = extractProxyCacheKeyFromUrl(u: url); |
477 | |
478 | // Look for cached proxy information to avoid more work. |
479 | if (d->cachedProxyData.contains(key: proxyCacheKey)) { |
480 | KProxyData *data = d->cachedProxyData.object(key: proxyCacheKey); |
481 | proxyList = data->proxyList; |
482 | return data->protocol; |
483 | } |
484 | lock.unlock(); |
485 | |
486 | const QStringList proxies = KProtocolManagerPrivate::proxiesForUrl(url); |
487 | const int count = proxies.count(); |
488 | |
489 | if (count > 0 && !(count == 1 && proxies.first() == QLatin1String("DIRECT" ))) { |
490 | for (const QString &proxy : proxies) { |
491 | if (proxy == QLatin1String("DIRECT" )) { |
492 | proxyList << proxy; |
493 | } else { |
494 | QUrl u(proxy); |
495 | if (!u.isEmpty() && u.isValid() && !u.scheme().isEmpty()) { |
496 | proxyList << proxy; |
497 | } |
498 | } |
499 | } |
500 | } |
501 | |
502 | // The idea behind worker protocols is not applicable to http |
503 | // and webdav protocols as well as protocols unknown to KDE. |
504 | /* clang-format off */ |
505 | if (!proxyList.isEmpty() |
506 | && !protocol.startsWith(s: QLatin1String("http" )) |
507 | && !protocol.startsWith(s: QLatin1String("webdav" )) |
508 | && KProtocolInfo::isKnownProtocol(protocol)) { /* clang-format on */ |
509 | for (const QString &proxy : std::as_const(t&: proxyList)) { |
510 | QUrl u(proxy); |
511 | if (u.isValid() && KProtocolInfo::isKnownProtocol(protocol: u.scheme())) { |
512 | protocol = u.scheme(); |
513 | break; |
514 | } |
515 | } |
516 | } |
517 | |
518 | lock.relock(); |
519 | // cache the proxy information... |
520 | d->cachedProxyData.insert(key: proxyCacheKey, object: new KProxyData(protocol, proxyList)); |
521 | return protocol; |
522 | } |
523 | |
524 | /*================================= USER-AGENT SETTINGS =====================*/ |
525 | |
526 | // This is not the OS, but the windowing system, e.g. X11 on Unix/Linux. |
527 | static QString platform() |
528 | { |
529 | #if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) |
530 | return QStringLiteral("X11" ); |
531 | #elif defined(Q_OS_MAC) |
532 | return QStringLiteral("Macintosh" ); |
533 | #elif defined(Q_OS_WIN) |
534 | return QStringLiteral("Windows" ); |
535 | #else |
536 | return QStringLiteral("Unknown" ); |
537 | #endif |
538 | } |
539 | |
540 | QString KProtocolManagerPrivate::defaultUserAgent(const QString &_modifiers) |
541 | { |
542 | KProtocolManagerPrivate *d = kProtocolManagerPrivate(); |
543 | QMutexLocker lock(&d->mutex); |
544 | QString modifiers = _modifiers.toLower(); |
545 | if (modifiers.isEmpty()) { |
546 | modifiers = QStringLiteral("om" ); // Show OS, Machine |
547 | } |
548 | |
549 | if (d->modifiers == modifiers && !d->useragent.isEmpty()) { |
550 | return d->useragent; |
551 | } |
552 | |
553 | d->modifiers = modifiers; |
554 | |
555 | QString systemName; |
556 | QString systemVersion; |
557 | QString machine; |
558 | QString supp; |
559 | const bool sysInfoFound = KProtocolManagerPrivate::getSystemNameVersionAndMachine(systemName, systemVersion, machine); |
560 | |
561 | supp += platform(); |
562 | |
563 | if (sysInfoFound) { |
564 | if (modifiers.contains(c: QLatin1Char('o'))) { |
565 | supp += QLatin1String("; " ) + systemName; |
566 | if (modifiers.contains(c: QLatin1Char('v'))) { |
567 | supp += QLatin1Char(' ') + systemVersion; |
568 | } |
569 | |
570 | if (modifiers.contains(c: QLatin1Char('m'))) { |
571 | supp += QLatin1Char(' ') + machine; |
572 | } |
573 | } |
574 | |
575 | if (modifiers.contains(c: QLatin1Char('l'))) { |
576 | supp += QLatin1String("; " ) + QLocale::languageToString(language: QLocale().language()); |
577 | } |
578 | } |
579 | |
580 | QString appName = QCoreApplication::applicationName(); |
581 | if (appName.isEmpty() || appName.startsWith(s: QLatin1String("kcmshell" ), cs: Qt::CaseInsensitive)) { |
582 | appName = QStringLiteral("KDE" ); |
583 | } |
584 | QString appVersion = QCoreApplication::applicationVersion(); |
585 | if (appVersion.isEmpty()) { |
586 | appVersion += QLatin1String(KIO_VERSION_STRING); |
587 | } |
588 | |
589 | d->useragent = QLatin1String("Mozilla/5.0 (%1) " ).arg(args&: supp) |
590 | + QLatin1String("KIO/%1.%2 " ).arg(args: QString::number(KIO_VERSION_MAJOR), args: QString::number(KIO_VERSION_MINOR)) |
591 | + QLatin1String("%1/%2" ).arg(args&: appName, args&: appVersion); |
592 | |
593 | // qDebug() << "USERAGENT STRING:" << d->useragent; |
594 | return d->useragent; |
595 | } |
596 | |
597 | bool KProtocolManagerPrivate::getSystemNameVersionAndMachine(QString &systemName, QString &systemVersion, QString &machine) |
598 | { |
599 | #if defined(Q_OS_WIN) |
600 | // we do not use unameBuf.sysname information constructed in kdewin32 |
601 | // because we want to get separate name and version |
602 | systemName = QStringLiteral("Windows" ); |
603 | OSVERSIONINFOEX versioninfo; |
604 | ZeroMemory(&versioninfo, sizeof(OSVERSIONINFOEX)); |
605 | // try calling GetVersionEx using the OSVERSIONINFOEX, if that fails, try using the OSVERSIONINFO |
606 | versioninfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); |
607 | bool ok = GetVersionEx((OSVERSIONINFO *)&versioninfo); |
608 | if (!ok) { |
609 | versioninfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); |
610 | ok = GetVersionEx((OSVERSIONINFO *)&versioninfo); |
611 | } |
612 | if (ok) { |
613 | systemVersion = QString::number(versioninfo.dwMajorVersion); |
614 | systemVersion += QLatin1Char('.'); |
615 | systemVersion += QString::number(versioninfo.dwMinorVersion); |
616 | } |
617 | #else |
618 | struct utsname unameBuf; |
619 | if (0 != uname(name: &unameBuf)) { |
620 | return false; |
621 | } |
622 | systemName = QString::fromUtf8(utf8: unameBuf.sysname); |
623 | systemVersion = QString::fromUtf8(utf8: unameBuf.release); |
624 | machine = QString::fromUtf8(utf8: unameBuf.machine); |
625 | #endif |
626 | return true; |
627 | } |
628 | |
629 | /*==================================== OTHERS ===============================*/ |
630 | |
631 | bool KProtocolManager::markPartial() |
632 | { |
633 | KProtocolManagerPrivate *d = kProtocolManagerPrivate(); |
634 | QMutexLocker lock(&d->mutex); |
635 | return config()->group(group: QString()).readEntry(key: "MarkPartial" , defaultValue: true); |
636 | } |
637 | |
638 | int KProtocolManager::minimumKeepSize() |
639 | { |
640 | KProtocolManagerPrivate *d = kProtocolManagerPrivate(); |
641 | QMutexLocker lock(&d->mutex); |
642 | return config()->group(group: QString()).readEntry(key: "MinimumKeepSize" , |
643 | defaultValue: DEFAULT_MINIMUM_KEEP_SIZE); // 5000 byte |
644 | } |
645 | |
646 | bool KProtocolManager::autoResume() |
647 | { |
648 | KProtocolManagerPrivate *d = kProtocolManagerPrivate(); |
649 | QMutexLocker lock(&d->mutex); |
650 | return config()->group(group: QString()).readEntry(key: "AutoResume" , defaultValue: false); |
651 | } |
652 | |
653 | /* =========================== PROTOCOL CAPABILITIES ============== */ |
654 | |
655 | static KProtocolInfoPrivate *findProtocol(const QUrl &url) |
656 | { |
657 | if (!url.isValid()) { |
658 | return nullptr; |
659 | } |
660 | QString protocol = url.scheme(); |
661 | if (!KProtocolInfo::proxiedBy(protocol).isEmpty()) { |
662 | QStringList dummy; |
663 | protocol = KProtocolManagerPrivate::workerProtocol(url, proxyList&: dummy); |
664 | } |
665 | |
666 | return KProtocolInfoFactory::self()->findProtocol(protocol); |
667 | } |
668 | |
669 | KProtocolInfo::Type KProtocolManager::inputType(const QUrl &url) |
670 | { |
671 | KProtocolInfoPrivate *prot = findProtocol(url); |
672 | if (!prot) { |
673 | return KProtocolInfo::T_NONE; |
674 | } |
675 | |
676 | return prot->m_inputType; |
677 | } |
678 | |
679 | KProtocolInfo::Type KProtocolManager::outputType(const QUrl &url) |
680 | { |
681 | KProtocolInfoPrivate *prot = findProtocol(url); |
682 | if (!prot) { |
683 | return KProtocolInfo::T_NONE; |
684 | } |
685 | |
686 | return prot->m_outputType; |
687 | } |
688 | |
689 | bool KProtocolManager::isSourceProtocol(const QUrl &url) |
690 | { |
691 | KProtocolInfoPrivate *prot = findProtocol(url); |
692 | if (!prot) { |
693 | return false; |
694 | } |
695 | |
696 | return prot->m_isSourceProtocol; |
697 | } |
698 | |
699 | bool KProtocolManager::supportsListing(const QUrl &url) |
700 | { |
701 | KProtocolInfoPrivate *prot = findProtocol(url); |
702 | if (!prot) { |
703 | return false; |
704 | } |
705 | |
706 | return prot->m_supportsListing; |
707 | } |
708 | |
709 | QStringList KProtocolManager::listing(const QUrl &url) |
710 | { |
711 | KProtocolInfoPrivate *prot = findProtocol(url); |
712 | if (!prot) { |
713 | return QStringList(); |
714 | } |
715 | |
716 | return prot->m_listing; |
717 | } |
718 | |
719 | bool KProtocolManager::supportsReading(const QUrl &url) |
720 | { |
721 | KProtocolInfoPrivate *prot = findProtocol(url); |
722 | if (!prot) { |
723 | return false; |
724 | } |
725 | |
726 | return prot->m_supportsReading; |
727 | } |
728 | |
729 | bool KProtocolManager::supportsWriting(const QUrl &url) |
730 | { |
731 | KProtocolInfoPrivate *prot = findProtocol(url); |
732 | if (!prot) { |
733 | return false; |
734 | } |
735 | |
736 | return prot->m_supportsWriting; |
737 | } |
738 | |
739 | bool KProtocolManager::supportsMakeDir(const QUrl &url) |
740 | { |
741 | KProtocolInfoPrivate *prot = findProtocol(url); |
742 | if (!prot) { |
743 | return false; |
744 | } |
745 | |
746 | return prot->m_supportsMakeDir; |
747 | } |
748 | |
749 | bool KProtocolManager::supportsDeleting(const QUrl &url) |
750 | { |
751 | KProtocolInfoPrivate *prot = findProtocol(url); |
752 | if (!prot) { |
753 | return false; |
754 | } |
755 | |
756 | return prot->m_supportsDeleting; |
757 | } |
758 | |
759 | bool KProtocolManager::supportsLinking(const QUrl &url) |
760 | { |
761 | KProtocolInfoPrivate *prot = findProtocol(url); |
762 | if (!prot) { |
763 | return false; |
764 | } |
765 | |
766 | return prot->m_supportsLinking; |
767 | } |
768 | |
769 | bool KProtocolManager::supportsMoving(const QUrl &url) |
770 | { |
771 | KProtocolInfoPrivate *prot = findProtocol(url); |
772 | if (!prot) { |
773 | return false; |
774 | } |
775 | |
776 | return prot->m_supportsMoving; |
777 | } |
778 | |
779 | bool KProtocolManager::supportsOpening(const QUrl &url) |
780 | { |
781 | KProtocolInfoPrivate *prot = findProtocol(url); |
782 | if (!prot) { |
783 | return false; |
784 | } |
785 | |
786 | return prot->m_supportsOpening; |
787 | } |
788 | |
789 | bool KProtocolManager::supportsTruncating(const QUrl &url) |
790 | { |
791 | KProtocolInfoPrivate *prot = findProtocol(url); |
792 | if (!prot) { |
793 | return false; |
794 | } |
795 | |
796 | return prot->m_supportsTruncating; |
797 | } |
798 | |
799 | bool KProtocolManager::canCopyFromFile(const QUrl &url) |
800 | { |
801 | KProtocolInfoPrivate *prot = findProtocol(url); |
802 | if (!prot) { |
803 | return false; |
804 | } |
805 | |
806 | return prot->m_canCopyFromFile; |
807 | } |
808 | |
809 | bool KProtocolManager::canCopyToFile(const QUrl &url) |
810 | { |
811 | KProtocolInfoPrivate *prot = findProtocol(url); |
812 | if (!prot) { |
813 | return false; |
814 | } |
815 | |
816 | return prot->m_canCopyToFile; |
817 | } |
818 | |
819 | bool KProtocolManager::canRenameFromFile(const QUrl &url) |
820 | { |
821 | KProtocolInfoPrivate *prot = findProtocol(url); |
822 | if (!prot) { |
823 | return false; |
824 | } |
825 | |
826 | return prot->m_canRenameFromFile; |
827 | } |
828 | |
829 | bool KProtocolManager::canRenameToFile(const QUrl &url) |
830 | { |
831 | KProtocolInfoPrivate *prot = findProtocol(url); |
832 | if (!prot) { |
833 | return false; |
834 | } |
835 | |
836 | return prot->m_canRenameToFile; |
837 | } |
838 | |
839 | bool KProtocolManager::canDeleteRecursive(const QUrl &url) |
840 | { |
841 | KProtocolInfoPrivate *prot = findProtocol(url); |
842 | if (!prot) { |
843 | return false; |
844 | } |
845 | |
846 | return prot->m_canDeleteRecursive; |
847 | } |
848 | |
849 | KProtocolInfo::FileNameUsedForCopying KProtocolManager::fileNameUsedForCopying(const QUrl &url) |
850 | { |
851 | KProtocolInfoPrivate *prot = findProtocol(url); |
852 | if (!prot) { |
853 | return KProtocolInfo::FromUrl; |
854 | } |
855 | |
856 | return prot->m_fileNameUsedForCopying; |
857 | } |
858 | |
859 | QString KProtocolManager::defaultMimetype(const QUrl &url) |
860 | { |
861 | KProtocolInfoPrivate *prot = findProtocol(url); |
862 | if (!prot) { |
863 | return QString(); |
864 | } |
865 | |
866 | return prot->m_defaultMimetype; |
867 | } |
868 | |
869 | QString KProtocolManager::protocolForArchiveMimetype(const QString &mimeType) |
870 | { |
871 | KProtocolManagerPrivate *d = kProtocolManagerPrivate(); |
872 | QMutexLocker lock(&d->mutex); |
873 | if (d->protocolForArchiveMimetypes.isEmpty()) { |
874 | const QList<KProtocolInfoPrivate *> allProtocols = KProtocolInfoFactory::self()->allProtocols(); |
875 | for (KProtocolInfoPrivate *allProtocol : allProtocols) { |
876 | const QStringList archiveMimetypes = allProtocol->m_archiveMimeTypes; |
877 | for (const QString &mime : archiveMimetypes) { |
878 | d->protocolForArchiveMimetypes.insert(key: mime, value: allProtocol->m_name); |
879 | } |
880 | } |
881 | } |
882 | return d->protocolForArchiveMimetypes.value(key: mimeType); |
883 | } |
884 | |
885 | QString KProtocolManager::charsetFor(const QUrl &url) |
886 | { |
887 | return KIO::WorkerConfig::self()->configData(protocol: url.scheme(), host: url.host(), QStringLiteral("Charset" )); |
888 | } |
889 | |
890 | bool KProtocolManager::supportsPermissions(const QUrl &url) |
891 | { |
892 | KProtocolInfoPrivate *prot = findProtocol(url); |
893 | if (!prot) { |
894 | return true; |
895 | } |
896 | |
897 | return prot->m_supportsPermissions; |
898 | } |
899 | |
900 | #undef PRIVATE_DATA |
901 | |
902 | #include "moc_kprotocolmanager_p.cpp" |
903 | |