1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 1999 Torben Weis <weis@kde.org>
4 SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
5 SPDX-FileCopyrightText: 2012 David Faure <faure@kde.org>
6 SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org>
7
8 SPDX-License-Identifier: LGPL-2.0-only
9*/
10
11#include "kprotocolinfo.h"
12#include "kprotocolinfo_p.h"
13#include "kprotocolinfofactory_p.h"
14
15#include "kiocoredebug.h"
16
17#include <KApplicationTrader>
18#include <KConfig>
19#include <KConfigGroup>
20#include <KJsonUtils>
21#include <KPluginMetaData>
22#include <KSharedConfig>
23#include <QUrl>
24
25KProtocolInfoPrivate::KProtocolInfoPrivate(const QString &name, const QString &exec, const QJsonObject &json)
26 : m_name(name)
27 , m_exec(exec)
28{
29 // source has fallback true if not set
30 m_isSourceProtocol = json.value(QStringLiteral("source")).toBool(defaultValue: true);
31 // true if not set for backwards compatibility
32 m_supportsPermissions = json.value(QStringLiteral("permissions")).toBool(defaultValue: true);
33
34 // other bools are fine with default false by toBool
35 m_isHelperProtocol = json.value(QStringLiteral("helper")).toBool();
36 m_supportsReading = json.value(QStringLiteral("reading")).toBool();
37 m_supportsWriting = json.value(QStringLiteral("writing")).toBool();
38 m_supportsMakeDir = json.value(QStringLiteral("makedir")).toBool();
39 m_supportsDeleting = json.value(QStringLiteral("deleting")).toBool();
40 m_supportsLinking = json.value(QStringLiteral("linking")).toBool();
41 m_supportsMoving = json.value(QStringLiteral("moving")).toBool();
42 m_supportsOpening = json.value(QStringLiteral("opening")).toBool();
43 m_supportsTruncating = json.value(QStringLiteral("truncating")).toBool();
44 m_canCopyFromFile = json.value(QStringLiteral("copyFromFile")).toBool();
45 m_canCopyToFile = json.value(QStringLiteral("copyToFile")).toBool();
46 m_canRenameFromFile = json.value(QStringLiteral("renameFromFile")).toBool();
47 m_canRenameToFile = json.value(QStringLiteral("renameToFile")).toBool();
48 m_canDeleteRecursive = json.value(QStringLiteral("deleteRecursive")).toBool();
49
50 // default is "FromURL"
51 const QString fnu = json.value(QStringLiteral("fileNameUsedForCopying")).toString();
52 m_fileNameUsedForCopying = KProtocolInfo::FromUrl;
53 if (fnu == QLatin1String("Name")) {
54 m_fileNameUsedForCopying = KProtocolInfo::Name;
55 } else if (fnu == QLatin1String("DisplayName")) {
56 m_fileNameUsedForCopying = KProtocolInfo::DisplayName;
57 }
58
59 m_listing = json.value(QStringLiteral("listing")).toVariant().toStringList();
60 // Many .protocol files say "Listing=false" when they really mean "Listing=" (i.e. unsupported)
61 if (m_listing.count() == 1 && m_listing.first() == QLatin1String("false")) {
62 m_listing.clear();
63 }
64 m_supportsListing = (m_listing.count() > 0);
65
66 m_defaultMimetype = json.value(QStringLiteral("defaultMimetype")).toString();
67
68 // determineMimetypeFromExtension has fallback true if not set
69 m_determineMimetypeFromExtension = json.value(QStringLiteral("determineMimetypeFromExtension")).toBool(defaultValue: true);
70
71 m_archiveMimeTypes = json.value(QStringLiteral("archiveMimetype")).toVariant().toStringList();
72
73 m_icon = json.value(QStringLiteral("Icon")).toString();
74
75 // config has fallback to name if not set
76 m_config = json.value(QStringLiteral("config")).toString(defaultValue: m_name);
77
78 // max workers has fallback to 1 if not set
79 m_maxWorkers = json.value(QStringLiteral("maxInstances")).toInt(defaultValue: 1);
80
81 m_maxWorkersPerHost = json.value(QStringLiteral("maxInstancesPerHost")).toInt();
82
83 QString tmp = json.value(QStringLiteral("input")).toString();
84 if (tmp == QLatin1String("filesystem")) {
85 m_inputType = KProtocolInfo::T_FILESYSTEM;
86 } else if (tmp == QLatin1String("stream")) {
87 m_inputType = KProtocolInfo::T_STREAM;
88 } else {
89 m_inputType = KProtocolInfo::T_NONE;
90 }
91
92 tmp = json.value(QStringLiteral("output")).toString();
93 if (tmp == QLatin1String("filesystem")) {
94 m_outputType = KProtocolInfo::T_FILESYSTEM;
95 } else if (tmp == QLatin1String("stream")) {
96 m_outputType = KProtocolInfo::T_STREAM;
97 } else {
98 m_outputType = KProtocolInfo::T_NONE;
99 }
100
101 m_docPath = json.value(QStringLiteral("X-DocPath")).toString();
102 if (m_docPath.isEmpty()) {
103 m_docPath = json.value(QStringLiteral("DocPath")).toString();
104 }
105
106 m_protClass = json.value(QStringLiteral("Class")).toString().toLower();
107 if (!m_protClass.startsWith(c: QLatin1Char(':'))) {
108 m_protClass.prepend(c: QLatin1Char(':'));
109 }
110
111 // ExtraNames is a translated value, use the KCoreAddons helper to read it
112 const QStringList extraNames = KJsonUtils::readTranslatedValue(jo: json, QStringLiteral("ExtraNames")).toVariant().toStringList();
113 const QStringList extraTypes = json.value(QStringLiteral("ExtraTypes")).toVariant().toStringList();
114 if (extraNames.size() == extraTypes.size()) {
115 auto func = [](const QString &name, const QString &type) {
116 const int metaType = QMetaType::fromName(name: type.toLatin1().constData()).id();
117 // currently QMetaType::Type and ExtraField::Type use the same subset of values, so we can just cast.
118 return KProtocolInfo::ExtraField(name, static_cast<KProtocolInfo::ExtraField::Type>(metaType));
119 };
120
121 std::transform(first1: extraNames.cbegin(), last1: extraNames.cend(), first2: extraTypes.cbegin(), result: std::back_inserter(x&: m_extraFields), binary_op: func);
122 } else {
123 qCWarning(KIO_CORE) << "Malformed JSON protocol file for protocol:" << name
124 << ", number of the ExtraNames fields should match the number of ExtraTypes fields";
125 }
126
127 // fallback based on class
128 m_showPreviews = json.value(QStringLiteral("ShowPreviews")).toBool(defaultValue: m_protClass == QLatin1String(":local"));
129
130 m_capabilities = json.value(QStringLiteral("Capabilities")).toVariant().toStringList();
131
132 m_proxyProtocol = json.value(QStringLiteral("ProxiedBy")).toString();
133}
134
135//
136// Static functions:
137//
138
139QStringList KProtocolInfo::protocols()
140{
141 return KProtocolInfoFactory::self()->protocols();
142}
143
144bool KProtocolInfo::isFilterProtocol(const QString &_protocol)
145{
146 // We call the findProtocol directly (not via KProtocolManager) to bypass any proxy settings.
147 KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(protocol: _protocol);
148 if (!prot) {
149 return false;
150 }
151
152 return !prot->m_isSourceProtocol;
153}
154
155QString KProtocolInfo::icon(const QString &_protocol)
156{
157 // We call the findProtocol directly (not via KProtocolManager) to bypass any proxy settings.
158 KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(protocol: _protocol);
159 if (!prot) {
160 if (auto service = KApplicationTrader::preferredService(mimeType: QLatin1String("x-scheme-handler/") + _protocol)) {
161 return service->icon();
162 } else {
163 return QString();
164 }
165 }
166
167 return prot->m_icon;
168}
169
170QString KProtocolInfo::config(const QString &_protocol)
171{
172 // We call the findProtocol directly (not via KProtocolManager) to bypass any proxy settings.
173 KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(protocol: _protocol);
174 if (!prot) {
175 return QString();
176 }
177
178 return QStringLiteral("kio_%1rc").arg(a: prot->m_config);
179}
180
181int KProtocolInfo::maxWorkers(const QString &_protocol)
182{
183 KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(protocol: _protocol);
184 if (!prot) {
185 return 1;
186 }
187
188 return prot->m_maxWorkers;
189}
190
191int KProtocolInfo::maxWorkersPerHost(const QString &_protocol)
192{
193 KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(protocol: _protocol);
194 if (!prot) {
195 return 0;
196 }
197
198 return prot->m_maxWorkersPerHost;
199}
200
201bool KProtocolInfo::determineMimetypeFromExtension(const QString &_protocol)
202{
203 KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(protocol: _protocol);
204 if (!prot) {
205 return true;
206 }
207
208 return prot->m_determineMimetypeFromExtension;
209}
210
211QString KProtocolInfo::exec(const QString &protocol)
212{
213 KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(protocol);
214 if (!prot) {
215 return QString();
216 }
217 return prot->m_exec;
218}
219
220KProtocolInfo::ExtraFieldList KProtocolInfo::extraFields(const QUrl &url)
221{
222 KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(protocol: url.scheme());
223 if (!prot) {
224 return ExtraFieldList();
225 }
226
227 return prot->m_extraFields;
228}
229
230QString KProtocolInfo::defaultMimetype(const QString &_protocol)
231{
232 KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(protocol: _protocol);
233 if (!prot) {
234 return QString();
235 }
236
237 return prot->m_defaultMimetype;
238}
239
240QString KProtocolInfo::docPath(const QString &_protocol)
241{
242 KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(protocol: _protocol);
243 if (!prot) {
244 return QString();
245 }
246
247 return prot->m_docPath;
248}
249
250QString KProtocolInfo::protocolClass(const QString &_protocol)
251{
252 KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(protocol: _protocol);
253 if (!prot) {
254 return QString();
255 }
256
257 return prot->m_protClass;
258}
259
260bool KProtocolInfo::showFilePreview(const QString &_protocol)
261{
262 KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(protocol: _protocol);
263 const bool defaultSetting = prot ? prot->m_showPreviews : false;
264
265 KConfigGroup group(KSharedConfig::openConfig(), QStringLiteral("PreviewSettings"));
266 return group.readEntry(key: _protocol, aDefault: defaultSetting);
267}
268
269QStringList KProtocolInfo::capabilities(const QString &_protocol)
270{
271 KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(protocol: _protocol);
272 if (!prot) {
273 return QStringList();
274 }
275
276 return prot->m_capabilities;
277}
278
279QStringList KProtocolInfo::archiveMimetypes(const QString &protocol)
280{
281 KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(protocol);
282 if (!prot) {
283 return QStringList();
284 }
285
286 return prot->m_archiveMimeTypes;
287}
288
289QString KProtocolInfo::proxiedBy(const QString &_protocol)
290{
291 KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(protocol: _protocol);
292 if (!prot) {
293 return QString();
294 }
295
296 return prot->m_proxyProtocol;
297}
298
299bool KProtocolInfo::isFilterProtocol(const QUrl &url)
300{
301 return isFilterProtocol(protocol: url.scheme());
302}
303
304bool KProtocolInfo::isHelperProtocol(const QUrl &url)
305{
306 return isHelperProtocol(protocol: url.scheme());
307}
308
309bool KProtocolInfo::isHelperProtocol(const QString &protocol)
310{
311 // We call the findProtocol directly (not via KProtocolManager) to bypass any proxy settings.
312 KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(protocol);
313 if (prot) {
314 return prot->m_isHelperProtocol;
315 }
316 return false;
317}
318
319bool KProtocolInfo::isKnownProtocol(const QUrl &url)
320{
321 return isKnownProtocol(protocol: url.scheme());
322}
323
324bool KProtocolInfo::isKnownProtocol(const QString &protocol, bool updateCacheIfNotfound)
325{
326 // We call the findProtocol (const QString&) to bypass any proxy settings.
327 KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(protocol, updateCacheIfNotfound);
328 return prot;
329}
330

source code of kio/src/core/kprotocolinfo.cpp