File indexing completed on 2024-04-28 15:26:26

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 
0007     SPDX-License-Identifier: LGPL-2.0-only
0008 */
0009 
0010 #include "kprotocolinfo_p.h"
0011 #include "kprotocolinfofactory_p.h"
0012 
0013 #include <KPluginMetaData>
0014 
0015 #include <QCoreApplication>
0016 #include <QDirIterator>
0017 #include <QStandardPaths>
0018 
0019 #include "kiocoredebug.h"
0020 
0021 Q_GLOBAL_STATIC(KProtocolInfoFactory, kProtocolInfoFactoryInstance)
0022 
0023 KProtocolInfoFactory *KProtocolInfoFactory::self()
0024 {
0025     return kProtocolInfoFactoryInstance();
0026 }
0027 
0028 KProtocolInfoFactory::KProtocolInfoFactory()
0029     : m_cacheDirty(true)
0030 {
0031 }
0032 
0033 KProtocolInfoFactory::~KProtocolInfoFactory()
0034 {
0035     QMutexLocker locker(&m_mutex);
0036     qDeleteAll(m_cache);
0037     m_cache.clear();
0038     m_cacheDirty = true;
0039 }
0040 
0041 QStringList KProtocolInfoFactory::protocols()
0042 {
0043     QMutexLocker locker(&m_mutex);
0044 
0045     // fill cache, if not already done and use it
0046     fillCache();
0047     return m_cache.keys();
0048 }
0049 
0050 QList<KProtocolInfoPrivate *> KProtocolInfoFactory::allProtocols()
0051 {
0052     QMutexLocker locker(&m_mutex);
0053 
0054     // fill cache, if not already done and use it
0055     fillCache();
0056     return m_cache.values();
0057 }
0058 
0059 KProtocolInfoPrivate *KProtocolInfoFactory::findProtocol(const QString &protocol)
0060 {
0061     Q_ASSERT(!protocol.isEmpty());
0062     Q_ASSERT(!protocol.contains(QLatin1Char(':')));
0063 
0064     QMutexLocker locker(&m_mutex);
0065 
0066     const bool filled = fillCache();
0067 
0068     KProtocolInfoPrivate *info = m_cache.value(protocol);
0069     if (!info && !filled) {
0070         // Unknown protocol! Maybe it just got installed and our cache is out of date?
0071         qCDebug(KIO_CORE) << "Refilling KProtocolInfoFactory cache in the hope to find" << protocol;
0072         m_cacheDirty = true;
0073         fillCache();
0074         info = m_cache.value(protocol);
0075     }
0076     return info;
0077 }
0078 
0079 bool KProtocolInfoFactory::fillCache()
0080 {
0081     // mutex MUST be locked from the outside!
0082     Q_ASSERT(!m_mutex.tryLock());
0083 
0084     // no work if filled
0085     if (!m_cacheDirty) {
0086         return false;
0087     }
0088 
0089     qDeleteAll(m_cache);
0090     m_cache.clear();
0091 
0092     // first: search for meta data protocol info, that might be bundled with applications
0093     // we search in all library paths inside kf5/kio
0094     const QVector<KPluginMetaData> plugins = KPluginMetaData::findPlugins(QStringLiteral("kf" QT_STRINGIFY(QT_VERSION_MAJOR) "/kio"));
0095     for (const KPluginMetaData &md : plugins) {
0096         // get worker name & protocols it supports, if any
0097         const QString workerPath = md.fileName();
0098         const QJsonObject protocols(md.rawData().value(QStringLiteral("KDE-KIO-Protocols")).toObject());
0099         qCDebug(KIO_CORE) << workerPath << "supports protocols" << protocols.keys();
0100 
0101         // add all protocols, does nothing if object invalid
0102         for (auto it = protocols.begin(); it != protocols.end(); ++it) {
0103             // skip empty objects
0104             const QJsonObject protocol(it.value().toObject());
0105             if (protocol.isEmpty()) {
0106                 continue;
0107             }
0108 
0109             // add to cache, skip double entries
0110             if (!m_cache.contains(it.key())) {
0111                 m_cache.insert(it.key(), new KProtocolInfoPrivate(it.key(), workerPath, protocol));
0112             }
0113         }
0114     }
0115 
0116 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 84)
0117     // second: fallback to .protocol files
0118     const QStringList serviceDirs =
0119         QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("kservices5"), QStandardPaths::LocateDirectory)
0120         << QCoreApplication::applicationDirPath() + QLatin1String("/kservices5");
0121     for (const QString &serviceDir : serviceDirs) {
0122         QDirIterator it(serviceDir);
0123         while (it.hasNext()) {
0124             const QString file = it.next();
0125             if (file.endsWith(QLatin1String(".protocol"))) {
0126                 const QString prot = it.fileInfo().baseName();
0127                 // add to cache, skip double entries
0128                 if (!m_cache.contains(prot)) {
0129                     qCDebug(KIO_CORE) << "Loading deprecated protocol file, please port it to JSON metadata" << file;
0130                     m_cache.insert(prot, new KProtocolInfoPrivate(file));
0131                 }
0132             }
0133         }
0134     }
0135 #endif
0136 
0137     // all done, don't do it again
0138     m_cacheDirty = false;
0139     return true;
0140 }