/** * SPDX-FileCopyrightText: 2013 Albert Vaca * * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL */ #include "pluginloader.h" #include #include #include #include #include #include #include "core_debug.h" #include "device.h" #include "kdeconnectconfig.h" #include "kdeconnectplugin.h" // In older Qt released, qAsConst isnt available #include "qtcompat_p.h" PluginLoader *PluginLoader::instance() { static PluginLoader *instance = new PluginLoader(); return instance; } PluginLoader::PluginLoader() { #ifdef SAILFISHOS const QVector staticPlugins = QPluginLoader::staticPlugins(); for (auto &staticPlugin : staticPlugins) { QJsonObject jsonMetadata = staticPlugin.metaData().value(QStringLiteral("MetaData")).toObject(); KPluginMetaData metadata(jsonMetadata, QString()); if (metadata.serviceTypes().contains(QStringLiteral("KdeConnect/Plugin"))) { plugins.insert(metadata.pluginId(), metadata); pluginsFactories.insert(metadata.pluginId(), qobject_cast(staticPlugin.instance())); } } #else const QVector data = KPluginLoader::findPlugins(QStringLiteral("kdeconnect/")); for (const KPluginMetaData &metadata : data) { plugins[metadata.pluginId()] = metadata; } #endif } QStringList PluginLoader::getPluginList() const { return plugins.keys(); } bool PluginLoader::doesPluginExist(const QString &name) const { return plugins.contains(name); } KPluginMetaData PluginLoader::getPluginInfo(const QString &name) const { return plugins.value(name); } KdeConnectPlugin *PluginLoader::instantiatePluginForDevice(const QString &pluginName, Device *device) const { KdeConnectPlugin *ret = nullptr; KPluginMetaData service = plugins.value(pluginName); if (!service.isValid()) { qCDebug(KDECONNECT_CORE) << "Plugin unknown" << pluginName; return ret; } #ifdef SAILFISHOS KPluginFactory *factory = pluginsFactories.value(pluginName); #else KPluginLoader loader(service.fileName()); KPluginFactory *factory = loader.factory(); if (!factory) { qCDebug(KDECONNECT_CORE) << "KPluginFactory could not load the plugin:" << service.pluginId() << loader.errorString(); return ret; } #endif const QStringList outgoingInterfaces = KPluginMetaData::readStringList(service.rawData(), QStringLiteral("X-KdeConnect-OutgoingPacketType")); QVariant deviceVariant = QVariant::fromValue(device); ret = factory->create(device, QVariantList() << deviceVariant << pluginName << outgoingInterfaces << service.iconName()); if (!ret) { qCDebug(KDECONNECT_CORE) << "Error loading plugin"; return ret; } // qCDebug(KDECONNECT_CORE) << "Loaded plugin:" << service.pluginId(); return ret; } QStringList PluginLoader::incomingCapabilities() const { QSet ret; for (const KPluginMetaData &service : qAsConst(plugins)) { QStringList rawValues = service.value(QStringLiteral("X-KdeConnect-SupportedPacketType"), QStringList()); ret += QSet(rawValues.begin(), rawValues.end()); } return ret.values(); } QStringList PluginLoader::outgoingCapabilities() const { QSet ret; for (const KPluginMetaData &service : qAsConst(plugins)) { QStringList rawValues = service.value(QStringLiteral("X-KdeConnect-OutgoingPacketType"), QStringList()); ret += QSet(rawValues.begin(), rawValues.end()); } return ret.values(); } QSet PluginLoader::pluginsForCapabilities(const QSet &incoming, const QSet &outgoing) { QSet ret; QString myDeviceType = KdeConnectConfig::instance().deviceType().toString(); for (const KPluginMetaData &service : qAsConst(plugins)) { // Check if the plugin support this device type const QSet supportedDeviceTypes = KPluginMetaData::readStringList(service.rawData(), QStringLiteral("X-KdeConnect-SupportedDeviceTypes")).toSet(); if (!supportedDeviceTypes.isEmpty()) { if (!supportedDeviceTypes.contains(myDeviceType)) { qCDebug(KDECONNECT_CORE) << "Not loading plugin" << service.pluginId() << "because this device of type" << myDeviceType << "is not supported. Supports:" << supportedDeviceTypes.toList().join(QStringLiteral(", ")); continue; } } // Check if capbilites intersect with the remote device const QSet pluginIncomingCapabilities = KPluginMetaData::readStringList(service.rawData(), QStringLiteral("X-KdeConnect-SupportedPacketType")).toSet(); const QSet pluginOutgoingCapabilities = KPluginMetaData::readStringList(service.rawData(), QStringLiteral("X-KdeConnect-OutgoingPacketType")).toSet(); bool capabilitiesEmpty = (pluginIncomingCapabilities.isEmpty() && pluginOutgoingCapabilities.isEmpty()); if (!capabilitiesEmpty) { #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) bool capabilitiesIntersect = (outgoing.intersects(pluginIncomingCapabilities) || incoming.intersects(pluginOutgoingCapabilities)); #else QSet commonIncoming = incoming; commonIncoming.intersect(pluginOutgoingCapabilities); QSet commonOutgoing = outgoing; commonOutgoing.intersect(pluginIncomingCapabilities); bool capabilitiesIntersect = (!commonIncoming.isEmpty() || !commonOutgoing.isEmpty()); #endif if (!capabilitiesIntersect) { qCDebug(KDECONNECT_CORE) << "Not loading plugin" << service.pluginId() << "because device doesn't support it"; continue; } } // If we get here, the plugin can be loaded ret += service.pluginId(); } return ret; }