KIO

kprotocolmanager.cpp
1 /*
2  This file is part of the KDE libraries
3  SPDX-FileCopyrightText: 1999 Torben Weis <[email protected]>
4  SPDX-FileCopyrightText: 2000 Waldo Bastain <[email protected]>
5  SPDX-FileCopyrightText: 2000 Dawit Alemayehu <[email protected]>
6  SPDX-FileCopyrightText: 2008 JarosÅ‚aw Staniek <[email protected]>
7  SPDX-FileCopyrightText: 2022 Harald Sitter <[email protected]>
8 
9  SPDX-License-Identifier: LGPL-2.0-only
10 */
11 
12 #include "kprotocolmanager.h"
13 #include "kprotocolinfo_p.h"
14 
15 #include "hostinfo.h"
16 
17 #include <config-kiocore.h>
18 
19 #include <qplatformdefs.h>
20 #include <string.h>
21 #ifdef Q_OS_WIN
22 #include <qt_windows.h>
23 #undef interface // windows.h defines this, breaks QtDBus since it has parameters named interface
24 #else
25 #include <sys/utsname.h>
26 #endif
27 
28 #include <QCache>
29 #include <QCoreApplication>
30 #ifndef KIO_ANDROID_STUB
31 #include <QDBusInterface>
32 #include <QDBusReply>
33 #endif
34 #include <QHostAddress>
35 #include <QHostInfo>
36 #include <QLocale>
37 #include <QMimeDatabase>
38 #include <QRegularExpression>
39 #include <QSslSocket>
40 #include <QStandardPaths>
41 #include <QUrl>
42 
43 #if !defined(QT_NO_NETWORKPROXY) && (defined(Q_OS_WIN32) || defined(Q_OS_MAC))
44 #include <QNetworkProxyFactory>
45 #include <QNetworkProxyQuery>
46 #endif
47 
48 #include <KConfigGroup>
49 #include <KSharedConfig>
50 #include <kio_version.h>
51 
52 #include <kprotocolinfofactory_p.h>
53 
54 #include "http_worker_defaults.h"
55 #include "ioworker_defaults.h"
56 #include "workerconfig.h"
57 
59 
60 /*
61  Domain suffix match. E.g. return true if host is "cuzco.inka.de" and
62  nplist is "inka.de,hadiko.de" or if host is "localhost" and nplist is
63  "localhost".
64 */
65 static bool revmatch(const char *host, const char *nplist)
66 {
67  if (host == nullptr) {
68  return false;
69  }
70 
71  const char *hptr = host + strlen(host) - 1;
72  const char *nptr = nplist + strlen(nplist) - 1;
73  const char *shptr = hptr;
74 
75  while (nptr >= nplist) {
76  if (*hptr != *nptr) {
77  hptr = shptr;
78 
79  // Try to find another domain or host in the list
80  while (--nptr >= nplist && *nptr != ',' && *nptr != ' ') {
81  ;
82  }
83 
84  // Strip out multiple spaces and commas
85  while (--nptr >= nplist && (*nptr == ',' || *nptr == ' ')) {
86  ;
87  }
88  } else {
89  if (nptr == nplist || nptr[-1] == ',' || nptr[-1] == ' ') {
90  return true;
91  }
92  if (nptr[-1] == '/' && hptr == host) { // "bugs.kde.org" vs "http://bugs.kde.org", the config UI says URLs are ok
93  return true;
94  }
95  if (hptr == host) { // e.g. revmatch("bugs.kde.org","mybugs.kde.org")
96  return false;
97  }
98 
99  hptr--;
100  nptr--;
101  }
102  }
103 
104  return false;
105 }
106 
107 class KProxyData : public QObject
108 {
109  Q_OBJECT
110 public:
111  KProxyData(const QString &workerProtocol, const QStringList &proxyAddresses)
112  : protocol(workerProtocol)
113  , proxyList(proxyAddresses)
114  {
115  }
116 
117  void removeAddress(const QString &address)
118  {
119  proxyList.removeAll(address);
120  }
121 
122  QString protocol;
123  QStringList proxyList;
124 };
125 
126 class KProtocolManagerPrivate
127 {
128 public:
129  KProtocolManagerPrivate();
130  ~KProtocolManagerPrivate();
131  bool shouldIgnoreProxyFor(const QUrl &url);
132  void sync();
133  KProtocolManager::ProxyType proxyType();
134  bool useReverseProxy();
135  QString readNoProxyFor();
136  QString proxyFor(const QString &protocol);
137  QStringList getSystemProxyFor(const QUrl &url);
138 
139  QMutex mutex; // protects all member vars
140  KSharedConfig::Ptr configPtr;
141  KSharedConfig::Ptr http_config;
142  QString modifiers;
143  QString useragent;
144  QString noProxyFor;
145  QList<SubnetPair> noProxySubnets;
146  QCache<QString, KProxyData> cachedProxyData;
147 
148  QMap<QString /*mimetype*/, QString /*protocol*/> protocolForArchiveMimetypes;
149 };
150 
151 Q_GLOBAL_STATIC(KProtocolManagerPrivate, kProtocolManagerPrivate)
152 
153 static void syncOnExit()
154 {
155  if (kProtocolManagerPrivate.exists()) {
156  kProtocolManagerPrivate()->sync();
157  }
158 }
159 
160 KProtocolManagerPrivate::KProtocolManagerPrivate()
161 {
162  // post routine since KConfig::sync() breaks if called too late
163  qAddPostRoutine(syncOnExit);
164  cachedProxyData.setMaxCost(200); // double the max cost.
165 }
166 
167 KProtocolManagerPrivate::~KProtocolManagerPrivate()
168 {
169 }
170 
171 /*
172  * Returns true if url is in the no proxy list.
173  */
174 bool KProtocolManagerPrivate::shouldIgnoreProxyFor(const QUrl &url)
175 {
176  bool isMatch = false;
177  const KProtocolManager::ProxyType type = proxyType();
178  const bool useRevProxy = ((type == KProtocolManager::ManualProxy) && useReverseProxy());
179  const bool useNoProxyList = (type == KProtocolManager::ManualProxy || type == KProtocolManager::EnvVarProxy);
180 
181  // No proxy only applies to ManualProxy and EnvVarProxy types...
182  if (useNoProxyList && noProxyFor.isEmpty()) {
183  QStringList noProxyForList(readNoProxyFor().split(QLatin1Char(',')));
184  QMutableStringListIterator it(noProxyForList);
185  while (it.hasNext()) {
186  SubnetPair subnet = QHostAddress::parseSubnet(it.next());
187  if (!subnet.first.isNull()) {
188  noProxySubnets << subnet;
189  it.remove();
190  }
191  }
192  noProxyFor = noProxyForList.join(QLatin1Char(','));
193  }
194 
195  if (!noProxyFor.isEmpty()) {
196  QString qhost = url.host().toLower();
197  QByteArray host = qhost.toLatin1();
198  const QString qno_proxy = noProxyFor.trimmed().toLower();
199  const QByteArray no_proxy = qno_proxy.toLatin1();
200  isMatch = revmatch(host.constData(), no_proxy.constData());
201 
202  // If no match is found and the request url has a port
203  // number, try the combination of "host:port". This allows
204  // users to enter host:port in the No-proxy-For list.
205  if (!isMatch && url.port() > 0) {
206  qhost += QLatin1Char(':') + QString::number(url.port());
207  host = qhost.toLatin1();
208  isMatch = revmatch(host.constData(), no_proxy.constData());
209  }
210 
211  // If the hostname does not contain a dot, check if
212  // <local> is part of noProxy.
213  if (!isMatch && !host.isEmpty() && (strchr(host.constData(), '.') == nullptr)) {
214  isMatch = revmatch("<local>", no_proxy.constData());
215  }
216  }
217 
218  const QString host(url.host());
219 
220  if (!noProxySubnets.isEmpty() && !host.isEmpty()) {
222  // If request url is not IP address, do a DNS lookup of the hostname.
223  // TODO: Perhaps we should make configurable ?
224  if (address.isNull()) {
225  // qDebug() << "Performing DNS lookup for" << host;
227  const QList<QHostAddress> addresses = info.addresses();
228  if (!addresses.isEmpty()) {
229  address = addresses.first();
230  }
231  }
232 
233  if (!address.isNull()) {
234  for (const SubnetPair &subnet : std::as_const(noProxySubnets)) {
235  if (address.isInSubnet(subnet)) {
236  isMatch = true;
237  break;
238  }
239  }
240  }
241  }
242 
243  return (useRevProxy != isMatch);
244 }
245 
246 void KProtocolManagerPrivate::sync()
247 {
248  QMutexLocker lock(&mutex);
249  if (http_config) {
250  http_config->sync();
251  }
252  if (configPtr) {
253  configPtr->sync();
254  }
255 }
256 
258 {
259  KProtocolManagerPrivate *d = kProtocolManagerPrivate();
260  QMutexLocker lock(&d->mutex);
261  if (d->http_config) {
262  d->http_config->reparseConfiguration();
263  }
264  if (d->configPtr) {
265  d->configPtr->reparseConfiguration();
266  }
267  d->cachedProxyData.clear();
268  d->noProxyFor.clear();
269  d->modifiers.clear();
270  d->useragent.clear();
271  lock.unlock();
272 
273  // Force the slave config to re-read its config...
274  KIO::WorkerConfig::self()->reset();
275 }
276 
277 static KSharedConfig::Ptr config()
278 {
279  KProtocolManagerPrivate *d = kProtocolManagerPrivate();
280  Q_ASSERT(!d->mutex.tryLock()); // the caller must have locked the mutex
281  if (!d->configPtr) {
282  d->configPtr = KSharedConfig::openConfig(QStringLiteral("kioslaverc"), KConfig::NoGlobals);
283  }
284  return d->configPtr;
285 }
286 
287 KProtocolManager::ProxyType KProtocolManagerPrivate::proxyType()
288 {
289  KConfigGroup cg(config(), "Proxy Settings");
290  return static_cast<KProtocolManager::ProxyType>(cg.readEntry("ProxyType", 0));
291 }
292 
293 bool KProtocolManagerPrivate::useReverseProxy()
294 {
295  KConfigGroup cg(config(), "Proxy Settings");
296  return cg.readEntry("ReversedException", false);
297 }
298 
299 QString KProtocolManagerPrivate::readNoProxyFor()
300 {
301  QString noProxy = config()->group("Proxy Settings").readEntry("NoProxyFor");
302  if (proxyType() == KProtocolManager::EnvVarProxy) {
303  noProxy = QString::fromLocal8Bit(qgetenv(noProxy.toLocal8Bit().constData()));
304  }
305  return noProxy;
306 }
307 
308 QMap<QString, QString> KProtocolManager::entryMap(const QString &group)
309 {
310  KProtocolManagerPrivate *d = kProtocolManagerPrivate();
311  QMutexLocker lock(&d->mutex);
312  return config()->entryMap(group);
313 }
314 
315 static KConfigGroup http_config()
316 {
317  KProtocolManagerPrivate *d = kProtocolManagerPrivate();
318  Q_ASSERT(!d->mutex.tryLock()); // the caller must have locked the mutex
319  if (!d->http_config) {
320  d->http_config = KSharedConfig::openConfig(QStringLiteral("kio_httprc"), KConfig::NoGlobals);
321  }
322  return KConfigGroup(d->http_config, QString());
323 }
324 
325 /*=============================== TIMEOUT SETTINGS ==========================*/
326 
328 {
329  KProtocolManagerPrivate *d = kProtocolManagerPrivate();
330  QMutexLocker lock(&d->mutex);
331  KConfigGroup cg(config(), QString());
332  int val = cg.readEntry("ReadTimeout", DEFAULT_READ_TIMEOUT);
333  return qMax(MIN_TIMEOUT_VALUE, val);
334 }
335 
337 {
338  KProtocolManagerPrivate *d = kProtocolManagerPrivate();
339  QMutexLocker lock(&d->mutex);
340  KConfigGroup cg(config(), QString());
341  int val = cg.readEntry("ConnectTimeout", DEFAULT_CONNECT_TIMEOUT);
342  return qMax(MIN_TIMEOUT_VALUE, val);
343 }
344 
346 {
347  KProtocolManagerPrivate *d = kProtocolManagerPrivate();
348  QMutexLocker lock(&d->mutex);
349  KConfigGroup cg(config(), QString());
350  int val = cg.readEntry("ProxyConnectTimeout", DEFAULT_PROXY_CONNECT_TIMEOUT);
351  return qMax(MIN_TIMEOUT_VALUE, val);
352 }
353 
355 {
356  KProtocolManagerPrivate *d = kProtocolManagerPrivate();
357  QMutexLocker lock(&d->mutex);
358  KConfigGroup cg(config(), QString());
359  int val = cg.readEntry("ResponseTimeout", DEFAULT_RESPONSE_TIMEOUT);
360  return qMax(MIN_TIMEOUT_VALUE, val);
361 }
362 
363 /*========================== PROXY SETTINGS =================================*/
364 
366 {
367  return proxyType() != NoProxy;
368 }
369 
371 {
372  KProtocolManagerPrivate *d = kProtocolManagerPrivate();
373  QMutexLocker lock(&d->mutex);
374  return d->useReverseProxy();
375 }
376 
378 {
379  KProtocolManagerPrivate *d = kProtocolManagerPrivate();
380  QMutexLocker lock(&d->mutex);
381  return d->proxyType();
382 }
383 
385 {
386  KProtocolManagerPrivate *d = kProtocolManagerPrivate();
387  QMutexLocker lock(&d->mutex);
388  KConfigGroup cg(config(), "Proxy Settings");
389  return static_cast<ProxyAuthMode>(cg.readEntry("AuthMode", 0));
390 }
391 
392 /*========================== CACHING =====================================*/
393 
395 {
396  KProtocolManagerPrivate *d = kProtocolManagerPrivate();
397  QMutexLocker lock(&d->mutex);
398  return http_config().readEntry("UseCache", true);
399 }
400 
402 {
403  KProtocolManagerPrivate *d = kProtocolManagerPrivate();
404  QMutexLocker lock(&d->mutex);
405  QString tmp = http_config().readEntry("cache");
406  if (tmp.isEmpty()) {
407  return DEFAULT_CACHE_CONTROL;
408  }
409  return KIO::parseCacheControl(tmp);
410 }
411 
413 {
414  KProtocolManagerPrivate *d = kProtocolManagerPrivate();
415  QMutexLocker lock(&d->mutex);
417 }
418 
420 {
421  KProtocolManagerPrivate *d = kProtocolManagerPrivate();
422  QMutexLocker lock(&d->mutex);
423  return http_config().readEntry("MaxCacheAge", DEFAULT_MAX_CACHE_AGE);
424 }
425 
427 {
428  KProtocolManagerPrivate *d = kProtocolManagerPrivate();
429  QMutexLocker lock(&d->mutex);
430  return http_config().readEntry("MaxCacheSize", DEFAULT_MAX_CACHE_SIZE);
431 }
432 
434 {
435  KProtocolManagerPrivate *d = kProtocolManagerPrivate();
436  QMutexLocker lock(&d->mutex);
437  return d->readNoProxyFor();
438 }
439 
440 static QString adjustProtocol(const QString &scheme)
441 {
442  if (scheme.compare(QLatin1String("webdav"), Qt::CaseInsensitive) == 0) {
443  return QStringLiteral("http");
444  }
445 
446  if (scheme.compare(QLatin1String("webdavs"), Qt::CaseInsensitive) == 0) {
447  return QStringLiteral("https");
448  }
449 
450  return scheme.toLower();
451 }
452 
454 {
455  KProtocolManagerPrivate *d = kProtocolManagerPrivate();
456  QMutexLocker lock(&d->mutex);
457  return d->proxyFor(protocol);
458 }
459 
460 QString KProtocolManagerPrivate::proxyFor(const QString &protocol)
461 {
462  const QString key = adjustProtocol(protocol) + QLatin1String("Proxy");
463  QString proxyStr(config()->group("Proxy Settings").readEntry(key));
464  const int index = proxyStr.lastIndexOf(QLatin1Char(' '));
465 
466  if (index > -1) {
467  const QStringView portStr = QStringView(proxyStr).right(proxyStr.length() - index - 1);
468  const bool isDigits = std::all_of(portStr.cbegin(), portStr.cend(), [](const QChar c) {
469  return c.isDigit();
470  });
471 
472  if (isDigits) {
473  proxyStr = QStringView(proxyStr).left(index) + QLatin1Char(':') + portStr;
474  } else {
475  proxyStr.clear();
476  }
477  }
478 
479  return proxyStr;
480 }
481 
483 {
484  const QStringList proxies = proxiesForUrl(url);
485 
486  if (proxies.isEmpty()) {
487  return QString();
488  }
489 
490  return proxies.first();
491 }
492 
493 QStringList KProtocolManagerPrivate::getSystemProxyFor(const QUrl &url)
494 {
495  QStringList proxies;
496 
497 #if !defined(QT_NO_NETWORKPROXY) && (defined(Q_OS_WIN32) || defined(Q_OS_MAC))
498  QNetworkProxyQuery query(url);
500  proxies.reserve(proxyList.size());
501  for (const QNetworkProxy &proxy : proxyList) {
502  QUrl url;
503  const QNetworkProxy::ProxyType type = proxy.type();
504  if (type == QNetworkProxy::NoProxy || type == QNetworkProxy::DefaultProxy) {
505  proxies << QLatin1String("DIRECT");
506  continue;
507  }
508 
510  url.setScheme(QLatin1String("http"));
511  } else if (type == QNetworkProxy::Socks5Proxy) {
512  url.setScheme(QLatin1String("socks"));
513  } else if (type == QNetworkProxy::FtpCachingProxy) {
514  url.setScheme(QLatin1String("ftp"));
515  }
516 
517  url.setHost(proxy.hostName());
518  url.setPort(proxy.port());
519  url.setUserName(proxy.user());
520  proxies << url.url();
521  }
522 #else
523  // On Unix/Linux use system environment variables if any are set.
524  QString proxyVar(proxyFor(url.scheme()));
525  // Check for SOCKS proxy, if not proxy is found for given url.
526  if (!proxyVar.isEmpty()) {
527  const QString proxy(QString::fromLocal8Bit(qgetenv(proxyVar.toLocal8Bit().constData())).trimmed());
528  if (!proxy.isEmpty()) {
529  proxies << proxy;
530  }
531  }
532  // Add the socks proxy as an alternate proxy if it exists,
533  proxyVar = proxyFor(QStringLiteral("socks"));
534  if (!proxyVar.isEmpty()) {
535  QString proxy = QString::fromLocal8Bit(qgetenv(proxyVar.toLocal8Bit().constData())).trimmed();
536  // Make sure the scheme of SOCKS proxy is always set to "socks://".
537  const int index = proxy.indexOf(QLatin1String("://"));
538  const int offset = (index == -1) ? 0 : (index + 3);
539  proxy = QLatin1String("socks://") + QStringView(proxy).mid(offset);
540  if (!proxy.isEmpty()) {
541  proxies << proxy;
542  }
543  }
544 #endif
545  return proxies;
546 }
547 
549 {
550  QStringList proxyList;
551 
552  KProtocolManagerPrivate *d = kProtocolManagerPrivate();
553  QMutexLocker lock(&d->mutex);
554  if (!d->shouldIgnoreProxyFor(url)) {
555  switch (d->proxyType()) {
556  case PACProxy:
557  case WPADProxy: {
558  QUrl u(url);
559  const QString protocol = adjustProtocol(u.scheme());
560  u.setScheme(protocol);
561 
562 #ifndef KIO_ANDROID_STUB
563  if (protocol.startsWith(QLatin1String("http")) || protocol.startsWith(QLatin1String("ftp"))) {
565  QDBusInterface(QStringLiteral("org.kde.kded5"), QStringLiteral("/modules/proxyscout"), QStringLiteral("org.kde.KPAC.ProxyScout"))
566  .call(QStringLiteral("proxiesForUrl"), u.toString());
567  proxyList = reply;
568  }
569 #endif
570  break;
571  }
572  case EnvVarProxy:
573  proxyList = d->getSystemProxyFor(url);
574  break;
575  case ManualProxy: {
576  QString proxy(d->proxyFor(url.scheme()));
577  if (!proxy.isEmpty()) {
578  proxyList << proxy;
579  }
580  // Add the socks proxy as an alternate proxy if it exists,
581  proxy = d->proxyFor(QStringLiteral("socks"));
582  if (!proxy.isEmpty()) {
583  // Make sure the scheme of SOCKS proxy is always set to "socks://".
584  const int index = proxy.indexOf(QLatin1String("://"));
585  const int offset = (index == -1) ? 0 : (index + 3);
586  proxy = QLatin1String("socks://") + QStringView(proxy).mid(offset);
587  proxyList << proxy;
588  }
589  break;
590  }
591  case NoProxy:
592  break;
593  }
594  }
595 
596  if (proxyList.isEmpty()) {
597  proxyList << QStringLiteral("DIRECT");
598  }
599 
600  return proxyList;
601 }
602 
604 {
605 #ifndef KIO_ANDROID_STUB
606  QDBusInterface(QStringLiteral("org.kde.kded5"), QStringLiteral("/modules/proxyscout")).asyncCall(QStringLiteral("blackListProxy"), proxy);
607 #endif
608 
609  KProtocolManagerPrivate *d = kProtocolManagerPrivate();
610  QMutexLocker lock(&d->mutex);
611  const QStringList keys(d->cachedProxyData.keys());
612  for (const QString &key : keys) {
613  d->cachedProxyData[key]->removeAddress(proxy);
614  }
615 }
616 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 101)
618 {
619  return workerProtocol(url, proxy);
620 }
621 #endif
622 
624 {
625  QStringList proxyList;
626  const QString protocol = KProtocolManager::workerProtocol(url, proxyList);
627  if (!proxyList.isEmpty()) {
628  proxy = proxyList.first();
629  }
630  return protocol;
631 }
632 
633 // Generates proxy cache key from request given url.
634 static QString extractProxyCacheKeyFromUrl(const QUrl &u)
635 {
636  QString key = u.scheme();
637  key += u.host();
638 
639  if (u.port() > 0) {
640  key += QString::number(u.port());
641  }
642  return key;
643 }
644 
645 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 101)
647 {
648  return workerProtocol(url, proxyList);
649 }
650 #endif
651 
653 {
654  proxyList.clear();
655 
656  // Do not perform a proxy lookup for any url classified as a ":local" url or
657  // one that does not have a host component or if proxy is disabled.
658  QString protocol(url.scheme());
659  if (url.host().isEmpty() || KProtocolInfo::protocolClass(protocol) == QLatin1String(":local")
660  || KProtocolManager::proxyType() == KProtocolManager::NoProxy) {
661  return protocol;
662  }
663 
664  const QString proxyCacheKey = extractProxyCacheKeyFromUrl(url);
665 
666  KProtocolManagerPrivate *d = kProtocolManagerPrivate();
667  QMutexLocker lock(&d->mutex);
668  // Look for cached proxy information to avoid more work.
669  if (d->cachedProxyData.contains(proxyCacheKey)) {
670  KProxyData *data = d->cachedProxyData.object(proxyCacheKey);
671  proxyList = data->proxyList;
672  return data->protocol;
673  }
674  lock.unlock();
675 
676  const QStringList proxies = proxiesForUrl(url);
677  const int count = proxies.count();
678 
679  if (count > 0 && !(count == 1 && proxies.first() == QLatin1String("DIRECT"))) {
680  for (const QString &proxy : proxies) {
681  if (proxy == QLatin1String("DIRECT")) {
682  proxyList << proxy;
683  } else {
684  QUrl u(proxy);
685  if (!u.isEmpty() && u.isValid() && !u.scheme().isEmpty()) {
686  proxyList << proxy;
687  }
688  }
689  }
690  }
691 
692  // The idea behind worker protocols is not applicable to http
693  // and webdav protocols as well as protocols unknown to KDE.
694  /* clang-format off */
695  if (!proxyList.isEmpty()
696  && !protocol.startsWith(QLatin1String("http"))
697  && !protocol.startsWith(QLatin1String("webdav"))
698  && KProtocolInfo::isKnownProtocol(protocol)) { /* clang-format on */
699  for (const QString &proxy : std::as_const(proxyList)) {
700  QUrl u(proxy);
702  protocol = u.scheme();
703  break;
704  }
705  }
706  }
707 
708  lock.relock();
709  // cache the proxy information...
710  d->cachedProxyData.insert(proxyCacheKey, new KProxyData(protocol, proxyList));
711  return protocol;
712 }
713 
714 /*================================= USER-AGENT SETTINGS =====================*/
715 
717 {
718  const QString sendUserAgent = KIO::WorkerConfig::self()->configData(QStringLiteral("http"), hostname.toLower(), QStringLiteral("SendUserAgent")).toLower();
719  if (sendUserAgent == QLatin1String("false")) {
720  return QString();
721  }
722 
723  const QString useragent = KIO::WorkerConfig::self()->configData(QStringLiteral("http"), hostname.toLower(), QStringLiteral("UserAgent"));
724 
725  // Return the default user-agent if none is specified
726  // for the requested host.
727  if (useragent.isEmpty()) {
728  return defaultUserAgent();
729  }
730 
731  return useragent;
732 }
733 
735 {
736  const QString modifiers = KIO::WorkerConfig::self()->configData(QStringLiteral("http"), QString(), QStringLiteral("UserAgentKeys"));
737  return defaultUserAgent(modifiers);
738 }
739 
740 // This is not the OS, but the windowing system, e.g. X11 on Unix/Linux.
741 static QString platform()
742 {
743 #if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN)
744  return QStringLiteral("X11");
745 #elif defined(Q_OS_MAC)
746  return QStringLiteral("Macintosh");
747 #elif defined(Q_OS_WIN)
748  return QStringLiteral("Windows");
749 #else
750  return QStringLiteral("Unknown");
751 #endif
752 }
753 
755 {
756  KProtocolManagerPrivate *d = kProtocolManagerPrivate();
757  QMutexLocker lock(&d->mutex);
758  QString modifiers = _modifiers.toLower();
759  if (modifiers.isEmpty()) {
760  modifiers = QString::fromLatin1(DEFAULT_USER_AGENT_KEYS);
761  }
762 
763  if (d->modifiers == modifiers && !d->useragent.isEmpty()) {
764  return d->useragent;
765  }
766 
767  d->modifiers = modifiers;
768 
769  QString systemName;
770  QString systemVersion;
771  QString machine;
772  QString supp;
773  const bool sysInfoFound = getSystemNameVersionAndMachine(systemName, systemVersion, machine);
774 
775  supp += platform();
776 
777  if (sysInfoFound) {
778  if (modifiers.contains(QLatin1Char('o'))) {
779  supp += QLatin1String("; ") + systemName;
780  if (modifiers.contains(QLatin1Char('v'))) {
781  supp += QLatin1Char(' ') + systemVersion;
782  }
783 
784  if (modifiers.contains(QLatin1Char('m'))) {
785  supp += QLatin1Char(' ') + machine;
786  }
787  }
788 
789  if (modifiers.contains(QLatin1Char('l'))) {
790  supp += QLatin1String("; ") + QLocale::languageToString(QLocale().language());
791  }
792  }
793 
796  appName = QStringLiteral("KDE");
797  }
799  if (appVersion.isEmpty()) {
800  appVersion += QLatin1String(KIO_VERSION_STRING);
801  }
802 
803  d->useragent = QLatin1String("Mozilla/5.0 (%1) ").arg(supp)
804  + QLatin1String("KIO/%1.%2 ").arg(QString::number(KIO_VERSION_MAJOR), QString::number(KIO_VERSION_MINOR))
805  + QLatin1String("%1/%2").arg(appName, appVersion);
806 
807  // qDebug() << "USERAGENT STRING:" << d->useragent;
808  return d->useragent;
809 }
810 
811 QString KProtocolManager::userAgentForApplication(const QString &appName, const QString &appVersion, const QStringList &extraInfo)
812 {
813  QString systemName;
814  QString systemVersion;
815  QString machine;
816  QString info;
817 
818  if (getSystemNameVersionAndMachine(systemName, systemVersion, machine)) {
819  info += systemName + QLatin1Char('/') + systemVersion + QLatin1String("; ");
820  }
821 
822  info += QLatin1String("KDE/") + QStringLiteral(KIO_VERSION_STRING);
823 
824  if (!machine.isEmpty()) {
825  info += QLatin1String("; ") + machine;
826  }
827 
828  info += QLatin1String("; ") + extraInfo.join(QLatin1String("; "));
829 
830  return (appName + QLatin1Char('/') + appVersion + QStringLiteral(" (") + info + QLatin1Char(')'));
831 }
832 
833 bool KProtocolManager::getSystemNameVersionAndMachine(QString &systemName, QString &systemVersion, QString &machine)
834 {
835 #if defined(Q_OS_WIN) && !defined(_WIN32_WCE)
836  // we do not use unameBuf.sysname information constructed in kdewin32
837  // because we want to get separate name and version
838  systemName = QStringLiteral("Windows");
839  OSVERSIONINFOEX versioninfo;
840  ZeroMemory(&versioninfo, sizeof(OSVERSIONINFOEX));
841  // try calling GetVersionEx using the OSVERSIONINFOEX, if that fails, try using the OSVERSIONINFO
842  versioninfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
843  bool ok = GetVersionEx((OSVERSIONINFO *)&versioninfo);
844  if (!ok) {
845  versioninfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
846  ok = GetVersionEx((OSVERSIONINFO *)&versioninfo);
847  }
848  if (ok) {
849  systemVersion = QString::number(versioninfo.dwMajorVersion);
850  systemVersion += QLatin1Char('.');
851  systemVersion += QString::number(versioninfo.dwMinorVersion);
852  }
853 #else
854  struct utsname unameBuf;
855  if (0 != uname(&unameBuf)) {
856  return false;
857  }
858  systemName = QString::fromUtf8(unameBuf.sysname);
859  systemVersion = QString::fromUtf8(unameBuf.release);
860  machine = QString::fromUtf8(unameBuf.machine);
861 #endif
862  return true;
863 }
864 
866 {
867  const QLatin1String english("en");
868 
869  // User's desktop language preference.
870  QStringList languageList = QLocale().uiLanguages();
871 
872  // Replace possible "C" in the language list with "en", unless "en" is
873  // already present. This is to keep user's priorities in order.
874  // If afterwards "en" is still not present, append it.
875  int idx = languageList.indexOf(QLatin1String("C"));
876  if (idx != -1) {
877  if (languageList.contains(english)) {
878  languageList.removeAt(idx);
879  } else {
880  languageList[idx] = english;
881  }
882  }
883  if (!languageList.contains(english)) {
884  languageList += english;
885  }
886 
887  // Some languages may have web codes different from locale codes,
888  // read them from the config and insert in proper order.
889  KConfig acclangConf(QStringLiteral("accept-languages.codes"), KConfig::NoGlobals);
890  KConfigGroup replacementCodes(&acclangConf, "ReplacementCodes");
891  QStringList languageListFinal;
892  for (const QString &lang : std::as_const(languageList)) {
893  const QStringList langs = replacementCodes.readEntry(lang, QStringList());
894  if (langs.isEmpty()) {
895  languageListFinal += lang;
896  } else {
897  languageListFinal += langs;
898  }
899  }
900 
901  // The header is composed of comma separated languages, with an optional
902  // associated priority estimate (q=1..0) defaulting to 1.
903  // As our language tags are already sorted by priority, we'll just decrease
904  // the value evenly
905  int prio = 10;
906  QString header;
907  for (const QString &lang : std::as_const(languageListFinal)) {
908  header += lang;
909  if (prio < 10) {
910  header += QLatin1String(";q=0.") + QString::number(prio);
911  }
912  // do not add cosmetic whitespace in here : it is less compatible (#220677)
913  header += QLatin1Char(',');
914  if (prio > 1) {
915  --prio;
916  }
917  }
918  header.chop(1);
919 
920  // Some of the languages may have country specifier delimited by
921  // underscore, or modifier delimited by at-sign.
922  // The header should use dashes instead.
923  header.replace(QLatin1Char('_'), QLatin1Char('-'));
924  header.replace(QLatin1Char('@'), QLatin1Char('-'));
925 
926  return header;
927 }
928 
929 /*==================================== OTHERS ===============================*/
930 
932 {
933  KProtocolManagerPrivate *d = kProtocolManagerPrivate();
934  QMutexLocker lock(&d->mutex);
935  return config()->group(QByteArray()).readEntry("MarkPartial", true);
936 }
937 
939 {
940  KProtocolManagerPrivate *d = kProtocolManagerPrivate();
941  QMutexLocker lock(&d->mutex);
942  return config()->group(QByteArray()).readEntry("MinimumKeepSize",
943  DEFAULT_MINIMUM_KEEP_SIZE); // 5000 byte
944 }
945 
947 {
948  KProtocolManagerPrivate *d = kProtocolManagerPrivate();
949  QMutexLocker lock(&d->mutex);
950  return config()->group(QByteArray()).readEntry("AutoResume", false);
951 }
952 
954 {
955  KProtocolManagerPrivate *d = kProtocolManagerPrivate();
956  QMutexLocker lock(&d->mutex);
957  return config()->group(QByteArray()).readEntry("PersistentConnections", true);
958 }
959 
961 {
962  KProtocolManagerPrivate *d = kProtocolManagerPrivate();
963  QMutexLocker lock(&d->mutex);
964  return config()->group(QByteArray()).readEntry("PersistentProxyConnection", false);
965 }
966 
968 {
969  KProtocolManagerPrivate *d = kProtocolManagerPrivate();
970  QMutexLocker lock(&d->mutex);
971  return config()->group("Proxy Settings").readEntry("Proxy Config Script");
972 }
973 
974 /* =========================== PROTOCOL CAPABILITIES ============== */
975 
976 static KProtocolInfoPrivate *findProtocol(const QUrl &url)
977 {
978  if (!url.isValid()) {
979  return nullptr;
980  }
981  QString protocol = url.scheme();
982  if (!KProtocolInfo::proxiedBy(protocol).isEmpty()) {
983  QString dummy;
984  protocol = KProtocolManager::workerProtocol(url, dummy);
985  }
986 
987  return KProtocolInfoFactory::self()->findProtocol(protocol);
988 }
989 
991 {
992  KProtocolInfoPrivate *prot = findProtocol(url);
993  if (!prot) {
994  return KProtocolInfo::T_NONE;
995  }
996 
997  return prot->m_inputType;
998 }
999 
1001 {
1002  KProtocolInfoPrivate *prot = findProtocol(url);
1003  if (!prot) {
1004  return KProtocolInfo::T_NONE;
1005  }
1006 
1007  return prot->m_outputType;
1008 }
1009 
1011 {
1012  KProtocolInfoPrivate *prot = findProtocol(url);
1013  if (!prot) {
1014  return false;
1015  }
1016 
1017  return prot->m_isSourceProtocol;
1018 }
1019 
1021 {
1022  KProtocolInfoPrivate *prot = findProtocol(url);
1023  if (!prot) {
1024  return false;
1025  }
1026 
1027  return prot->m_supportsListing;
1028 }
1029 
1031 {
1032  KProtocolInfoPrivate *prot = findProtocol(url);
1033  if (!prot) {
1034  return QStringList();
1035  }
1036 
1037  return prot->m_listing;
1038 }
1039 
1041 {
1042  KProtocolInfoPrivate *prot = findProtocol(url);
1043  if (!prot) {
1044  return false;
1045  }
1046 
1047  return prot->m_supportsReading;
1048 }
1049 
1051 {
1052  KProtocolInfoPrivate *prot = findProtocol(url);
1053  if (!prot) {
1054  return false;
1055  }
1056 
1057  return prot->m_supportsWriting;
1058 }
1059 
1061 {
1062  KProtocolInfoPrivate *prot = findProtocol(url);
1063  if (!prot) {
1064  return false;
1065  }
1066 
1067  return prot->m_supportsMakeDir;
1068 }
1069 
1071 {
1072  KProtocolInfoPrivate *prot = findProtocol(url);
1073  if (!prot) {
1074  return false;
1075  }
1076 
1077  return prot->m_supportsDeleting;
1078 }
1079 
1081 {
1082  KProtocolInfoPrivate *prot = findProtocol(url);
1083  if (!prot) {
1084  return false;
1085  }
1086 
1087  return prot->m_supportsLinking;
1088 }
1089 
1091 {
1092  KProtocolInfoPrivate *prot = findProtocol(url);
1093  if (!prot) {
1094  return false;
1095  }
1096 
1097  return prot->m_supportsMoving;
1098 }
1099 
1101 {
1102  KProtocolInfoPrivate *prot = findProtocol(url);
1103  if (!prot) {
1104  return false;
1105  }
1106 
1107  return prot->m_supportsOpening;
1108 }
1109 
1111 {
1112  KProtocolInfoPrivate *prot = findProtocol(url);
1113  if (!prot) {
1114  return false;
1115  }
1116 
1117  return prot->m_supportsTruncating;
1118 }
1119 
1121 {
1122  KProtocolInfoPrivate *prot = findProtocol(url);
1123  if (!prot) {
1124  return false;
1125  }
1126 
1127  return prot->m_canCopyFromFile;
1128 }
1129 
1131 {
1132  KProtocolInfoPrivate *prot = findProtocol(url);
1133  if (!prot) {
1134  return false;
1135  }
1136 
1137  return prot->m_canCopyToFile;
1138 }
1139 
1141 {
1142  KProtocolInfoPrivate *prot = findProtocol(url);
1143  if (!prot) {
1144  return false;
1145  }
1146 
1147  return prot->m_canRenameFromFile;
1148 }
1149 
1151 {
1152  KProtocolInfoPrivate *prot = findProtocol(url);
1153  if (!prot) {
1154  return false;
1155  }
1156 
1157  return prot->m_canRenameToFile;
1158 }
1159 
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 
1181 {
1182  KProtocolInfoPrivate *prot = findProtocol(url);
1183  if (!prot) {
1184  return QString();
1185  }
1186 
1187  return prot->m_defaultMimetype;
1188 }
1189 
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 
1207 {
1208  return KIO::WorkerConfig::self()->configData(url.scheme(), url.host(), QStringLiteral("Charset"));
1209 }
1210 
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"
Q_OBJECTQ_OBJECT
T & first()
static bool supportsMakeDir(const QUrl &url)
Returns whether the protocol can create directories/folders.
QString readEntry(const char *key, const char *aDefault=nullptr) const
static bool canCopyToFile(const QUrl &url)
Returns whether the protocol can copy files/objects directly to the filesystem itself.
static bool canRenameFromFile(const QUrl &url)
Returns whether the protocol can rename (i.e.
static ProxyAuthMode proxyAuthMode()
Returns the way proxy authorization should be handled.
static bool supportsPermissions(const QUrl &url)
Returns whether the protocol suppports KIO/POSIX permissions handling.
static bool supportsReading(const QUrl &url)
Returns whether the protocol can retrieve data from URLs.
QString number(int n, int base)
QStringView right(qsizetype length) const const
@ T_NONE
no information about the type available
Definition: kprotocolinfo.h:84
QString fromUtf8(const char *str, int size)
QPair< QHostAddress, int > parseSubnet(const QString &subnet)
CaseInsensitive
static QStringList proxiesForUrl(const QUrl &url)
Returns all the possible proxy server addresses for url.
static void reparseConfiguration()
Force a reload of the general config file of KIO workers ( kioslaverc).
Type type(const QSqlDatabase &db)
QString scheme() const const
QList< QNetworkProxy > systemProxyForQuery(const QNetworkProxyQuery &query)
int count(const T &value) const const
ProxyAuthMode
Proxy authorization modes.
static int readTimeout()
Returns the preferred timeout value for reading from remote connections in seconds.
bool contains(const QString &str, Qt::CaseSensitivity cs) const const
QString trimmed() const const
QString url(QUrl::FormattingOptions options) const const
QString arg(Args &&... args) const const
QStringView mid(qsizetype start) const const
static QString slaveProtocol(const QUrl &url, QString &proxy)
Return the protocol to use in order to handle the given url It's usually the same,...
void chop(int n)
static bool canDeleteRecursive(const QUrl &url)
Returns whether the protocol can recursively delete directories by itself.
static bool supportsMoving(const QUrl &url)
Returns whether the protocol can move files/objects between different locations.
void setHost(const QString &host, QUrl::ParsingMode mode)
QString writableLocation(QStandardPaths::StandardLocation type)
QStringView::const_iterator cbegin() const const
QByteArray toLatin1() const const
static int maxCacheAge()
Returns the maximum age in seconds cached files should be kept before they are deleted as necessary.
Type
Describes the type of a protocol.
Definition: kprotocolinfo.h:81
QDBusMessage call(const QString &method, Args &&... args)
QStringView::const_iterator cend() const const
static bool supportsTruncating(const QUrl &url)
Returns whether the protocol can be truncated with FileJob::truncate(KIO::filesize_t length).
QStringView left(qsizetype length) const const
QDBusPendingCall asyncCall(const QString &method, Args &&... args)
static KProtocolInfo::FileNameUsedForCopying fileNameUsedForCopying(const QUrl &url)
This setting defines the strategy to use for generating a filename, when copying a file or directory ...
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
static QString workerProtocol(const QUrl &url, QString &proxy)
Return the protocol to use in order to handle the given url It's usually the same,...
void removeAt(int i)
Q_GLOBAL_STATIC(Internal::StaticControl, s_instance) class ControlPrivate
void setScheme(const QString &scheme)
static QString protocolForArchiveMimetype(const QString &mimeType)
Returns which protocol handles this MIME type, if it's an archive MIME type.
static bool isKnownProtocol(const QUrl &url)
Returns whether a protocol is installed that is able to handle url.
void reserve(int alloc)
QString languageToString(QLocale::Language language)
static bool isSourceProtocol(const QUrl &url)
Returns whether the protocol can act as a source protocol.
bool isValid() const const
int size() const const
static bool autoResume()
Returns true if partial downloads should be automatically resumed.
ProxyType
Types of proxy configuration.
static bool persistentProxyConnection()
Returns true if proxy connections should be persistent.
static bool useCache()
Returns true/false to indicate whether a cache should be used.
QString toString(QUrl::FormattingOptions options) const const
static QString charsetFor(const QUrl &url)
Returns the charset to use for the specified url.
QString fromLocal8Bit(const char *str, int size)
bool isEmpty() const const
static int connectTimeout()
Returns the preferred timeout value for remote connections in seconds.
static bool supportsOpening(const QUrl &url)
Returns whether the protocol can be opened using KIO::open(const QUrl&).
static ProxyType proxyType()
Returns the type of proxy configuration that is used.
static bool markPartial()
Returns true if partial downloads should be marked with a ".part" extension.
bool isEmpty() const const
static KProtocolInfo::Type outputType(const QUrl &url)
Returns whether the protocol should be treated as a filesystem or as a stream when writing to it.
QCA_EXPORT QString appName()
void setPort(int port)
static QString proxyConfigScript()
Returns the URL of the script for automatic proxy configuration.
bool isEmpty() const const
static QString defaultMimetype(const QUrl &url)
Returns default MIME type for this URL based on the protocol.
PostalAddress address(const QVariant &location)
static QString defaultUserAgent()
Returns the default user-agent string used for web browsing.
QString join(const QString &separator) const const
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
static QString proxyForUrl(const QUrl &url)
Returns the Proxy server address for a given URL.
KSharedConfigPtr config()
static bool canRenameToFile(const QUrl &url)
Returns whether the protocol can rename (i.e.
MetaData configData(const QString &protocol, const QString &host)
Query worker configuration for workers of type protocol when dealing with host.
int indexOf(QStringView str, int from) const const
static KIO::CacheControl cacheControl()
Returns the Cache control directive to be used.
QString & replace(int position, int n, QChar after)
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
static bool getSystemNameVersionAndMachine(QString &systemName, QString &systemVersion, QString &machine)
Returns system name, version and machine type, for example "Windows", "5.1", "i686".
ScriptableExtension * host() const
static bool supportsDeleting(const QUrl &url)
Returns whether the protocol can delete files/objects.
QString toLower() const const
QString host(QUrl::ComponentFormattingOptions options) const const
static bool useReverseProxy()
Returns whether or not the proxy server lookup should be reversed or not.
void reset()
Undo any changes made by calls to setConfigData.
const char * constData() const const
static bool canCopyFromFile(const QUrl &url)
Returns whether the protocol can copy files/objects directly from the filesystem itself.
QString fromLatin1(const char *str, int size)
static QStringList listing(const QUrl &url)
Returns the list of fields this protocol returns when listing The current possibilities are Name,...
QStringList uiLanguages() const const
static bool supportsListing(const QUrl &url)
Returns whether the protocol can list files/objects.
static void badProxy(const QString &proxy)
Marks this proxy as bad (down).
static QString proxyFor(const QString &protocol)
Returns the proxy server address for a given protocol.
static int responseTimeout()
Returns the preferred response timeout value for remote connecting in seconds.
int port(int defaultPort) const const
static QString acceptLanguagesHeader()
Return Accept-Languages header built up according to user's desktop language settings.
KIOCORE_EXPORT void lookupHost(const QString &hostName, QObject *receiver, const char *member)
Definition: hostinfo.cpp:246
void clear()
static int proxyConnectTimeout()
Returns the preferred timeout value for proxy connections in seconds.
static QString userAgentForHost(const QString &hostname)
Returns the user-agent string configured for the specified host.
static bool persistentConnections()
Returns true if connections should be persistent.
QString readPathEntry(const char *key, const QString &aDefault) const
bool sync() override
static KProtocolInfo::Type inputType(const QUrl &url)
Returns whether the protocol should be treated as a filesystem or as a stream when reading from it.
static QString cacheDir()
The directory which contains the cache files.
int compare(const QString &other, Qt::CaseSensitivity cs) const const
void setUserName(const QString &userName, QUrl::ParsingMode mode)
QByteArray toLocal8Bit() const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
static QString noProxyFor()
Returns the strings for hosts that should contacted DIRECTLY, bypassing any proxy settings.
QList< QHostAddress > addresses() const const
CacheControl
Specifies how to use the cache.
Definition: global.h:341
KIOCORE_EXPORT KIO::CacheControl parseCacheControl(const QString &cacheControl)
Parses the string representation of the cache control option.
Definition: global.cpp:158
static bool supportsLinking(const QUrl &url)
Returns whether the protocol can create links between files/objects.
static int maxCacheSize()
Returns the maximum size that can be used for caching.
static QString userAgentForApplication(const QString &appName, const QString &appVersion, const QStringList &extraInfo=QStringList())
Returns the application's user-agent string.
static bool supportsWriting(const QUrl &url)
Returns whether the protocol can store data to URLs.
static bool useProxy()
Returns whether or not the user specified the use of proxy server to make connections.
static QString proxiedBy(const QString &protocol)
Returns the name of the protocol through which the request will be routed if proxy support is enabled...
static QString protocolClass(const QString &protocol)
Returns the protocol class for the specified protocol.
static int minimumKeepSize()
Returns the minimum file size for keeping aborted downloads.
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon May 8 2023 03:54:43 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.