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

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 1999 Torben Weis <weis@kde.org>
0004     SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
0005     SPDX-FileCopyrightText: 2012 David Faure <faure@kde.org>
0006     SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org>
0007 
0008     SPDX-License-Identifier: LGPL-2.0-only
0009 */
0010 
0011 #include "kprotocolinfo.h"
0012 #include "kprotocolinfo_p.h"
0013 #include "kprotocolinfofactory_p.h"
0014 
0015 #include "kiocoredebug.h"
0016 
0017 #include <KApplicationTrader>
0018 #include <KConfig>
0019 #include <KConfigGroup>
0020 #include <KJsonUtils>
0021 #include <KPluginMetaData>
0022 #include <KSharedConfig>
0023 #include <QUrl>
0024 
0025 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 84)
0026 //
0027 // Internal functions:
0028 //
0029 KProtocolInfoPrivate::KProtocolInfoPrivate(const QString &path)
0030 {
0031     KConfig sconfig(path, KConfig::SimpleConfig);
0032     KConfigGroup config(&sconfig, "Protocol");
0033 
0034     m_name = config.readEntry("protocol");
0035     m_exec = config.readPathEntry("exec", QString());
0036     m_isSourceProtocol = config.readEntry("source", true);
0037     m_supportsPermissions = config.readEntry("permissions", true);
0038     m_isHelperProtocol = config.readEntry("helper", false);
0039     m_supportsReading = config.readEntry("reading", false);
0040     m_supportsWriting = config.readEntry("writing", false);
0041     m_supportsMakeDir = config.readEntry("makedir", false);
0042     m_supportsDeleting = config.readEntry("deleting", false);
0043     m_supportsLinking = config.readEntry("linking", false);
0044     m_supportsMoving = config.readEntry("moving", false);
0045     m_supportsOpening = config.readEntry("opening", false);
0046     m_supportsTruncating = config.readEntry("truncating", false);
0047     m_canCopyFromFile = config.readEntry("copyFromFile", false);
0048     m_canCopyToFile = config.readEntry("copyToFile", false);
0049     m_canRenameFromFile = config.readEntry("renameFromFile", false);
0050     m_canRenameToFile = config.readEntry("renameToFile", false);
0051     m_canDeleteRecursive = config.readEntry("deleteRecursive", false);
0052     const QString fnu = config.readEntry("fileNameUsedForCopying", "FromURL");
0053     m_fileNameUsedForCopying = KProtocolInfo::FromUrl;
0054     if (fnu == QLatin1String("Name")) {
0055         m_fileNameUsedForCopying = KProtocolInfo::Name;
0056     } else if (fnu == QLatin1String("DisplayName")) {
0057         m_fileNameUsedForCopying = KProtocolInfo::DisplayName;
0058     }
0059 
0060     m_listing = config.readEntry("listing", QStringList());
0061     // Many .protocol files say "Listing=false" when they really mean "Listing=" (i.e. unsupported)
0062     if (m_listing.count() == 1 && m_listing.first() == QLatin1String("false")) {
0063         m_listing.clear();
0064     }
0065     m_supportsListing = (m_listing.count() > 0);
0066     m_defaultMimetype = config.readEntry("defaultMimetype");
0067     m_determineMimetypeFromExtension = config.readEntry("determineMimetypeFromExtension", true);
0068     m_archiveMimeTypes = config.readEntry("archiveMimetype", QStringList());
0069     m_icon = config.readEntry("Icon");
0070     m_config = config.readEntry("config", m_name);
0071     m_maxWorkers = config.readEntry("maxInstances", 1);
0072     m_maxWorkersPerHost = config.readEntry("maxInstancesPerHost", 0);
0073 
0074     QString tmp = config.readEntry("input");
0075     if (tmp == QLatin1String("filesystem")) {
0076         m_inputType = KProtocolInfo::T_FILESYSTEM;
0077     } else if (tmp == QLatin1String("stream")) {
0078         m_inputType = KProtocolInfo::T_STREAM;
0079     } else {
0080         m_inputType = KProtocolInfo::T_NONE;
0081     }
0082 
0083     tmp = config.readEntry("output");
0084     if (tmp == QLatin1String("filesystem")) {
0085         m_outputType = KProtocolInfo::T_FILESYSTEM;
0086     } else if (tmp == QLatin1String("stream")) {
0087         m_outputType = KProtocolInfo::T_STREAM;
0088     } else {
0089         m_outputType = KProtocolInfo::T_NONE;
0090     }
0091 
0092     m_docPath = config.readPathEntry("X-DocPath", QString());
0093     if (m_docPath.isEmpty()) {
0094         m_docPath = config.readPathEntry("DocPath", QString());
0095     }
0096     m_protClass = config.readEntry("Class").toLower();
0097     if (!m_protClass.startsWith(QLatin1Char(':'))) {
0098         m_protClass.prepend(QLatin1Char(':'));
0099     }
0100 
0101     const QStringList extraNames = config.readEntry("ExtraNames", QStringList());
0102     const QStringList extraTypes = config.readEntry("ExtraTypes", QStringList());
0103     auto it = extraNames.cbegin();
0104     auto typeit = extraTypes.cbegin();
0105     for (; it != extraNames.cend() && typeit != extraTypes.cend(); ++it, ++typeit) {
0106         QVariant::Type type = QVariant::nameToType((*typeit).toLatin1().constData());
0107         // currently QVariant::Type and ExtraField::Type use the same subset of values, so we can just cast.
0108         m_extraFields.append(KProtocolInfo::ExtraField(*it, static_cast<KProtocolInfo::ExtraField::Type>(type)));
0109     }
0110 
0111     m_showPreviews = config.readEntry("ShowPreviews", m_protClass == QLatin1String(":local"));
0112 
0113     m_capabilities = config.readEntry("Capabilities", QStringList());
0114 
0115     m_slaveHandlesNotify = config.readEntry("slaveHandlesNotify", QStringList());
0116 
0117     m_proxyProtocol = config.readEntry("ProxiedBy");
0118 }
0119 #endif
0120 
0121 KProtocolInfoPrivate::KProtocolInfoPrivate(const QString &name, const QString &exec, const QJsonObject &json)
0122     : m_name(name)
0123     , m_exec(exec)
0124 {
0125     // source has fallback true if not set
0126     m_isSourceProtocol = json.value(QStringLiteral("source")).toBool(true);
0127     // true if not set for backwards compatibility
0128     m_supportsPermissions = json.value(QStringLiteral("permissions")).toBool(true);
0129 
0130     // other bools are fine with default false by toBool
0131     m_isHelperProtocol = json.value(QStringLiteral("helper")).toBool();
0132     m_supportsReading = json.value(QStringLiteral("reading")).toBool();
0133     m_supportsWriting = json.value(QStringLiteral("writing")).toBool();
0134     m_supportsMakeDir = json.value(QStringLiteral("makedir")).toBool();
0135     m_supportsDeleting = json.value(QStringLiteral("deleting")).toBool();
0136     m_supportsLinking = json.value(QStringLiteral("linking")).toBool();
0137     m_supportsMoving = json.value(QStringLiteral("moving")).toBool();
0138     m_supportsOpening = json.value(QStringLiteral("opening")).toBool();
0139     m_supportsTruncating = json.value(QStringLiteral("truncating")).toBool();
0140     m_canCopyFromFile = json.value(QStringLiteral("copyFromFile")).toBool();
0141     m_canCopyToFile = json.value(QStringLiteral("copyToFile")).toBool();
0142     m_canRenameFromFile = json.value(QStringLiteral("renameFromFile")).toBool();
0143     m_canRenameToFile = json.value(QStringLiteral("renameToFile")).toBool();
0144     m_canDeleteRecursive = json.value(QStringLiteral("deleteRecursive")).toBool();
0145 
0146     // default is "FromURL"
0147     const QString fnu = json.value(QStringLiteral("fileNameUsedForCopying")).toString();
0148     m_fileNameUsedForCopying = KProtocolInfo::FromUrl;
0149     if (fnu == QLatin1String("Name")) {
0150         m_fileNameUsedForCopying = KProtocolInfo::Name;
0151     } else if (fnu == QLatin1String("DisplayName")) {
0152         m_fileNameUsedForCopying = KProtocolInfo::DisplayName;
0153     }
0154 
0155     m_listing = json.value(QStringLiteral("listing")).toVariant().toStringList();
0156     // Many .protocol files say "Listing=false" when they really mean "Listing=" (i.e. unsupported)
0157     if (m_listing.count() == 1 && m_listing.first() == QLatin1String("false")) {
0158         m_listing.clear();
0159     }
0160     m_supportsListing = (m_listing.count() > 0);
0161 
0162     m_defaultMimetype = json.value(QStringLiteral("defaultMimetype")).toString();
0163 
0164     // determineMimetypeFromExtension has fallback true if not set
0165     m_determineMimetypeFromExtension = json.value(QStringLiteral("determineMimetypeFromExtension")).toBool(true);
0166 
0167     m_archiveMimeTypes = json.value(QStringLiteral("archiveMimetype")).toVariant().toStringList();
0168 
0169     m_icon = json.value(QStringLiteral("Icon")).toString();
0170 
0171     // config has fallback to name if not set
0172     m_config = json.value(QStringLiteral("config")).toString(m_name);
0173 
0174     // max workers has fallback to 1 if not set
0175     m_maxWorkers = json.value(QStringLiteral("maxInstances")).toInt(1);
0176 
0177     m_maxWorkersPerHost = json.value(QStringLiteral("maxInstancesPerHost")).toInt();
0178 
0179     QString tmp = json.value(QStringLiteral("input")).toString();
0180     if (tmp == QLatin1String("filesystem")) {
0181         m_inputType = KProtocolInfo::T_FILESYSTEM;
0182     } else if (tmp == QLatin1String("stream")) {
0183         m_inputType = KProtocolInfo::T_STREAM;
0184     } else {
0185         m_inputType = KProtocolInfo::T_NONE;
0186     }
0187 
0188     tmp = json.value(QStringLiteral("output")).toString();
0189     if (tmp == QLatin1String("filesystem")) {
0190         m_outputType = KProtocolInfo::T_FILESYSTEM;
0191     } else if (tmp == QLatin1String("stream")) {
0192         m_outputType = KProtocolInfo::T_STREAM;
0193     } else {
0194         m_outputType = KProtocolInfo::T_NONE;
0195     }
0196 
0197     m_docPath = json.value(QStringLiteral("X-DocPath")).toString();
0198     if (m_docPath.isEmpty()) {
0199         m_docPath = json.value(QStringLiteral("DocPath")).toString();
0200     }
0201 
0202     m_protClass = json.value(QStringLiteral("Class")).toString().toLower();
0203     if (!m_protClass.startsWith(QLatin1Char(':'))) {
0204         m_protClass.prepend(QLatin1Char(':'));
0205     }
0206 
0207     // ExtraNames is a translated value, use the KCoreAddons helper to read it
0208     const QStringList extraNames = KJsonUtils::readTranslatedValue(json, QStringLiteral("ExtraNames")).toVariant().toStringList();
0209     const QStringList extraTypes = json.value(QStringLiteral("ExtraTypes")).toVariant().toStringList();
0210     if (extraNames.size() == extraTypes.size()) {
0211         auto func = [](const QString &name, const QString &type) {
0212             QVariant::Type variantType = QVariant::nameToType(type.toLatin1().constData());
0213             // currently QVariant::Type and ExtraField::Type use the same subset of values, so we can just cast.
0214             return KProtocolInfo::ExtraField(name, static_cast<KProtocolInfo::ExtraField::Type>(variantType));
0215         };
0216 
0217         std::transform(extraNames.cbegin(), extraNames.cend(), extraTypes.cbegin(), std::back_inserter(m_extraFields), func);
0218     } else {
0219         qCWarning(KIO_CORE) << "Malformed JSON protocol file for protocol:" << name
0220                             << ", number of the ExtraNames fields should match the number of ExtraTypes fields";
0221     }
0222 
0223     // fallback based on class
0224     m_showPreviews = json.value(QStringLiteral("ShowPreviews")).toBool(m_protClass == QLatin1String(":local"));
0225 
0226     m_capabilities = json.value(QStringLiteral("Capabilities")).toVariant().toStringList();
0227 
0228 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 101)
0229     m_slaveHandlesNotify = json.value(QStringLiteral("slaveHandlesNotify")).toVariant().toStringList();
0230 #endif
0231 
0232     m_proxyProtocol = json.value(QStringLiteral("ProxiedBy")).toString();
0233 }
0234 
0235 //
0236 // Static functions:
0237 //
0238 
0239 QStringList KProtocolInfo::protocols()
0240 {
0241     return KProtocolInfoFactory::self()->protocols();
0242 }
0243 
0244 bool KProtocolInfo::isFilterProtocol(const QString &_protocol)
0245 {
0246     // We call the findProtocol directly (not via KProtocolManager) to bypass any proxy settings.
0247     KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(_protocol);
0248     if (!prot) {
0249         return false;
0250     }
0251 
0252     return !prot->m_isSourceProtocol;
0253 }
0254 
0255 QString KProtocolInfo::icon(const QString &_protocol)
0256 {
0257     // We call the findProtocol directly (not via KProtocolManager) to bypass any proxy settings.
0258     KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(_protocol);
0259     if (!prot) {
0260         if (auto service = KApplicationTrader::preferredService(QLatin1String("x-scheme-handler/") + _protocol)) {
0261             return service->icon();
0262         } else {
0263             return QString();
0264         }
0265     }
0266 
0267     return prot->m_icon;
0268 }
0269 
0270 QString KProtocolInfo::config(const QString &_protocol)
0271 {
0272     // We call the findProtocol directly (not via KProtocolManager) to bypass any proxy settings.
0273     KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(_protocol);
0274     if (!prot) {
0275         return QString();
0276     }
0277 
0278     return QStringLiteral("kio_%1rc").arg(prot->m_config);
0279 }
0280 
0281 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 101)
0282 int KProtocolInfo::maxSlaves(const QString &_protocol)
0283 {
0284     return maxWorkers(_protocol);
0285 }
0286 #endif
0287 
0288 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 101)
0289 int KProtocolInfo::maxSlavesPerHost(const QString &_protocol)
0290 {
0291     return maxWorkersPerHost(_protocol);
0292 }
0293 #endif
0294 
0295 int KProtocolInfo::maxWorkers(const QString &_protocol)
0296 {
0297     KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(_protocol);
0298     if (!prot) {
0299         return 1;
0300     }
0301 
0302     return prot->m_maxWorkers;
0303 }
0304 
0305 int KProtocolInfo::maxWorkersPerHost(const QString &_protocol)
0306 {
0307     KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(_protocol);
0308     if (!prot) {
0309         return 0;
0310     }
0311 
0312     return prot->m_maxWorkersPerHost;
0313 }
0314 
0315 bool KProtocolInfo::determineMimetypeFromExtension(const QString &_protocol)
0316 {
0317     KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(_protocol);
0318     if (!prot) {
0319         return true;
0320     }
0321 
0322     return prot->m_determineMimetypeFromExtension;
0323 }
0324 
0325 QString KProtocolInfo::exec(const QString &protocol)
0326 {
0327     KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(protocol);
0328     if (!prot) {
0329         return QString();
0330     }
0331     return prot->m_exec;
0332 }
0333 
0334 KProtocolInfo::ExtraFieldList KProtocolInfo::extraFields(const QUrl &url)
0335 {
0336     KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(url.scheme());
0337     if (!prot) {
0338         return ExtraFieldList();
0339     }
0340 
0341     return prot->m_extraFields;
0342 }
0343 
0344 QString KProtocolInfo::defaultMimetype(const QString &_protocol)
0345 {
0346     KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(_protocol);
0347     if (!prot) {
0348         return QString();
0349     }
0350 
0351     return prot->m_defaultMimetype;
0352 }
0353 
0354 QString KProtocolInfo::docPath(const QString &_protocol)
0355 {
0356     KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(_protocol);
0357     if (!prot) {
0358         return QString();
0359     }
0360 
0361     return prot->m_docPath;
0362 }
0363 
0364 QString KProtocolInfo::protocolClass(const QString &_protocol)
0365 {
0366     KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(_protocol);
0367     if (!prot) {
0368         return QString();
0369     }
0370 
0371     return prot->m_protClass;
0372 }
0373 
0374 bool KProtocolInfo::showFilePreview(const QString &_protocol)
0375 {
0376     KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(_protocol);
0377     const bool defaultSetting = prot ? prot->m_showPreviews : false;
0378 
0379     KConfigGroup group(KSharedConfig::openConfig(), "PreviewSettings");
0380     return group.readEntry(_protocol, defaultSetting);
0381 }
0382 
0383 QStringList KProtocolInfo::capabilities(const QString &_protocol)
0384 {
0385     KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(_protocol);
0386     if (!prot) {
0387         return QStringList();
0388     }
0389 
0390     return prot->m_capabilities;
0391 }
0392 
0393 QStringList KProtocolInfo::archiveMimetypes(const QString &protocol)
0394 {
0395     KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(protocol);
0396     if (!prot) {
0397         return QStringList();
0398     }
0399 
0400     return prot->m_archiveMimeTypes;
0401 }
0402 
0403 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 101)
0404 QStringList KProtocolInfo::slaveHandlesNotify(const QString &_protocol)
0405 {
0406     KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(_protocol);
0407     if (!prot) {
0408         return QStringList();
0409     }
0410 
0411     return prot->m_slaveHandlesNotify;
0412 }
0413 #endif
0414 
0415 QString KProtocolInfo::proxiedBy(const QString &_protocol)
0416 {
0417     KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(_protocol);
0418     if (!prot) {
0419         return QString();
0420     }
0421 
0422     return prot->m_proxyProtocol;
0423 }
0424 
0425 bool KProtocolInfo::isFilterProtocol(const QUrl &url)
0426 {
0427     return isFilterProtocol(url.scheme());
0428 }
0429 
0430 bool KProtocolInfo::isHelperProtocol(const QUrl &url)
0431 {
0432     return isHelperProtocol(url.scheme());
0433 }
0434 
0435 bool KProtocolInfo::isHelperProtocol(const QString &protocol)
0436 {
0437     // We call the findProtocol directly (not via KProtocolManager) to bypass any proxy settings.
0438     KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(protocol);
0439     if (prot) {
0440         return prot->m_isHelperProtocol;
0441     }
0442     return false;
0443 }
0444 
0445 bool KProtocolInfo::isKnownProtocol(const QUrl &url)
0446 {
0447     return isKnownProtocol(url.scheme());
0448 }
0449 
0450 bool KProtocolInfo::isKnownProtocol(const QString &protocol)
0451 {
0452     // We call the findProtocol (const QString&) to bypass any proxy settings.
0453     KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(protocol);
0454     return prot;
0455 }