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

KDE's Doxygen guidelines are available online.