File indexing completed on 2023-10-01 04:11:45

0001 /*
0002     SPDX-FileCopyrightText: 2010 Ryan Rix <ry@n.rix.si>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "pluginloader.h"
0008 
0009 #include <QPointer>
0010 #include <QStandardPaths>
0011 
0012 #include <KLazyLocalizedString>
0013 #include <KRuntimePlatform>
0014 #include <KService>
0015 #include <KServiceTypeTrader>
0016 #include <QDebug>
0017 #include <QRegularExpression>
0018 #include <kcoreaddons_export.h>
0019 #include <kpackage/packageloader.h>
0020 
0021 #include "config-plasma.h"
0022 
0023 #include "applet.h"
0024 #include "containment.h"
0025 #include "containmentactions.h"
0026 #include "dataengine.h"
0027 #include "debug_p.h"
0028 #include "package.h"
0029 #include "private/applet_p.h"
0030 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 83)
0031 #include "private/package_p.h"
0032 #include "private/packagestructure_p.h"
0033 #endif
0034 #include "private/service_p.h" // for NullService
0035 #include "private/storage_p.h"
0036 
0037 namespace Plasma
0038 {
0039 inline bool isContainmentMetaData(const KPluginMetaData &md)
0040 {
0041     return md.rawData().contains(QStringLiteral("X-Plasma-ContainmentType"))
0042 #if KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 89)
0043         || md.serviceTypes().contains(QLatin1String("Plasma/Containment"))
0044 #endif
0045         ;
0046 }
0047 static PluginLoader *s_pluginLoader = nullptr;
0048 
0049 class PluginLoaderPrivate
0050 {
0051 public:
0052     PluginLoaderPrivate()
0053         : isDefaultLoader(false)
0054     {
0055     }
0056 
0057     static QSet<QString> s_customCategories;
0058 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 83)
0059     QHash<QString, QPointer<PackageStructure>> structures;
0060 #endif
0061     bool isDefaultLoader;
0062 
0063     static QString s_dataEnginePluginDir;
0064     static QString s_packageStructurePluginDir;
0065     static QString s_plasmoidsPluginDir;
0066     static QString s_servicesPluginDir;
0067     static QString s_containmentActionsPluginDir;
0068 
0069     class Cache
0070     {
0071         // We only use this cache during start of the process to speed up many consecutive calls
0072         // After that, we're too afraid to produce race conditions and it's not that time-critical anyway
0073         // the 20 seconds here means that the cache is only used within 20sec during startup, after that,
0074         // complexity goes up and we'd have to update the cache in order to avoid subtle bugs
0075         // just not using the cache is way easier then, since it doesn't make *that* much of a difference,
0076         // anyway
0077         int maxCacheAge = 20;
0078         qint64 pluginCacheAge = 0;
0079         QHash<QString, KPluginMetaData> plugins;
0080 
0081     public:
0082         KPluginMetaData findPluginById(const QString &name, const QString &pluginNamespace);
0083     };
0084     Cache plasmoidCache;
0085     Cache dataengineCache;
0086     Cache containmentactionCache;
0087 };
0088 
0089 QSet<QString> PluginLoaderPrivate::s_customCategories;
0090 
0091 QString PluginLoaderPrivate::s_dataEnginePluginDir = QStringLiteral("plasma/dataengine");
0092 QString PluginLoaderPrivate::s_packageStructurePluginDir = QStringLiteral("plasma/packagestructure");
0093 QString PluginLoaderPrivate::s_plasmoidsPluginDir = QStringLiteral("plasma/applets");
0094 QString PluginLoaderPrivate::s_servicesPluginDir = QStringLiteral("plasma/services");
0095 QString PluginLoaderPrivate::s_containmentActionsPluginDir = QStringLiteral("plasma/containmentactions");
0096 
0097 PluginLoader::PluginLoader()
0098     : d(new PluginLoaderPrivate)
0099 {
0100 }
0101 
0102 PluginLoader::~PluginLoader()
0103 {
0104 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 83)
0105     for (const auto &wp : std::as_const(d->structures)) {
0106         delete wp;
0107     }
0108 #endif
0109     delete d;
0110 }
0111 
0112 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 86)
0113 void PluginLoader::setPluginLoader(PluginLoader *loader)
0114 {
0115     if (!s_pluginLoader) {
0116         s_pluginLoader = loader;
0117     }
0118 }
0119 #endif
0120 
0121 PluginLoader *PluginLoader::self()
0122 {
0123     if (!s_pluginLoader) {
0124         // we have been called before any PluginLoader was set, so just use the default
0125         // implementation. this prevents plugins from nefariously injecting their own
0126         // plugin loader if the app doesn't
0127         s_pluginLoader = new PluginLoader;
0128         s_pluginLoader->d->isDefaultLoader = true;
0129     }
0130 
0131     return s_pluginLoader;
0132 }
0133 
0134 Applet *PluginLoader::loadApplet(const QString &name, uint appletId, const QVariantList &args)
0135 {
0136     if (name.isEmpty()) {
0137         return nullptr;
0138     }
0139 
0140 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 86)
0141     Applet *applet = d->isDefaultLoader ? nullptr : internalLoadApplet(name, appletId, args);
0142     if (applet) {
0143         return applet;
0144     }
0145 #else
0146     Applet *applet = nullptr;
0147 #endif
0148 
0149     if (appletId == 0) {
0150         appletId = ++AppletPrivate::s_maxAppletId;
0151     }
0152 
0153     auto plugin = d->plasmoidCache.findPluginById(name, PluginLoaderPrivate::s_plasmoidsPluginDir);
0154     const KPackage::Package p = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Applet"), name);
0155 
0156     // If the applet is using another applet package, search for the plugin of the other applet
0157     if (!plugin.isValid()) {
0158         const QString parentPlugin = p.metadata().value(QStringLiteral("X-Plasma-RootPath"));
0159         if (!parentPlugin.isEmpty()) {
0160             plugin = d->plasmoidCache.findPluginById(parentPlugin, PluginLoaderPrivate::s_plasmoidsPluginDir);
0161         }
0162     }
0163 
0164     if (plugin.isValid()) {
0165         QPluginLoader loader(plugin.fileName());
0166         QVariantList allArgs = {QVariant::fromValue(p), loader.metaData().toVariantMap(), appletId};
0167         allArgs << args;
0168         if (KPluginFactory *factory = KPluginFactory::loadFactory(plugin).plugin) {
0169             if (factory->metaData().rawData().isEmpty()) {
0170                 factory->setMetaData(p.metadata());
0171             }
0172             applet = factory->create<Plasma::Applet>(nullptr, allArgs);
0173         }
0174     }
0175     if (applet) {
0176         return applet;
0177     }
0178 
0179     QVariantList allArgs;
0180     allArgs << QVariant::fromValue(p) << p.metadata().fileName() << appletId << args;
0181 
0182     if (isContainmentMetaData(p.metadata())) {
0183         applet = new Containment(nullptr, p.metadata(), allArgs);
0184     } else {
0185         applet = new Applet(nullptr, p.metadata(), allArgs);
0186     }
0187 
0188     const QString localePath = p.filePath("translations");
0189     if (!localePath.isEmpty()) {
0190         KLocalizedString::addDomainLocaleDir(QByteArray("plasma_applet_") + name.toLatin1(), localePath);
0191     }
0192     return applet;
0193 }
0194 
0195 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 94)
0196 DataEngine *PluginLoader::loadDataEngine(const QString &name)
0197 {
0198 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 86)
0199     DataEngine *engine = d->isDefaultLoader ? nullptr : internalLoadDataEngine(name);
0200     if (engine) {
0201         return engine;
0202     }
0203 #else
0204     DataEngine *engine = nullptr;
0205 #endif
0206 
0207     // Look for C++ plugins first
0208     KPluginMetaData plugin = d->dataengineCache.findPluginById(name, PluginLoaderPrivate::s_dataEnginePluginDir);
0209     if (plugin.isValid()) {
0210         const QVariantList args{QVariant::fromValue(plugin)};
0211         engine = KPluginFactory::instantiatePlugin<Plasma::DataEngine>(plugin, nullptr, args).plugin;
0212     }
0213     if (engine) {
0214         return engine;
0215     }
0216 
0217     const KPackage::Package p = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/DataEngine"), name);
0218     if (!p.isValid()) {
0219         return nullptr;
0220     }
0221 
0222     return new DataEngine(p.metadata(), nullptr);
0223 }
0224 #endif
0225 
0226 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 94)
0227 QStringList PluginLoader::listAllEngines(const QString &parentApp)
0228 {
0229     QStringList engines;
0230     // Look for C++ plugins first
0231     auto filter = [&parentApp](const KPluginMetaData &md) -> bool {
0232         return md.value(QStringLiteral("X-KDE-ParentApp")) == parentApp;
0233     };
0234     QVector<KPluginMetaData> plugins;
0235     if (parentApp.isEmpty()) {
0236         plugins = KPluginMetaData::findPlugins(PluginLoaderPrivate::s_dataEnginePluginDir);
0237     } else {
0238         plugins = KPluginMetaData::findPlugins(PluginLoaderPrivate::s_dataEnginePluginDir, filter);
0239     }
0240 
0241     for (auto &plugin : std::as_const(plugins)) {
0242         engines << plugin.pluginId();
0243     }
0244 
0245     const QList<KPluginMetaData> packagePlugins = KPackage::PackageLoader::self()->listPackages(QStringLiteral("Plasma/DataEngine"));
0246     for (const auto &plugin : packagePlugins) {
0247         engines << plugin.pluginId();
0248     }
0249 
0250     return engines;
0251 }
0252 #endif
0253 
0254 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 77)
0255 KPluginInfo::List PluginLoader::listEngineInfo(const QString &parentApp)
0256 {
0257     return PluginLoader::self()->listDataEngineInfo(parentApp);
0258 }
0259 #endif
0260 
0261 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 81)
0262 KPluginInfo::List PluginLoader::listEngineInfoByCategory(const QString &category, const QString &parentApp)
0263 {
0264     KPluginInfo::List list;
0265 
0266     // Look for C++ plugins first
0267     auto filterNormal = [&category](const KPluginMetaData &md) -> bool {
0268         return md.value(QStringLiteral("X-KDE-PluginInfo-Category")) == category;
0269     };
0270     auto filterParentApp = [&category, &parentApp](const KPluginMetaData &md) -> bool {
0271         return md.value(QStringLiteral("X-KDE-ParentApp")) == parentApp //
0272             && md.value(QStringLiteral("X-KDE-PluginInfo-Category")) == category;
0273     };
0274     QVector<KPluginMetaData> plugins;
0275     if (parentApp.isEmpty()) {
0276         plugins = KPluginMetaData::findPlugins(PluginLoaderPrivate::s_dataEnginePluginDir, filterNormal);
0277     } else {
0278         plugins = KPluginMetaData::findPlugins(PluginLoaderPrivate::s_dataEnginePluginDir, filterParentApp);
0279     }
0280 
0281     list = KPluginInfo::fromMetaData(plugins);
0282 
0283     // TODO FIXME: PackageLoader needs to have a function to inject packageStructures
0284     const QList<KPluginMetaData> packagePlugins = KPackage::PackageLoader::self()->listPackages(QStringLiteral("Plasma/DataEngine"));
0285     list << KPluginInfo::fromMetaData(packagePlugins.toVector());
0286 
0287     return list;
0288 }
0289 #endif
0290 
0291 Service *PluginLoader::loadService(const QString &name, const QVariantList &args, QObject *parent)
0292 {
0293 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 86)
0294     Service *service = d->isDefaultLoader ? nullptr : internalLoadService(name, args, parent);
0295     if (service) {
0296         return service;
0297     }
0298 #else
0299     Service *service = nullptr;
0300 #endif
0301 
0302     // TODO: scripting API support
0303     if (name.isEmpty()) {
0304         return new NullService(QString(), parent);
0305     } else if (name == QLatin1String("org.kde.servicestorage")) {
0306         return new Storage(parent);
0307     }
0308 
0309     // Look for C++ plugins first
0310     KPluginMetaData plugin = KPluginMetaData::findPluginById(PluginLoaderPrivate::s_servicesPluginDir, name);
0311     if (plugin.isValid()) {
0312         service = KPluginFactory::instantiatePlugin<Plasma::Service>(plugin, parent, args).plugin;
0313     }
0314 
0315     if (service) {
0316         if (service->name().isEmpty()) {
0317             service->setName(name);
0318         }
0319         return service;
0320     } else {
0321         return new NullService(name, parent);
0322     }
0323 }
0324 
0325 ContainmentActions *PluginLoader::loadContainmentActions(Containment *parent, const QString &name, const QVariantList &args)
0326 {
0327     if (name.isEmpty()) {
0328         return nullptr;
0329     }
0330 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 86)
0331     ContainmentActions *actions = d->isDefaultLoader ? nullptr : internalLoadContainmentActions(parent, name, args);
0332     if (actions) {
0333         return actions;
0334     }
0335 #endif
0336 
0337     KPluginMetaData plugin = d->containmentactionCache.findPluginById(name, PluginLoaderPrivate::s_containmentActionsPluginDir);
0338 
0339     if (plugin.isValid()) {
0340         if (auto res = KPluginFactory::instantiatePlugin<Plasma::ContainmentActions>(plugin, nullptr, {QVariant::fromValue(plugin)})) {
0341             return res.plugin;
0342         }
0343     }
0344 
0345 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 88)
0346     QString constraint = QStringLiteral("[X-KDE-PluginInfo-Name] == '%1'").arg(name);
0347     KService::List offers = KServiceTypeTrader::self()->query(QStringLiteral("Plasma/ContainmentActions"), constraint);
0348 
0349     if (offers.isEmpty()) {
0350 #ifndef NDEBUG
0351         qCDebug(LOG_PLASMA) << "offers is empty for " << name;
0352 #endif
0353         return nullptr;
0354     }
0355 
0356     KService::Ptr offer = offers.first();
0357     qCWarning(LOG_PLASMA) << "Plugin" << name << "was loaded using deprecated KServiceTypeTrader."
0358                           << "Use embedded json metadata and install the plugin in plasma/containmentactions namespace instead";
0359 
0360     KPluginMetaData data(offer->library());
0361     QVariantList allArgs;
0362     allArgs << offer->storageId() << args;
0363 
0364     return KPluginFactory::instantiatePlugin<Plasma::ContainmentActions>(data, nullptr, allArgs).plugin;
0365 #else
0366     return nullptr;
0367 #endif
0368 }
0369 
0370 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 83)
0371 Package PluginLoader::loadPackage(const QString &packageFormat, const QString &specialization)
0372 {
0373     if (!d->isDefaultLoader) {
0374         Package p = internalLoadPackage(packageFormat, specialization);
0375         if (p.hasValidStructure()) {
0376             return p;
0377         }
0378     }
0379 
0380     if (packageFormat.isEmpty()) {
0381         return Package();
0382     }
0383 
0384     const QString hashkey = packageFormat + QLatin1Char('%') + specialization;
0385     PackageStructure *structure = d->structures.value(hashkey).data();
0386 
0387     if (structure) {
0388         return Package(structure);
0389     }
0390 
0391     KPackage::PackageStructure *internalStructure = KPackage::PackageLoader::self()->loadPackageStructure(packageFormat);
0392 
0393     if (internalStructure) {
0394         structure = new PackageStructure();
0395         structure->d->internalStructure = internalStructure;
0396         // fallback to old structures
0397     } else {
0398         auto filter = [packageFormat](const KPluginMetaData &md) -> bool {
0399             return md.value(QStringLiteral("X-KDE-PluginInfo-Name")) == packageFormat;
0400         };
0401 
0402         const QVector<KPluginMetaData> plugins = KPluginMetaData::findPlugins(PluginLoaderPrivate::s_packageStructurePluginDir, filter);
0403 
0404         if (!plugins.isEmpty()) {
0405             if (auto res = KPluginFactory::instantiatePlugin<Plasma::PackageStructure>(plugins.first())) {
0406                 structure = res.plugin;
0407             } else {
0408                 qWarning() << "Error loading plugin:" << res.errorString;
0409             }
0410 
0411             if (structure) {
0412                 structure->d->internalStructure = new PackageStructureWrapper(structure);
0413             }
0414         }
0415     }
0416 
0417     if (structure) {
0418         d->structures.insert(hashkey, structure);
0419         return Package(structure);
0420     }
0421 
0422     return Package();
0423 }
0424 #endif
0425 QList<KPluginMetaData> listAppletMetaDataInternal(const QString &category, const QString &parentApp)
0426 {
0427     auto platforms = KRuntimePlatform::runtimePlatform();
0428     // For now desktop always lists everything
0429     if (platforms.contains(QStringLiteral("desktop"))) {
0430         platforms.clear();
0431     }
0432 
0433     // FIXME: this assumes we are always use packages.. no pure c++
0434     std::function<bool(const KPluginMetaData &)> filter;
0435     if (category.isEmpty()) { // use all but the excluded categories
0436         KConfigGroup group(KSharedConfig::openConfig(), "General");
0437         QStringList excluded = group.readEntry("ExcludeCategories", QStringList());
0438 
0439         filter = [excluded, parentApp, platforms](const KPluginMetaData &md) -> bool {
0440             if (!platforms.isEmpty() && !md.formFactors().isEmpty()) {
0441                 bool found = false;
0442                 for (const auto &plat : platforms) {
0443                     if (md.formFactors().contains(plat)) {
0444                         found = true;
0445                         break;
0446                     }
0447                 }
0448 
0449                 if (!found) {
0450                     return false;
0451                 }
0452             }
0453 
0454             const QString pa = md.value(QStringLiteral("X-KDE-ParentApp"));
0455             return (parentApp.isEmpty() || pa == parentApp) && !excluded.contains(md.category());
0456         };
0457     } else { // specific category (this could be an excluded one - is that bad?)
0458 
0459         filter = [category, parentApp, platforms](const KPluginMetaData &md) -> bool {
0460             if (!platforms.isEmpty() && !md.formFactors().isEmpty()) {
0461                 bool found = false;
0462                 for (const auto &plat : platforms) {
0463                     if (md.formFactors().contains(plat)) {
0464                         found = true;
0465                         break;
0466                     }
0467                 }
0468 
0469                 if (!found) {
0470                     return false;
0471                 }
0472             }
0473 
0474             const QString pa = md.value(QStringLiteral("X-KDE-ParentApp"));
0475 
0476             if (category == QLatin1String("Miscellaneous")) {
0477                 return (parentApp.isEmpty() || pa == parentApp) && (md.category() == category || md.category().isEmpty());
0478             } else {
0479                 return (parentApp.isEmpty() || pa == parentApp) && md.category() == category;
0480             }
0481         };
0482     }
0483 
0484     return KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), filter);
0485 }
0486 
0487 QList<KPluginMetaData> PluginLoader::listAppletMetaData(const QString &category)
0488 {
0489     return listAppletMetaDataInternal(category, QString());
0490 }
0491 
0492 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 88)
0493 QList<KPluginMetaData> PluginLoader::listAppletMetaData(const QString &category, const QString &parentApp)
0494 {
0495     return listAppletMetaDataInternal(category, parentApp);
0496 }
0497 #endif
0498 
0499 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 28)
0500 KPluginInfo::List PluginLoader::listAppletInfo(const QString &category, const QString &parentApp)
0501 {
0502     const auto plugins = listAppletMetaData(category, parentApp);
0503 
0504 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 0)
0505     KPluginInfo::List list;
0506     // NOTE: it still produces kplugininfos from KServices because some user code expects
0507     // info.service() to be valid and would crash otherwise
0508     for (const auto &md : plugins) {
0509         QT_WARNING_PUSH
0510         QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
0511         QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations")
0512         auto pi = md.metaDataFileName().endsWith(QLatin1String(".json")) ? KPluginInfo(md) : KPluginInfo(KService::serviceByStorageId(md.metaDataFileName()));
0513         QT_WARNING_POP
0514         if (!pi.isValid()) {
0515             qCWarning(LOG_PLASMA) << "Could not load plugin info for plugin :" << md.pluginId() << "skipping plugin";
0516             continue;
0517         }
0518         list << pi;
0519     }
0520     return list;
0521 #else
0522     return KPluginInfo::fromMetaData(plugins.toVector());
0523 #endif
0524 }
0525 #endif
0526 
0527 QList<KPluginMetaData> PluginLoader::listAppletMetaDataForMimeType(const QString &mimeType)
0528 {
0529     auto filter = [&mimeType](const KPluginMetaData &md) -> bool {
0530         return md.value(QStringLiteral("X-Plasma-DropMimeTypes"), QStringList()).contains(mimeType);
0531     };
0532     return KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), filter);
0533 }
0534 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 83)
0535 KPluginInfo::List PluginLoader::listAppletInfoForMimeType(const QString &mimeType)
0536 {
0537     return KPluginInfo::fromMetaData(listAppletMetaDataForMimeType(mimeType).toVector());
0538 }
0539 #endif
0540 
0541 QList<KPluginMetaData> PluginLoader::listAppletMetaDataForUrl(const QUrl &url)
0542 {
0543     QString parentApp;
0544     QCoreApplication *app = QCoreApplication::instance();
0545     if (app) {
0546         parentApp = app->applicationName();
0547     }
0548 
0549     auto filter = [&parentApp](const KPluginMetaData &md) -> bool {
0550         const QString pa = md.value(QStringLiteral("X-KDE-ParentApp"));
0551         return (parentApp.isEmpty() || pa == parentApp) //
0552             && !md.value(QStringLiteral("X-Plasma-DropUrlPatterns"), QStringList()).isEmpty();
0553     };
0554     const QList<KPluginMetaData> allApplets = KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), filter);
0555 
0556     QList<KPluginMetaData> filtered;
0557     for (const KPluginMetaData &md : allApplets) {
0558         const QStringList urlPatterns = md.value(QStringLiteral("X-Plasma-DropUrlPatterns"), QStringList());
0559         for (const QString &glob : urlPatterns) {
0560             QRegularExpression rx(QRegularExpression::anchoredPattern(QRegularExpression::wildcardToRegularExpression(glob)));
0561             if (rx.match(url.toString()).hasMatch()) {
0562                 filtered << md;
0563             }
0564         }
0565     }
0566 
0567     return filtered;
0568 }
0569 
0570 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 36)
0571 KPluginInfo::List PluginLoader::listAppletInfoForUrl(const QUrl &url)
0572 {
0573     return KPluginInfo::fromMetaData(listAppletMetaDataForUrl(url).toVector());
0574 }
0575 #endif
0576 
0577 QStringList PluginLoader::listAppletCategories(const QString &parentApp, bool visibleOnly)
0578 {
0579     KConfigGroup group(KSharedConfig::openConfig(), "General");
0580     const QStringList excluded = group.readEntry("ExcludeCategories", QStringList());
0581     auto filter = [&parentApp, &excluded, visibleOnly](const KPluginMetaData &md) -> bool {
0582         const QString pa = md.value(QStringLiteral("X-KDE-ParentApp"));
0583         return (parentApp.isEmpty() || pa == parentApp) //
0584             && (excluded.isEmpty() || excluded.contains(md.value(QStringLiteral("X-KDE-PluginInfo-Category")))) //
0585             && (!visibleOnly || !md.isHidden());
0586     };
0587     const QList<KPluginMetaData> allApplets = KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), filter);
0588 
0589     QStringList categories;
0590     for (auto &plugin : allApplets) {
0591         if (plugin.category().isEmpty()) {
0592             if (!categories.contains(i18nc("misc category", "Miscellaneous"))) {
0593                 categories << i18nc("misc category", "Miscellaneous");
0594             }
0595         } else {
0596             categories << plugin.category();
0597         }
0598     }
0599     categories.sort();
0600     return categories;
0601 }
0602 
0603 void PluginLoader::setCustomAppletCategories(const QStringList &categories)
0604 {
0605     PluginLoaderPrivate::s_customCategories = QSet<QString>(categories.begin(), categories.end());
0606 }
0607 
0608 QStringList PluginLoader::customAppletCategories() const
0609 {
0610     return PluginLoaderPrivate::s_customCategories.values();
0611 }
0612 
0613 QString PluginLoader::appletCategory(const QString &appletName)
0614 {
0615     if (appletName.isEmpty()) {
0616         return QString();
0617     }
0618 
0619     const KPackage::Package p = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Applet"), appletName);
0620     if (!p.isValid()) {
0621         return QString();
0622     }
0623 
0624     return p.metadata().category();
0625 }
0626 
0627 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 83)
0628 KPluginInfo::List PluginLoader::listContainments(const QString &category, const QString &parentApp)
0629 {
0630     return listContainmentsOfType(QString(), category, parentApp);
0631 }
0632 #endif
0633 
0634 QList<KPluginMetaData> PluginLoader::listContainmentsMetaData(std::function<bool(const KPluginMetaData &)> filter)
0635 {
0636     auto ownFilter = [filter](const KPluginMetaData &md) -> bool {
0637         return isContainmentMetaData(md) && filter(md);
0638     };
0639 
0640     return KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), ownFilter);
0641 }
0642 
0643 QList<KPluginMetaData> PluginLoader::listContainmentsMetaDataOfType(const QString &type)
0644 {
0645     auto filter = [type](const KPluginMetaData &md) -> bool {
0646         return md.value(QStringLiteral("X-Plasma-ContainmentType")) == type;
0647     };
0648 
0649     return listContainmentsMetaData(filter);
0650 }
0651 
0652 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 83)
0653 KPluginInfo::List PluginLoader::listContainmentsOfType(const QString &type, const QString &category, const QString &parentApp)
0654 {
0655     auto filter = [&type, &category, &parentApp](const KPluginMetaData &md) -> bool {
0656         if (isContainmentMetaData(md)) {
0657             return false;
0658         }
0659         if (!parentApp.isEmpty() && md.value(QStringLiteral("X-KDE-ParentApp")) != parentApp) {
0660             return false;
0661         }
0662 
0663         if (!type.isEmpty() && md.value(QStringLiteral("X-Plasma-ContainmentType")) != type) {
0664             return false;
0665         }
0666 
0667         if (!category.isEmpty() && md.value(QStringLiteral("X-KDE-PluginInfo-Category")) != category) {
0668             return false;
0669         }
0670 
0671         return true;
0672     };
0673 
0674     return KPluginInfo::fromMetaData(KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), filter).toVector());
0675 }
0676 #endif
0677 
0678 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 83)
0679 KPluginInfo::List PluginLoader::listContainmentsForMimeType(const QString &mimeType)
0680 {
0681     auto filter = [&mimeType](const KPluginMetaData &md) -> bool {
0682         return isContainmentMetaData(md) && md.value(QStringLiteral("X-Plasma-DropMimeTypes"), QStringList()).contains(mimeType);
0683     };
0684 
0685     return KPluginInfo::fromMetaData(KPackage::PackageLoader::self()->findPackages(QStringLiteral("Plasma/Applet"), QString(), filter).toVector());
0686 }
0687 #endif
0688 
0689 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 83)
0690 QStringList PluginLoader::listContainmentTypes()
0691 {
0692     const KPluginInfo::List containmentInfos = listContainments();
0693     QSet<QString> types;
0694 
0695     for (const KPluginInfo &containmentInfo : containmentInfos) {
0696         const QStringList theseTypes = containmentInfo.property(QStringLiteral("X-Plasma-ContainmentType")).toStringList();
0697         for (const QString &type : theseTypes) {
0698             types.insert(type);
0699         }
0700     }
0701 
0702     return types.values();
0703 }
0704 #endif
0705 
0706 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 77)
0707 KPluginInfo::List PluginLoader::listDataEngineInfo(const QString &parentApp)
0708 {
0709     return KPluginInfo::fromMetaData(listDataEngineMetaData(parentApp));
0710 }
0711 #endif
0712 
0713 QVector<KPluginMetaData> PluginLoader::listDataEngineMetaData(const QString &parentApp)
0714 {
0715     auto filter = [&parentApp](const KPluginMetaData &md) -> bool {
0716         return md.value(QStringLiteral("X-KDE-ParentApp")) == parentApp;
0717     };
0718 
0719     QVector<KPluginMetaData> plugins;
0720     if (parentApp.isEmpty()) {
0721         plugins = KPluginMetaData::findPlugins(PluginLoaderPrivate::s_dataEnginePluginDir);
0722     } else {
0723         plugins = KPluginMetaData::findPlugins(PluginLoaderPrivate::s_dataEnginePluginDir, filter);
0724     }
0725 
0726     return plugins;
0727 }
0728 
0729 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 77)
0730 KPluginInfo::List PluginLoader::listContainmentActionsInfo(const QString &parentApp)
0731 {
0732     return KPluginInfo::fromMetaData(listContainmentActionsMetaData(parentApp));
0733 }
0734 #endif
0735 
0736 QVector<KPluginMetaData> PluginLoader::listContainmentActionsMetaData(const QString &parentApp)
0737 {
0738     auto filter = [&parentApp](const KPluginMetaData &md) -> bool {
0739         return md.value(QStringLiteral("X-KDE-ParentApp")) == parentApp;
0740     };
0741 
0742     QVector<KPluginMetaData> plugins;
0743     if (parentApp.isEmpty()) {
0744         plugins = KPluginMetaData::findPlugins(PluginLoaderPrivate::s_containmentActionsPluginDir);
0745     } else {
0746         plugins = KPluginMetaData::findPlugins(PluginLoaderPrivate::s_containmentActionsPluginDir, filter);
0747     }
0748 
0749 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 0)
0750     QSet<QString> knownPlugins;
0751     for (const KPluginMetaData &p : std::as_const(plugins)) {
0752         knownPlugins.insert(p.pluginId());
0753     }
0754     QString constraint;
0755     if (!parentApp.isEmpty()) {
0756         constraint = QLatin1String("[X-KDE-ParentApp] == '") + parentApp + QLatin1Char('\'');
0757     }
0758     const KService::List offers = KServiceTypeTrader::self()->query(QStringLiteral("Plasma/ContainmentActions"), constraint);
0759     for (KService::Ptr s : offers) {
0760         if (!knownPlugins.contains(s->pluginKeyword())) {
0761             QT_WARNING_PUSH
0762             QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations")
0763             QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations")
0764             plugins.append(KPluginInfo(s).toMetaData());
0765             QT_WARNING_POP
0766         }
0767     }
0768 #endif
0769 
0770     return plugins;
0771 }
0772 
0773 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 86)
0774 Applet *PluginLoader::internalLoadApplet(const QString &name, uint appletId, const QVariantList &args)
0775 {
0776     Q_UNUSED(name)
0777     Q_UNUSED(appletId)
0778     Q_UNUSED(args)
0779     return nullptr;
0780 }
0781 
0782 DataEngine *PluginLoader::internalLoadDataEngine(const QString &name)
0783 {
0784     Q_UNUSED(name)
0785     return nullptr;
0786 }
0787 
0788 ContainmentActions *PluginLoader::internalLoadContainmentActions(Containment *containment, const QString &name, const QVariantList &args)
0789 {
0790     Q_UNUSED(containment)
0791     Q_UNUSED(name)
0792     Q_UNUSED(args)
0793     return nullptr;
0794 }
0795 
0796 Service *PluginLoader::internalLoadService(const QString &name, const QVariantList &args, QObject *parent)
0797 {
0798     Q_UNUSED(name)
0799     Q_UNUSED(args)
0800     Q_UNUSED(parent)
0801     return nullptr;
0802 }
0803 
0804 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 83)
0805 Package PluginLoader::internalLoadPackage(const QString &name, const QString &specialization)
0806 {
0807     Q_UNUSED(name);
0808     Q_UNUSED(specialization);
0809     return Package();
0810 }
0811 #endif
0812 
0813 KPluginInfo::List PluginLoader::internalAppletInfo(const QString &category) const
0814 {
0815     Q_UNUSED(category)
0816     return KPluginInfo::List();
0817 }
0818 
0819 KPluginInfo::List PluginLoader::internalDataEngineInfo() const
0820 {
0821     return KPluginInfo::List();
0822 }
0823 
0824 KPluginInfo::List PluginLoader::internalServiceInfo() const
0825 {
0826     return KPluginInfo::List();
0827 }
0828 
0829 KPluginInfo::List PluginLoader::internalContainmentActionsInfo() const
0830 {
0831     return KPluginInfo::List();
0832 }
0833 #endif
0834 
0835 #if PLASMA_BUILD_DEPRECATED_SINCE(5, 88)
0836 static KPluginInfo::List standardInternalInfo(const QString &type, const QString &category = QString())
0837 {
0838     QStringList files = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation,
0839                                                   QLatin1String(PLASMA_RELATIVE_DATA_INSTALL_DIR "/internal/") + type + QLatin1String("/*.desktop"),
0840                                                   QStandardPaths::LocateFile);
0841 
0842     const KPluginInfo::List allInfo = KPluginInfo::fromFiles(files);
0843 
0844     if (category.isEmpty() || allInfo.isEmpty()) {
0845         return allInfo;
0846     }
0847 
0848     KPluginInfo::List matchingInfo;
0849     for (const KPluginInfo &info : allInfo) {
0850         if (info.category().compare(category, Qt::CaseInsensitive) == 0) {
0851             matchingInfo << info;
0852         }
0853     }
0854 
0855     return matchingInfo;
0856 }
0857 
0858 KPluginInfo::List PluginLoader::standardInternalAppletInfo(const QString &category) const
0859 {
0860     return standardInternalInfo(QStringLiteral("applets"), category);
0861 }
0862 
0863 KPluginInfo::List PluginLoader::standardInternalDataEngineInfo() const
0864 {
0865     return standardInternalInfo(QStringLiteral("dataengines"));
0866 }
0867 
0868 KPluginInfo::List PluginLoader::standardInternalServiceInfo() const
0869 {
0870     return standardInternalInfo(QStringLiteral("services"));
0871 }
0872 #endif
0873 
0874 KPluginMetaData PluginLoaderPrivate::Cache::findPluginById(const QString &name, const QString &pluginNamespace)
0875 {
0876     const qint64 now = qRound64(QDateTime::currentMSecsSinceEpoch() / 1000.0);
0877     bool useRuntimeCache = true;
0878 
0879     if (pluginCacheAge == 0) {
0880         // Find all the plugins now, but only once
0881         pluginCacheAge = now;
0882 
0883         const auto metaDataList = KPluginMetaData::findPlugins(pluginNamespace, {}, KPluginMetaData::AllowEmptyMetaData);
0884         for (const KPluginMetaData &metadata : metaDataList) {
0885             plugins.insert(metadata.pluginId(), metadata);
0886         }
0887     } else if (now - pluginCacheAge > maxCacheAge) {
0888         // cache is old and we're not within a few seconds of startup anymore
0889         useRuntimeCache = false;
0890         plugins.clear();
0891     }
0892 
0893     // if name wasn't a path, pluginName == name
0894     const QString pluginName = name.section(QLatin1Char('/'), -1);
0895 
0896     if (useRuntimeCache) {
0897         KPluginMetaData data = plugins.value(name);
0898         qCDebug(LOG_PLASMA) << "loading applet by name" << name << useRuntimeCache << data.isValid();
0899         return data;
0900     } else {
0901         const QVector<KPluginMetaData> offers = KPluginMetaData::findPlugins(
0902             pluginNamespace,
0903             [&pluginName](const KPluginMetaData &data) {
0904                 return data.pluginId() == pluginName;
0905             },
0906             KPluginMetaData::AllowEmptyMetaData);
0907         return offers.isEmpty() ? KPluginMetaData() : offers.first();
0908     }
0909 }
0910 
0911 } // Plasma Namespace