File indexing completed on 2024-04-28 15:29:50

0001 /*
0002     This file is part of the KDE project
0003     SPDX-FileCopyrightText: 2003, 2007 Matthias Kretz <kretz@kde.org>
0004     SPDX-FileCopyrightText: 2013 Sebastian Kügler <sebas@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-only
0007 */
0008 
0009 #include "kplugininfo.h"
0010 
0011 QT_WARNING_PUSH
0012 QT_WARNING_DISABLE_DEPRECATED
0013 
0014 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 90)
0015 #include "servicesdebug.h"
0016 #include <QDirIterator>
0017 #include <QJsonArray>
0018 #include <QMimeDatabase>
0019 #include <QStandardPaths>
0020 
0021 #include "ksycoca.h"
0022 #include "ksycoca_p.h"
0023 #include <KAboutData>
0024 #include <KDesktopFile>
0025 #include <KPluginMetaData>
0026 #include <kservicetypefactory_p.h>
0027 #include <kservicetypetrader.h>
0028 
0029 // clang-format off
0030 //#ifndef NDEBUG
0031 #define KPLUGININFO_ISVALID_ASSERTION \
0032     do { \
0033         if (!d) { \
0034             qFatal("Accessed invalid KPluginInfo object"); \
0035         } \
0036     } while (false)
0037 //#else
0038 //#define KPLUGININFO_ISVALID_ASSERTION
0039 //#endif
0040 
0041 #define GlobalQStringLiteral(name, str) inline QString name() { return QStringLiteral(str); }
0042 
0043 namespace {
0044 
0045 GlobalQStringLiteral(s_hiddenKey, "Hidden")
0046 GlobalQStringLiteral(s_nameKey, "Name")
0047 GlobalQStringLiteral(s_commentKey, "Comment")
0048 GlobalQStringLiteral(s_iconKey, "Icon")
0049 GlobalQStringLiteral(s_libraryKey, "X-KDE-Library")
0050 GlobalQStringLiteral(s_authorKey, "X-KDE-PluginInfo-Author")
0051 GlobalQStringLiteral(s_emailKey, "X-KDE-PluginInfo-Email")
0052 GlobalQStringLiteral(s_pluginNameKey, "X-KDE-PluginInfo-Name")
0053 GlobalQStringLiteral(s_versionKey, "X-KDE-PluginInfo-Version")
0054 GlobalQStringLiteral(s_websiteKey, "X-KDE-PluginInfo-Website")
0055 GlobalQStringLiteral(s_categoryKey, "X-KDE-PluginInfo-Category")
0056 GlobalQStringLiteral(s_licenseKey, "X-KDE-PluginInfo-License")
0057 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 79) && KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 79)
0058 GlobalQStringLiteral(s_dependenciesKey, "X-KDE-PluginInfo-Depends")
0059 #endif
0060 GlobalQStringLiteral(s_serviceTypesKey, "ServiceTypes")
0061 GlobalQStringLiteral(s_xKDEServiceTypes, "X-KDE-ServiceTypes")
0062 GlobalQStringLiteral(s_mimeTypeKey, "MimeType")
0063 GlobalQStringLiteral(s_formFactorsKey, "X-KDE-FormFactors")
0064 GlobalQStringLiteral(s_enabledbyDefaultKey, "X-KDE-PluginInfo-EnabledByDefault")
0065 GlobalQStringLiteral(s_enabledKey, "Enabled")
0066 
0067 // these keys are used in the json metadata
0068 GlobalQStringLiteral(s_jsonDescriptionKey, "Description")
0069 GlobalQStringLiteral(s_jsonAuthorsKey, "Authors")
0070 GlobalQStringLiteral(s_jsonEmailKey, "Email")
0071 GlobalQStringLiteral(s_jsonCategoryKey, "Category")
0072 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 79) && KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 79)
0073 GlobalQStringLiteral(s_jsonDependenciesKey, "Dependencies")
0074 #endif
0075 GlobalQStringLiteral(s_jsonEnabledByDefaultKey, "EnabledByDefault")
0076 GlobalQStringLiteral(s_jsonFormFactorsKey, "FormFactors")
0077 GlobalQStringLiteral(s_jsonLicenseKey, "License")
0078 GlobalQStringLiteral(s_jsonIdKey, "Id")
0079 GlobalQStringLiteral(s_jsonVersionKey, "Version")
0080 GlobalQStringLiteral(s_jsonWebsiteKey, "Website")
0081 GlobalQStringLiteral(s_jsonMimeTypesKey, "MimeTypes")
0082 GlobalQStringLiteral(s_jsonKPluginKey, "KPlugin")
0083 
0084 }
0085 // clang-format on
0086 
0087 class KPluginInfoPrivate : public QSharedData
0088 {
0089 public:
0090     KPluginInfoPrivate()
0091         : hidden(false)
0092         , pluginenabled(false)
0093         , kcmservicesCached(false)
0094     {
0095     }
0096 
0097     static QStringList deserializeList(const QString &data);
0098 
0099     bool hidden : 1;
0100     bool pluginenabled : 1;
0101     mutable bool kcmservicesCached : 1;
0102 
0103     KPluginMetaData metaData;
0104     KConfigGroup config;
0105     KService::Ptr service;
0106     mutable QList<KService::Ptr> kcmservices;
0107 
0108     /** assigns the @p md to @c metaData, but also ensures that compatibility values are handled */
0109     void setMetaData(const KPluginMetaData &md, bool warnOnOldStyle);
0110 };
0111 
0112 // This comes from KConfigGroupPrivate::deserializeList()
0113 QStringList KPluginInfoPrivate::deserializeList(const QString &data)
0114 {
0115     if (data.isEmpty()) {
0116         return QStringList();
0117     }
0118     if (data == QLatin1String("\\0")) {
0119         return QStringList(QString());
0120     }
0121     QStringList value;
0122     QString val;
0123     val.reserve(data.size());
0124     bool quoted = false;
0125     for (int p = 0; p < data.length(); p++) {
0126         if (quoted) {
0127             val += data[p];
0128             quoted = false;
0129         } else if (data[p].unicode() == '\\') {
0130             quoted = true;
0131         } else if (data[p].unicode() == ',' || data[p].unicode() == ';') {
0132             val.squeeze(); // release any unused memory
0133             value.append(val);
0134             val.clear();
0135             val.reserve(data.size() - p);
0136         } else {
0137             val += data[p];
0138         }
0139     }
0140     value.append(val);
0141     return value;
0142 }
0143 
0144 // maps the KService, QVariant and KDesktopFile keys to the new KPluginMetaData keys
0145 template<typename T, typename Func>
0146 static QJsonObject mapToJsonKPluginKey(const QString &name,
0147                                        const QString &description,
0148 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 79) && KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 79)
0149                                        const QStringList &dependencies,
0150 #endif
0151                                        const QStringList &serviceTypes,
0152                                        const QStringList &formFactors,
0153                                        const T &data,
0154                                        Func accessor)
0155 {
0156     /* Metadata structure is as follows:
0157      "KPlugin": {
0158         "Name": "Date and Time",
0159         "Description": "Date and time by timezone",
0160         "Icon": "preferences-system-time",
0161         "Authors": { "Name": "Aaron Seigo", "Email": "aseigo@kde.org" },
0162         "Category": "Date and Time",
0163 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 79) && KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 79)
0164         "Dependencies": [],
0165 #endif
0166         "EnabledByDefault": "true",
0167         "License": "LGPL",
0168         "Id": "time",
0169         "Version": "1.0",
0170         "Website": "http://plasma.kde.org/",
0171         "ServiceTypes": ["Plasma/DataEngine"]
0172         "FormFactors": ["tablet", "handset"]
0173     }
0174      */
0175     QJsonObject kplugin;
0176     kplugin[s_nameKey()] = name;
0177     kplugin[s_jsonDescriptionKey()] = description;
0178     kplugin[s_iconKey()] = accessor(data, s_iconKey());
0179     QJsonObject authors;
0180     authors[s_nameKey()] = accessor(data, s_authorKey());
0181     authors[s_jsonEmailKey()] = accessor(data, s_emailKey());
0182     kplugin[s_jsonAuthorsKey()] = authors;
0183     kplugin[s_jsonCategoryKey()] = accessor(data, s_categoryKey());
0184     QJsonValue enabledByDefault = accessor(data, s_enabledbyDefaultKey());
0185     // make sure that enabledByDefault is bool and not string
0186     if (!enabledByDefault.isBool()) {
0187         enabledByDefault = enabledByDefault.toString().compare(QLatin1String("true"), Qt::CaseInsensitive) == 0;
0188     }
0189     kplugin[s_jsonEnabledByDefaultKey()] = enabledByDefault;
0190     kplugin[s_jsonLicenseKey()] = accessor(data, s_licenseKey());
0191     kplugin[s_jsonIdKey()] = accessor(data, s_pluginNameKey());
0192     kplugin[s_jsonVersionKey()] = accessor(data, s_versionKey());
0193     kplugin[s_jsonWebsiteKey()] = accessor(data, s_websiteKey());
0194     kplugin[s_jsonFormFactorsKey()] = QJsonArray::fromStringList(formFactors);
0195     kplugin[s_serviceTypesKey()] = QJsonArray::fromStringList(serviceTypes);
0196 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 79) && KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 79)
0197     kplugin[s_jsonDependenciesKey()] = QJsonArray::fromStringList(dependencies);
0198 #endif
0199     QJsonValue mimeTypes = accessor(data, s_mimeTypeKey());
0200     if (mimeTypes.isString()) {
0201         QStringList mimeList = KPluginInfoPrivate::deserializeList(mimeTypes.toString());
0202         if (!mimeList.isEmpty()) {
0203             mimeTypes = QJsonArray::fromStringList(mimeList);
0204         } else {
0205             mimeTypes = QJsonValue();
0206         }
0207     }
0208     kplugin[s_jsonMimeTypesKey()] = mimeTypes;
0209     return kplugin;
0210 }
0211 
0212 // TODO: KF6 remove
0213 static KPluginMetaData fromCompatibilityJson(const QJsonObject &json, const QString &lib, const QString &metaDataFile, bool warnOnOldStyle)
0214 {
0215     // This is not added to KPluginMetaData(QJsonObject, QString) to ensure that all the compatility code
0216     // remains in kservice and does not increase the size of kcoreaddons
0217     QStringList serviceTypes = KPluginMetaData::readStringList(json, s_xKDEServiceTypes());
0218     if (serviceTypes.isEmpty()) {
0219         serviceTypes = KPluginMetaData::readStringList(json, s_serviceTypesKey());
0220     }
0221     QJsonObject obj = json;
0222     QString name = KPluginMetaData::readTranslatedString(json, s_nameKey());
0223     if (warnOnOldStyle) {
0224         qWarning(
0225             "Constructing a KPluginInfo object from old style JSON. Please use"
0226             " kcoreaddons_desktop_to_json() for \"%s\" instead of kservice_desktop_to_json()"
0227             " in your CMake code.",
0228             qPrintable(lib));
0229     }
0230     QString description = KPluginMetaData::readTranslatedString(json, s_commentKey());
0231     QStringList formfactors = KPluginMetaData::readStringList(json, s_jsonFormFactorsKey());
0232     QJsonObject kplugin = mapToJsonKPluginKey(name,
0233                                               description,
0234 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 79) && KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 79)
0235                                               KPluginMetaData::readStringList(json, s_dependenciesKey()),
0236 #endif
0237                                               serviceTypes,
0238                                               formfactors,
0239                                               json,
0240                                               [](const QJsonObject &o, const QString &key) {
0241                                                   return o.value(key);
0242                                               });
0243     obj.insert(s_jsonKPluginKey(), kplugin);
0244     return KPluginMetaData(obj, lib, metaDataFile);
0245 }
0246 
0247 void KPluginInfoPrivate::setMetaData(const KPluginMetaData &md, bool warnOnOldStyle)
0248 {
0249     const QJsonObject json = md.rawData();
0250     if (!json.contains(s_jsonKPluginKey())) {
0251         // "KPlugin" key does not exists -> convert from compatibility mode
0252         metaData = fromCompatibilityJson(json, md.fileName(), md.metaDataFileName(), warnOnOldStyle);
0253     } else {
0254         metaData = md;
0255     }
0256 }
0257 
0258 KPluginInfo::KPluginInfo(const KPluginMetaData &md)
0259     : d(new KPluginInfoPrivate)
0260 {
0261     d->setMetaData(md, true);
0262     if (!d->metaData.isValid()) {
0263         d.reset();
0264     }
0265 }
0266 
0267 KPluginInfo::KPluginInfo(const QString &filename /*, QStandardPaths::StandardLocation resource*/)
0268     : d(new KPluginInfoPrivate)
0269 {
0270     KDesktopFile file(/*resource,*/ filename);
0271 
0272     KConfigGroup cg = file.desktopGroup();
0273     if (!cg.exists()) {
0274         qCWarning(SERVICES) << filename << "has no desktop group, cannot construct a KPluginInfo object from it.";
0275         d.reset();
0276         return;
0277     }
0278     d->hidden = cg.readEntry(s_hiddenKey(), false);
0279     if (d->hidden) {
0280         return;
0281     }
0282 
0283     if (file.fileName().endsWith(QLatin1String(".desktop"))) {
0284         d->setMetaData(KPluginMetaData::fromDesktopFile(file.fileName()), true);
0285     } else {
0286         d->setMetaData(KPluginMetaData(file.fileName()), true);
0287     }
0288     if (!d->metaData.isValid()) {
0289         qCWarning(SERVICES) << "Failed to read metadata from .desktop file" << file.fileName();
0290         d.reset();
0291     }
0292 }
0293 
0294 KPluginInfo::KPluginInfo(const QVariantList &args, const QString &libraryPath)
0295     : d(new KPluginInfoPrivate)
0296 {
0297     const QString metaData = QStringLiteral("MetaData");
0298     for (const QVariant &v : args) {
0299         if (v.canConvert<QVariantMap>()) {
0300             const QVariantMap &m = v.toMap();
0301             const QVariant &_metadata = m.value(metaData);
0302             if (_metadata.canConvert<QVariantMap>()) {
0303                 const QVariantMap &map = _metadata.toMap();
0304                 if (map.value(s_hiddenKey()).toBool()) {
0305                     d->hidden = true;
0306                     break;
0307                 }
0308                 d->setMetaData(KPluginMetaData(QJsonObject::fromVariantMap(map), libraryPath), true);
0309                 break;
0310             }
0311         }
0312     }
0313     if (!d->metaData.isValid()) {
0314         d.reset();
0315     }
0316 }
0317 
0318 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 88)
0319 KPluginInfo::KPluginInfo(const KService::Ptr service)
0320     : d(new KPluginInfoPrivate)
0321 {
0322     if (!service) {
0323         d = nullptr; // isValid() == false
0324         return;
0325     }
0326     d->service = service;
0327     if (service->isDeleted()) {
0328         d->hidden = true;
0329         return;
0330     }
0331 
0332     KSycoca::self()->ensureCacheValid();
0333 
0334     QJsonObject json;
0335     const auto propertyList = service->propertyNames();
0336     for (const QString &key : propertyList) {
0337         QMetaType::Type t = KSycocaPrivate::self()->serviceTypeFactory()->findPropertyTypeByName(key);
0338         if (t == QMetaType::UnknownType) {
0339             t = QMetaType::QString; // default to string if the type is not known
0340         }
0341         QVariant v = service->property(key, t);
0342         if (v.isValid()) {
0343             json[key] = QJsonValue::fromVariant(v);
0344         }
0345     }
0346     // reintroduce the separation between MimeType= and X-KDE-ServiceTypes=
0347     // we could do this by modifying KService and KSyCoCa, but as this is only compatibility
0348     // code we just query QMimeDatabase whether a ServiceType is a valid MIME type.
0349     const QStringList services = service->serviceTypes();
0350     if (!services.isEmpty()) {
0351         QMimeDatabase db;
0352         QStringList mimeTypes;
0353         mimeTypes.reserve(services.size());
0354         QStringList newServiceTypes;
0355         newServiceTypes.reserve(services.size());
0356         for (const QString &s : services) {
0357             // If we have a valid mime type of a wildcard, we know it is a mime type and not a service type
0358             // this is for example required in the thumbnailers.
0359             // Also the conversion code in kcoreaddons does not check the validity.
0360             if (db.mimeTypeForName(s).isValid() || s.contains(QLatin1Char('*'))) {
0361                 mimeTypes << s;
0362             } else {
0363                 newServiceTypes << s;
0364             }
0365         }
0366         json[s_mimeTypeKey()] = QJsonArray::fromStringList(mimeTypes);
0367         json[s_xKDEServiceTypes()] = QJsonArray::fromStringList(newServiceTypes);
0368         json[s_serviceTypesKey()] = QJsonValue();
0369     }
0370 
0371     d->setMetaData(KPluginMetaData(json, service->library(), service->entryPath()), false);
0372     if (!d->metaData.isValid()) {
0373         d.reset();
0374     }
0375 }
0376 #endif
0377 
0378 KPluginInfo::KPluginInfo()
0379     : d(nullptr) // isValid() == false
0380 {
0381 }
0382 
0383 bool KPluginInfo::isValid() const
0384 {
0385     return d.data() != nullptr;
0386 }
0387 
0388 KPluginInfo::KPluginInfo(const KPluginInfo &rhs)
0389     : d(rhs.d)
0390 {
0391 }
0392 
0393 KPluginInfo &KPluginInfo::operator=(const KPluginInfo &rhs)
0394 {
0395     d = rhs.d;
0396     return *this;
0397 }
0398 
0399 bool KPluginInfo::operator==(const KPluginInfo &rhs) const
0400 {
0401     return d == rhs.d;
0402 }
0403 
0404 bool KPluginInfo::operator!=(const KPluginInfo &rhs) const
0405 {
0406     return d != rhs.d;
0407 }
0408 
0409 bool KPluginInfo::operator<(const KPluginInfo &rhs) const
0410 {
0411     if (category() < rhs.category()) {
0412         return true;
0413     }
0414     if (category() == rhs.category()) {
0415         return name() < rhs.name();
0416     }
0417     return false;
0418 }
0419 
0420 bool KPluginInfo::operator>(const KPluginInfo &rhs) const
0421 {
0422     if (category() > rhs.category()) {
0423         return true;
0424     }
0425     if (category() == rhs.category()) {
0426         return name() > rhs.name();
0427     }
0428     return false;
0429 }
0430 
0431 KPluginInfo::~KPluginInfo()
0432 {
0433 }
0434 
0435 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 88)
0436 QList<KPluginInfo> KPluginInfo::fromServices(const KService::List &services, const KConfigGroup &config)
0437 {
0438     QList<KPluginInfo> infolist;
0439     for (KService::List::ConstIterator it = services.begin(); it != services.end(); ++it) {
0440         KPluginInfo info(*it);
0441         if (info.isValid()) {
0442             info.setConfig(config);
0443             infolist += info;
0444         }
0445     }
0446     return infolist;
0447 }
0448 #endif
0449 
0450 QList<KPluginInfo> KPluginInfo::fromFiles(const QStringList &files, const KConfigGroup &config)
0451 {
0452     QList<KPluginInfo> infolist;
0453     infolist.reserve(files.size());
0454 
0455     std::transform(files.cbegin(), files.cend(), std::back_inserter(infolist), [&config](const QString &file) {
0456         KPluginInfo info(file);
0457         info.setConfig(config);
0458         return info;
0459     });
0460 
0461     return infolist;
0462 }
0463 
0464 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 81)
0465 QList<KPluginInfo> KPluginInfo::fromKPartsInstanceName(const QString &name, const KConfigGroup &config)
0466 {
0467     QStringList files;
0468     const QStringList dirs =
0469         QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, name + QLatin1String("/kpartplugins"), QStandardPaths::LocateDirectory);
0470     for (const QString &dir : dirs) {
0471         QDirIterator it(dir, QStringList() << QStringLiteral("*.desktop"));
0472         while (it.hasNext()) {
0473             files.append(it.next());
0474         }
0475     }
0476     return fromFiles(files, config);
0477 }
0478 #endif
0479 
0480 bool KPluginInfo::isHidden() const
0481 {
0482     KPLUGININFO_ISVALID_ASSERTION;
0483     return d->hidden;
0484 }
0485 
0486 void KPluginInfo::setPluginEnabled(bool enabled)
0487 {
0488     KPLUGININFO_ISVALID_ASSERTION;
0489     // qDebug() << Q_FUNC_INFO;
0490     d->pluginenabled = enabled;
0491 }
0492 
0493 bool KPluginInfo::isPluginEnabled() const
0494 {
0495     KPLUGININFO_ISVALID_ASSERTION;
0496     // qDebug() << Q_FUNC_INFO;
0497     return d->pluginenabled;
0498 }
0499 
0500 bool KPluginInfo::isPluginEnabledByDefault() const
0501 {
0502     KPLUGININFO_ISVALID_ASSERTION;
0503     // qDebug() << Q_FUNC_INFO;
0504     return d->metaData.isEnabledByDefault();
0505 }
0506 
0507 QString KPluginInfo::name() const
0508 {
0509     KPLUGININFO_ISVALID_ASSERTION;
0510     return d->metaData.name();
0511 }
0512 
0513 QString KPluginInfo::comment() const
0514 {
0515     KPLUGININFO_ISVALID_ASSERTION;
0516     return d->metaData.description();
0517 }
0518 
0519 QString KPluginInfo::icon() const
0520 {
0521     KPLUGININFO_ISVALID_ASSERTION;
0522     return d->metaData.iconName();
0523 }
0524 
0525 QString KPluginInfo::entryPath() const
0526 {
0527     KPLUGININFO_ISVALID_ASSERTION;
0528     return d->metaData.metaDataFileName();
0529 }
0530 
0531 QString KPluginInfo::author() const
0532 {
0533     KPLUGININFO_ISVALID_ASSERTION;
0534     const QList<KAboutPerson> &authors = d->metaData.authors();
0535     return authors.isEmpty() ? QString() : authors[0].name();
0536 }
0537 
0538 QString KPluginInfo::email() const
0539 {
0540     KPLUGININFO_ISVALID_ASSERTION;
0541     const QList<KAboutPerson> &authors = d->metaData.authors();
0542     return authors.isEmpty() ? QString() : authors[0].emailAddress();
0543 }
0544 
0545 QString KPluginInfo::category() const
0546 {
0547     KPLUGININFO_ISVALID_ASSERTION;
0548     return d->metaData.category();
0549 }
0550 
0551 QStringList KPluginInfo::formFactors() const
0552 {
0553     KPLUGININFO_ISVALID_ASSERTION;
0554     return d->metaData.formFactors();
0555 }
0556 
0557 QString KPluginInfo::pluginName() const
0558 {
0559     KPLUGININFO_ISVALID_ASSERTION;
0560     return d->metaData.pluginId();
0561 }
0562 
0563 QString KPluginInfo::libraryPath() const
0564 {
0565     KPLUGININFO_ISVALID_ASSERTION;
0566     return d->metaData.fileName();
0567 }
0568 
0569 QString KPluginInfo::version() const
0570 {
0571     KPLUGININFO_ISVALID_ASSERTION;
0572     return d->metaData.version();
0573 }
0574 
0575 QString KPluginInfo::website() const
0576 {
0577     KPLUGININFO_ISVALID_ASSERTION;
0578     return d->metaData.website();
0579 }
0580 
0581 QString KPluginInfo::license() const
0582 {
0583     KPLUGININFO_ISVALID_ASSERTION;
0584     return d->metaData.license();
0585 }
0586 
0587 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 79) && KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 79)
0588 QStringList KPluginInfo::dependencies() const
0589 {
0590     KPLUGININFO_ISVALID_ASSERTION;
0591     return d->metaData.dependencies();
0592 }
0593 #endif
0594 
0595 QStringList KPluginInfo::serviceTypes() const
0596 {
0597     KPLUGININFO_ISVALID_ASSERTION;
0598     // KService/KPluginInfo include the MIME types in serviceTypes()
0599     return d->metaData.serviceTypes() + d->metaData.mimeTypes();
0600 }
0601 
0602 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 70)
0603 KService::Ptr KPluginInfo::service() const
0604 {
0605     KPLUGININFO_ISVALID_ASSERTION;
0606     return d->service;
0607 }
0608 #endif
0609 
0610 QList<KService::Ptr> KPluginInfo::kcmServices() const
0611 {
0612     KPLUGININFO_ISVALID_ASSERTION;
0613     if (!d->kcmservicesCached) {
0614         d->kcmservices =
0615             KServiceTypeTrader::self()->query(QStringLiteral("KCModule"), QLatin1Char('\'') + pluginName() + QLatin1String("' in [X-KDE-ParentComponents]"));
0616         // qDebug() << "found" << d->kcmservices.count() << "offers for" << d->pluginName;
0617 
0618         d->kcmservicesCached = true;
0619     }
0620 
0621     return d->kcmservices;
0622 }
0623 
0624 void KPluginInfo::setConfig(const KConfigGroup &config)
0625 {
0626     KPLUGININFO_ISVALID_ASSERTION;
0627     d->config = config;
0628 }
0629 
0630 KConfigGroup KPluginInfo::config() const
0631 {
0632     KPLUGININFO_ISVALID_ASSERTION;
0633     return d->config;
0634 }
0635 
0636 QVariant KPluginInfo::property(const QString &key) const
0637 {
0638     KPLUGININFO_ISVALID_ASSERTION;
0639     if (d->service) {
0640         return d->service->property(key);
0641     }
0642     QVariant result = d->metaData.rawData().value(key).toVariant();
0643     if (result.isValid()) {
0644         KSycoca::self()->ensureCacheValid();
0645         const QMetaType::Type t = KSycocaPrivate::self()->serviceTypeFactory()->findPropertyTypeByName(key);
0646 
0647         // special case if we want a stringlist: split values by ',' or ';' and construct the list
0648         if (t == QMetaType::QStringList) {
0649             if (result.canConvert<QString>()) {
0650                 result = KPluginInfoPrivate::deserializeList(result.toString());
0651             } else if (result.canConvert<QVariantList>()) {
0652                 const QVariantList list = result.toList();
0653                 QStringList newResult;
0654                 for (const QVariant &value : list) {
0655                     newResult += value.toString();
0656                 }
0657                 result = newResult;
0658             } else {
0659                 qCWarning(SERVICES) << "Cannot interpret" << result << "into a string list";
0660             }
0661         }
0662         return result;
0663     }
0664 
0665     // If the key was not found check compatibility for old key names and print a warning
0666     // These warnings should only happen if JSON was generated with kcoreaddons_desktop_to_json
0667     // but the application uses KPluginTrader::query() instead of KPluginLoader::findPlugins()
0668     // TODO: KF6 remove
0669 #define RETURN_WITH_DEPRECATED_WARNING(ret)                                                                                                                    \
0670     qWarning("Calling KPluginInfo::property(\"%s\") is deprecated, use KPluginInfo::" #ret " in \"%s\" instead.",                                              \
0671              qPrintable(key),                                                                                                                                  \
0672              qPrintable(d->metaData.fileName()));                                                                                                              \
0673     return ret;
0674     if (key == s_authorKey()) {
0675         RETURN_WITH_DEPRECATED_WARNING(author());
0676     } else if (key == s_categoryKey()) {
0677         RETURN_WITH_DEPRECATED_WARNING(category());
0678     } else if (key == s_commentKey()) {
0679         RETURN_WITH_DEPRECATED_WARNING(comment());
0680 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 79) && KCOREADDONS_BUILD_DEPRECATED_SINCE(5, 79)
0681     } else if (key == s_dependenciesKey()) {
0682         RETURN_WITH_DEPRECATED_WARNING(dependencies());
0683 #endif
0684     } else if (key == s_emailKey()) {
0685         RETURN_WITH_DEPRECATED_WARNING(email());
0686     } else if (key == s_enabledbyDefaultKey()) {
0687         RETURN_WITH_DEPRECATED_WARNING(isPluginEnabledByDefault());
0688     } else if (key == s_libraryKey()) {
0689         RETURN_WITH_DEPRECATED_WARNING(libraryPath());
0690     } else if (key == s_licenseKey()) {
0691         RETURN_WITH_DEPRECATED_WARNING(license());
0692     } else if (key == s_nameKey()) {
0693         RETURN_WITH_DEPRECATED_WARNING(name());
0694     } else if (key == s_pluginNameKey()) {
0695         RETURN_WITH_DEPRECATED_WARNING(pluginName());
0696     } else if (key == s_serviceTypesKey()) {
0697         RETURN_WITH_DEPRECATED_WARNING(serviceTypes());
0698     } else if (key == s_versionKey()) {
0699         RETURN_WITH_DEPRECATED_WARNING(version());
0700     } else if (key == s_websiteKey()) {
0701         RETURN_WITH_DEPRECATED_WARNING(website());
0702     } else if (key == s_xKDEServiceTypes()) {
0703         RETURN_WITH_DEPRECATED_WARNING(serviceTypes());
0704     } else if (key == s_formFactorsKey()) {
0705         RETURN_WITH_DEPRECATED_WARNING(formFactors());
0706     }
0707 #undef RETURN_WITH_DEPRECATED_WARNING
0708     // not a compatibility key -> return invalid QVariant
0709     return result;
0710 }
0711 
0712 QVariantMap KPluginInfo::properties() const
0713 {
0714     return d->metaData.rawData().toVariantMap();
0715 }
0716 
0717 void KPluginInfo::save(KConfigGroup config)
0718 {
0719     KPLUGININFO_ISVALID_ASSERTION;
0720     // qDebug() << Q_FUNC_INFO;
0721     if (config.isValid()) {
0722         config.writeEntry(pluginName() + s_enabledKey(), isPluginEnabled());
0723     } else {
0724         if (!d->config.isValid()) {
0725             qCWarning(SERVICES) << "no KConfigGroup, cannot save";
0726             return;
0727         }
0728         d->config.writeEntry(pluginName() + s_enabledKey(), isPluginEnabled());
0729     }
0730 }
0731 
0732 void KPluginInfo::load(const KConfigGroup &config)
0733 {
0734     KPLUGININFO_ISVALID_ASSERTION;
0735     // qDebug() << Q_FUNC_INFO;
0736     if (config.isValid()) {
0737         setPluginEnabled(config.readEntry(pluginName() + s_enabledKey(), isPluginEnabledByDefault()));
0738     } else {
0739         if (!d->config.isValid()) {
0740             qCWarning(SERVICES) << "no KConfigGroup, cannot load";
0741             return;
0742         }
0743         setPluginEnabled(d->config.readEntry(pluginName() + s_enabledKey(), isPluginEnabledByDefault()));
0744     }
0745 }
0746 
0747 void KPluginInfo::defaults()
0748 {
0749     // qDebug() << Q_FUNC_INFO;
0750     setPluginEnabled(isPluginEnabledByDefault());
0751 }
0752 
0753 uint qHash(const KPluginInfo &p)
0754 {
0755     return qHash(reinterpret_cast<quint64>(p.d.data()));
0756 }
0757 
0758 KPluginInfo KPluginInfo::fromMetaData(const KPluginMetaData &md)
0759 {
0760     return KPluginInfo(md);
0761 }
0762 
0763 KPluginMetaData KPluginInfo::toMetaData() const
0764 {
0765     KPLUGININFO_ISVALID_ASSERTION;
0766     return d->metaData;
0767 }
0768 
0769 KPluginMetaData KPluginInfo::toMetaData(const KPluginInfo &info)
0770 {
0771     return info.toMetaData();
0772 }
0773 
0774 KPluginInfo::List KPluginInfo::fromMetaData(const QVector<KPluginMetaData> &list)
0775 {
0776     KPluginInfo::List ret;
0777     ret.reserve(list.size());
0778     for (const KPluginMetaData &md : list) {
0779         ret.append(KPluginInfo::fromMetaData(md));
0780     }
0781     return ret;
0782 }
0783 
0784 QVector<KPluginMetaData> KPluginInfo::toMetaData(const KPluginInfo::List &list)
0785 {
0786     QVector<KPluginMetaData> ret;
0787     ret.reserve(list.size());
0788     for (const KPluginInfo &info : list) {
0789         ret.append(info.toMetaData());
0790     }
0791     return ret;
0792 }
0793 
0794 #undef KPLUGININFO_ISVALID_ASSERTION
0795 #endif