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 | |
25 | KProtocolInfoPrivate::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 = KJsonUtils::readTranslatedValue(jo: json, QStringLiteral("ExtraNames" )).toVariant().toStringList(); |
113 | const QStringList = 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 | |
139 | QStringList KProtocolInfo::protocols() |
140 | { |
141 | return KProtocolInfoFactory::self()->protocols(); |
142 | } |
143 | |
144 | bool 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 | |
155 | QString 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 | |
170 | QString 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 | |
181 | int 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 | |
191 | int 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 | |
201 | bool 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 | |
211 | QString 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 | |
220 | KProtocolInfo::ExtraFieldList KProtocolInfo::(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 | |
230 | QString 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 | |
240 | QString 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 | |
250 | QString 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 | |
260 | bool 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 | |
269 | QStringList 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 | |
279 | QStringList 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 | |
289 | QString 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 | |
299 | bool KProtocolInfo::isFilterProtocol(const QUrl &url) |
300 | { |
301 | return isFilterProtocol(protocol: url.scheme()); |
302 | } |
303 | |
304 | bool KProtocolInfo::isHelperProtocol(const QUrl &url) |
305 | { |
306 | return isHelperProtocol(protocol: url.scheme()); |
307 | } |
308 | |
309 | bool 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 | |
319 | bool KProtocolInfo::isKnownProtocol(const QUrl &url) |
320 | { |
321 | return isKnownProtocol(protocol: url.scheme()); |
322 | } |
323 | |
324 | bool 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 | |