| 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 | |
| 7 | SPDX-License-Identifier: LGPL-2.0-only |
| 8 | */ |
| 9 | |
| 10 | #include "kprotocolinfo_p.h" |
| 11 | #include "kprotocolinfofactory_p.h" |
| 12 | |
| 13 | #include <KPluginMetaData> |
| 14 | |
| 15 | #include <QCoreApplication> |
| 16 | #include <QDirIterator> |
| 17 | #include <QStandardPaths> |
| 18 | |
| 19 | #include "kiocoredebug.h" |
| 20 | |
| 21 | Q_GLOBAL_STATIC(KProtocolInfoFactory, kProtocolInfoFactoryInstance) |
| 22 | |
| 23 | KProtocolInfoFactory *KProtocolInfoFactory::self() |
| 24 | { |
| 25 | return kProtocolInfoFactoryInstance(); |
| 26 | } |
| 27 | |
| 28 | KProtocolInfoFactory::KProtocolInfoFactory() |
| 29 | : m_cacheDirty(true) |
| 30 | { |
| 31 | } |
| 32 | |
| 33 | KProtocolInfoFactory::~KProtocolInfoFactory() |
| 34 | { |
| 35 | QMutexLocker locker(&m_mutex); |
| 36 | qDeleteAll(c: m_cache); |
| 37 | m_cache.clear(); |
| 38 | } |
| 39 | |
| 40 | QStringList KProtocolInfoFactory::protocols() |
| 41 | { |
| 42 | QMutexLocker locker(&m_mutex); |
| 43 | |
| 44 | // fill cache, if not already done and use it |
| 45 | fillCache(); |
| 46 | return m_cache.keys(); |
| 47 | } |
| 48 | |
| 49 | QList<KProtocolInfoPrivate *> KProtocolInfoFactory::allProtocols() |
| 50 | { |
| 51 | QMutexLocker locker(&m_mutex); |
| 52 | |
| 53 | // fill cache, if not already done and use it |
| 54 | fillCache(); |
| 55 | return m_cache.values(); |
| 56 | } |
| 57 | |
| 58 | KProtocolInfoPrivate *KProtocolInfoFactory::findProtocol(const QString &protocol, bool updateCacheIfNotfound) |
| 59 | { |
| 60 | Q_ASSERT(!protocol.isEmpty()); |
| 61 | Q_ASSERT(!protocol.contains(QLatin1Char(':'))); |
| 62 | |
| 63 | QMutexLocker locker(&m_mutex); |
| 64 | |
| 65 | const bool filled = fillCache(); |
| 66 | |
| 67 | KProtocolInfoPrivate *info = m_cache.value(key: protocol); |
| 68 | if (!info && !filled && updateCacheIfNotfound) { |
| 69 | // Unknown protocol! Maybe it just got installed and our cache is out of date? |
| 70 | qCDebug(KIO_CORE) << "Refilling KProtocolInfoFactory cache in the hope to find" << protocol; |
| 71 | m_cacheDirty = true; |
| 72 | fillCache(); |
| 73 | info = m_cache.value(key: protocol); |
| 74 | } |
| 75 | return info; |
| 76 | } |
| 77 | |
| 78 | bool KProtocolInfoFactory::fillCache() |
| 79 | { |
| 80 | // mutex MUST be locked from the outside! |
| 81 | Q_ASSERT(!m_mutex.tryLock()); |
| 82 | |
| 83 | // no work if filled |
| 84 | if (!m_cacheDirty) { |
| 85 | return false; |
| 86 | } |
| 87 | |
| 88 | qDeleteAll(c: m_cache); |
| 89 | m_cache.clear(); |
| 90 | |
| 91 | // first: search for meta data protocol info, that might be bundled with applications |
| 92 | // we search in all library paths inside kf5/kio |
| 93 | const QList<KPluginMetaData> plugins = KPluginMetaData::findPlugins(QStringLiteral("kf6/kio" )); |
| 94 | for (const KPluginMetaData &md : plugins) { |
| 95 | // get worker name & protocols it supports, if any |
| 96 | const QString workerPath = md.fileName(); |
| 97 | const QJsonObject protocols(md.rawData().value(QStringLiteral("KDE-KIO-Protocols" )).toObject()); |
| 98 | qCDebug(KIO_CORE) << workerPath << "supports protocols" << protocols.keys(); |
| 99 | |
| 100 | // add all protocols, does nothing if object invalid |
| 101 | for (auto it = protocols.begin(); it != protocols.end(); ++it) { |
| 102 | // skip empty objects |
| 103 | const QJsonObject protocol(it.value().toObject()); |
| 104 | if (protocol.isEmpty()) { |
| 105 | continue; |
| 106 | } |
| 107 | |
| 108 | // add to cache, skip double entries |
| 109 | if (!m_cache.contains(key: it.key())) { |
| 110 | m_cache.insert(key: it.key(), value: new KProtocolInfoPrivate(it.key(), workerPath, protocol)); |
| 111 | } |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | // all done, don't do it again |
| 116 | m_cacheDirty = false; |
| 117 | return true; |
| 118 | } |
| 119 | |