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 }