File indexing completed on 2024-11-10 04:40:43

0001 /*  -*- c++ -*-
0002     SPDX-FileCopyrightText: 2008 Tobias Koenig <tokoe@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "akonadicore_debug.h"
0008 #include "pluginloader_p.h"
0009 #include "private/standarddirs_p.h"
0010 #include <KConfig>
0011 #include <KConfigGroup>
0012 #include <KLocalizedString>
0013 #include <QDir>
0014 #include <QPluginLoader>
0015 #include <QStandardPaths>
0016 
0017 using namespace Akonadi;
0018 
0019 PluginMetaData::PluginMetaData()
0020     : loaded(false)
0021 {
0022 }
0023 
0024 PluginMetaData::PluginMetaData(const QString &lib, const QString &name, const QString &comment, const QString &cname)
0025     : library(lib)
0026     , nameLabel(name)
0027     , descriptionLabel(comment)
0028     , className(cname)
0029     , loaded(false)
0030 {
0031 }
0032 
0033 PluginLoader *PluginLoader::mSelf = nullptr;
0034 
0035 PluginLoader::PluginLoader()
0036 {
0037     scan();
0038 }
0039 
0040 PluginLoader::~PluginLoader()
0041 {
0042     qDeleteAll(mPluginLoaders);
0043     mPluginLoaders.clear();
0044 }
0045 
0046 PluginLoader *PluginLoader::self()
0047 {
0048     if (!mSelf) {
0049         mSelf = new PluginLoader();
0050     }
0051 
0052     return mSelf;
0053 }
0054 
0055 QStringList PluginLoader::names() const
0056 {
0057     return mPluginInfos.keys();
0058 }
0059 
0060 QObject *PluginLoader::createForName(const QString &name)
0061 {
0062     if (!mPluginInfos.contains(name)) {
0063         qCWarning(AKONADICORE_LOG) << "plugin name \"" << name << "\" is unknown to the plugin loader.";
0064         return nullptr;
0065     }
0066 
0067     PluginMetaData &info = mPluginInfos[name];
0068 
0069     // First try to load it statically
0070     const auto instances = QPluginLoader::staticInstances();
0071     for (auto plugin : instances) {
0072         if (QLatin1StringView(plugin->metaObject()->className()) == info.className) {
0073             info.loaded = true;
0074             return plugin;
0075         }
0076     }
0077 
0078     if (!info.loaded) {
0079         auto loader = new QPluginLoader(info.library);
0080         if (loader->fileName().isEmpty()) {
0081             qCWarning(AKONADICORE_LOG) << "Error loading" << info.library << ":" << loader->errorString();
0082             delete loader;
0083             return nullptr;
0084         }
0085 
0086         mPluginLoaders.insert(name, loader);
0087         info.loaded = true;
0088     }
0089 
0090     QPluginLoader *loader = mPluginLoaders.value(name);
0091     Q_ASSERT(loader);
0092 
0093     QObject *object = loader->instance();
0094     if (!object) {
0095         qCWarning(AKONADICORE_LOG) << "unable to load plugin" << info.library << "for plugin name" << name << ".";
0096         qCWarning(AKONADICORE_LOG) << "Error was:\"" << loader->errorString() << "\".";
0097         return nullptr;
0098     }
0099 
0100     return object;
0101 }
0102 
0103 PluginMetaData PluginLoader::infoForName(const QString &name) const
0104 {
0105     return mPluginInfos.value(name, PluginMetaData());
0106 }
0107 
0108 void PluginLoader::scan()
0109 {
0110     const auto dirs = StandardDirs::locateAllResourceDirs(QStringLiteral("akonadi/plugins/serializer/"));
0111     for (const QString &dir : dirs) {
0112         const QStringList fileNames = QDir(dir).entryList(QStringList() << QStringLiteral("*.desktop"));
0113         for (const QString &file : fileNames) {
0114             const QString entry = dir + QLatin1Char('/') + file;
0115             KConfig config(entry, KConfig::SimpleConfig);
0116             if (config.hasGroup(QLatin1StringView("Misc")) && config.hasGroup(QLatin1StringView("Plugin"))) {
0117                 KConfigGroup group(&config, QStringLiteral("Plugin"));
0118 
0119                 const QString type = group.readEntry("Type").toLower();
0120                 if (type.isEmpty()) {
0121                     qCWarning(AKONADICORE_LOG) << "missing or empty [Plugin]Type value in" << entry << "- skipping";
0122                     continue;
0123                 }
0124 
0125                 // read Class entry as a list so that types like QPair<A,B> are
0126                 // properly escaped and don't end up being split into QPair<A
0127                 // and B>.
0128                 const QStringList classes = group.readXdgListEntry("X-Akonadi-Class");
0129                 if (classes.isEmpty()) {
0130                     qCWarning(AKONADICORE_LOG) << "missing or empty [Plugin]X-Akonadi-Class value in" << entry << "- skipping";
0131                     continue;
0132                 }
0133 
0134                 const QString library = group.readEntry("X-KDE-Library");
0135                 if (library.isEmpty()) {
0136                     qCWarning(AKONADICORE_LOG) << "missing or empty [Plugin]X-KDE-Library value in" << entry << "- skipping";
0137                     continue;
0138                 }
0139 
0140                 KConfigGroup group2(&config, QStringLiteral("Misc"));
0141 
0142                 QString name = group2.readEntry("Name");
0143                 if (name.isEmpty()) {
0144                     qCWarning(AKONADICORE_LOG) << "missing or empty [Misc]Name value in \"" << entry << "\" - inserting default name";
0145                     name = i18n("Unnamed plugin");
0146                 }
0147 
0148                 QString comment = group2.readEntry("Comment");
0149                 if (comment.isEmpty()) {
0150                     qCWarning(AKONADICORE_LOG) << "missing or empty [Misc]Comment value in \"" << entry << "\" - inserting default name";
0151                     comment = i18n("No description available");
0152                 }
0153 
0154                 QString cname = group.readEntry("X-KDE-ClassName");
0155                 if (cname.isEmpty()) {
0156                     qCWarning(AKONADICORE_LOG) << "missing or empty X-KDE-ClassName value in \"" << entry << "\"";
0157                 }
0158 
0159                 const QStringList mimeTypes = type.split(QLatin1Char(','), Qt::SkipEmptyParts);
0160 
0161                 qCDebug(AKONADICORE_LOG) << "registering Desktop file" << entry << "for" << mimeTypes << '@' << classes;
0162                 for (const QString &mimeType : mimeTypes) {
0163                     for (const QString &classType : classes) {
0164                         mPluginInfos.insert(mimeType + QLatin1Char('@') + classType, PluginMetaData(library, name, comment, cname));
0165                     }
0166                 }
0167 
0168             } else {
0169                 qCWarning(AKONADICORE_LOG) << "Desktop file \"" << entry << "\" doesn't seem to describe a plugin "
0170                                            << "(misses Misc and/or Plugin group)";
0171             }
0172         }
0173     }
0174 }