1 /**
2  * SPDX-FileCopyrightText: 2013 Albert Vaca <albertvaka@gmail.com>
3  *
4  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
5  */
6 
7 #include "pluginloader.h"
8 
9 #include <QVector>
10 #include <QPluginLoader>
11 #include <KPluginMetaData>
12 #include <KPluginLoader>
13 #include <KPluginFactory>
14 #include <QStaticPlugin>
15 
16 #include "core_debug.h"
17 #include "device.h"
18 #include "kdeconnectplugin.h"
19 
20 //In older Qt released, qAsConst isnt available
21 #include "qtcompat_p.h"
22 
instance()23 PluginLoader* PluginLoader::instance()
24 {
25     static PluginLoader* instance = new PluginLoader();
26     return instance;
27 }
28 
PluginLoader()29 PluginLoader::PluginLoader()
30 {
31 #ifdef SAILFISHOS
32     const QVector<QStaticPlugin> staticPlugins = QPluginLoader::staticPlugins();
33     for (auto& staticPlugin : staticPlugins) {
34         QJsonObject jsonMetadata = staticPlugin.metaData().value(QStringLiteral("MetaData")).toObject();
35         KPluginMetaData metadata(jsonMetadata, QString());
36         if (metadata.serviceTypes().contains(QStringLiteral("KdeConnect/Plugin"))) {
37             plugins.insert(metadata.pluginId(), metadata);
38             pluginsFactories.insert(
39                 metadata.pluginId(),
40                 qobject_cast<KPluginFactory*>(staticPlugin.instance()));
41         }
42     }
43 #else
44     const QVector<KPluginMetaData> data = KPluginLoader::findPlugins(QStringLiteral("kdeconnect/"));
45     for (const KPluginMetaData& metadata : data) {
46         plugins[metadata.pluginId()] = metadata;
47     }
48 #endif
49 }
50 
getPluginList() const51 QStringList PluginLoader::getPluginList() const
52 {
53     return plugins.keys();
54 }
55 
getPluginInfo(const QString & name) const56 KPluginMetaData PluginLoader::getPluginInfo(const QString& name) const
57 {
58     return plugins.value(name);
59 }
60 
instantiatePluginForDevice(const QString & pluginName,Device * device) const61 KdeConnectPlugin* PluginLoader::instantiatePluginForDevice(const QString& pluginName, Device* device) const
62 {
63     KdeConnectPlugin* ret = nullptr;
64 
65     KPluginMetaData service = plugins.value(pluginName);
66     if (!service.isValid()) {
67         qCDebug(KDECONNECT_CORE) << "Plugin unknown" << pluginName;
68         return ret;
69     }
70 
71 #ifdef SAILFISHOS
72     KPluginFactory* factory = pluginsFactories.value(pluginName);
73 #else
74     KPluginLoader loader(service.fileName());
75     KPluginFactory* factory = loader.factory();
76     if (!factory) {
77         qCDebug(KDECONNECT_CORE) << "KPluginFactory could not load the plugin:" << service.pluginId() << loader.errorString();
78         return ret;
79     }
80 #endif
81 
82     const QStringList outgoingInterfaces = KPluginMetaData::readStringList(service.rawData(), QStringLiteral("X-KdeConnect-OutgoingPacketType"));
83 
84     QVariant deviceVariant = QVariant::fromValue<Device*>(device);
85 
86     ret = factory->create<KdeConnectPlugin>(device, QVariantList() << deviceVariant << pluginName << outgoingInterfaces << service.iconName());
87     if (!ret) {
88         qCDebug(KDECONNECT_CORE) << "Error loading plugin";
89         return ret;
90     }
91 
92     //qCDebug(KDECONNECT_CORE) << "Loaded plugin:" << service.pluginId();
93     return ret;
94 }
95 
incomingCapabilities() const96 QStringList PluginLoader::incomingCapabilities() const
97 {
98     QSet<QString> ret;
99     for (const KPluginMetaData& service : qAsConst(plugins)) {
100         ret += KPluginMetaData::readStringList(service.rawData(), QStringLiteral("X-KdeConnect-SupportedPacketType")).toSet();
101     }
102     return ret.values();
103 }
104 
outgoingCapabilities() const105 QStringList PluginLoader::outgoingCapabilities() const
106 {
107     QSet<QString> ret;
108     for (const KPluginMetaData& service : qAsConst(plugins)) {
109         ret += KPluginMetaData::readStringList(service.rawData(), QStringLiteral("X-KdeConnect-OutgoingPacketType")).toSet();
110     }
111     return ret.values();
112 }
113 
pluginsForCapabilities(const QSet<QString> & incoming,const QSet<QString> & outgoing)114 QSet<QString> PluginLoader::pluginsForCapabilities(const QSet<QString>& incoming, const QSet<QString>& outgoing)
115 {
116     QSet<QString> ret;
117 
118     for (const KPluginMetaData& service : qAsConst(plugins)) {
119         const QSet<QString> pluginIncomingCapabilities = KPluginMetaData::readStringList(service.rawData(), QStringLiteral("X-KdeConnect-SupportedPacketType")).toSet();
120         const QSet<QString> pluginOutgoingCapabilities = KPluginMetaData::readStringList(service.rawData(), QStringLiteral("X-KdeConnect-OutgoingPacketType")).toSet();
121 
122         bool capabilitiesEmpty = (pluginIncomingCapabilities.isEmpty() && pluginOutgoingCapabilities.isEmpty());
123 #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
124         bool capabilitiesIntersect = (outgoing.intersects(pluginIncomingCapabilities) || incoming.intersects(pluginOutgoingCapabilities));
125 #else
126         QSet<QString> commonIncoming = incoming;
127         commonIncoming.intersect(pluginOutgoingCapabilities);
128         QSet<QString> commonOutgoing = outgoing;
129         commonOutgoing.intersect(pluginIncomingCapabilities);
130         bool capabilitiesIntersect = (!commonIncoming.isEmpty() || !commonOutgoing.isEmpty());
131 #endif
132 
133         if (capabilitiesIntersect || capabilitiesEmpty) {
134             ret += service.pluginId();
135         } else {
136             qCDebug(KDECONNECT_CORE) << "Not loading plugin" << service.pluginId() <<  "because device doesn't support it";
137         }
138     }
139 
140     return ret;
141 }
142