1 /*
2  *   SPDX-FileCopyrightText: 2012 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
3  *
4  *   SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
5  */
6 
7 #include "DiscoverBackendsFactory.h"
8 #include "libdiscover_debug.h"
9 #include "resources/AbstractResourcesBackend.h"
10 #include "resources/ResourcesModel.h"
11 #include "utils.h"
12 #include <KConfigGroup>
13 #include <KDesktopFile>
14 #include <KLocalizedString>
15 #include <KSharedConfig>
16 #include <QCommandLineParser>
17 #include <QDir>
18 #include <QDirIterator>
19 #include <QPluginLoader>
20 #include <QStandardPaths>
21 
Q_GLOBAL_STATIC(QStringList,s_requestedBackends)22 Q_GLOBAL_STATIC(QStringList, s_requestedBackends)
23 
24 void DiscoverBackendsFactory::setRequestedBackends(const QStringList &backends)
25 {
26     *s_requestedBackends = backends;
27 }
28 
hasRequestedBackends()29 bool DiscoverBackendsFactory::hasRequestedBackends()
30 {
31     return !s_requestedBackends->isEmpty();
32 }
33 
DiscoverBackendsFactory()34 DiscoverBackendsFactory::DiscoverBackendsFactory()
35 {
36 }
37 
backend(const QString & name) const38 QVector<AbstractResourcesBackend *> DiscoverBackendsFactory::backend(const QString &name) const
39 {
40     if (QDir::isAbsolutePath(name) && QStandardPaths::isTestModeEnabled()) {
41         return backendForFile(name, QFileInfo(name).fileName());
42     } else {
43         return backendForFile(name, name);
44     }
45 }
46 
backendForFile(const QString & libname,const QString & name) const47 QVector<AbstractResourcesBackend *> DiscoverBackendsFactory::backendForFile(const QString &libname, const QString &name) const
48 {
49     QPluginLoader *loader = new QPluginLoader(QLatin1String("discover/") + libname, ResourcesModel::global());
50 
51     // qCDebug(LIBDISCOVER_LOG) << "trying to load plugin:" << loader->fileName();
52     AbstractResourcesBackendFactory *f = qobject_cast<AbstractResourcesBackendFactory *>(loader->instance());
53     if (!f) {
54         qCWarning(LIBDISCOVER_LOG) << "error loading" << libname << loader->errorString() << loader->metaData();
55         return {};
56     }
57     auto instances = f->newInstance(ResourcesModel::global(), name);
58     if (instances.isEmpty()) {
59         qCWarning(LIBDISCOVER_LOG) << "Couldn't find the backend: " << libname << "among" << allBackendNames(false, true);
60         return instances;
61     }
62 
63     return instances;
64 }
65 
allBackendNames(bool whitelist,bool allowDummy) const66 QStringList DiscoverBackendsFactory::allBackendNames(bool whitelist, bool allowDummy) const
67 {
68     if (whitelist) {
69         QStringList whitelistNames = *s_requestedBackends;
70         if (!whitelistNames.isEmpty())
71             return whitelistNames;
72     }
73 
74     QStringList pluginNames;
75     const auto libraryPaths = QCoreApplication::libraryPaths();
76     for (const QString &dir : libraryPaths) {
77         QDirIterator it(dir + QStringLiteral("/discover"), QDir::Files);
78         while (it.hasNext()) {
79             it.next();
80             if (QLibrary::isLibrary(it.fileName()) && (allowDummy || it.fileName() != QLatin1String("dummy-backend.so"))) {
81                 pluginNames += it.fileInfo().baseName();
82             }
83         }
84     }
85 
86     pluginNames.removeDuplicates(); // will happen when discover is installed twice on the system
87     return pluginNames;
88 }
89 
allBackends() const90 QVector<AbstractResourcesBackend *> DiscoverBackendsFactory::allBackends() const
91 {
92     QStringList names = allBackendNames();
93     auto ret = kTransform<QVector<AbstractResourcesBackend *>>(names, [this](const QString &name) {
94         return backend(name);
95     });
96     ret.removeAll(nullptr);
97 
98     if (ret.isEmpty())
99         qCWarning(LIBDISCOVER_LOG) << "Didn't find any Discover backend!";
100     return ret;
101 }
102 
backendsCount() const103 int DiscoverBackendsFactory::backendsCount() const
104 {
105     return allBackendNames().count();
106 }
107 
setupCommandLine(QCommandLineParser * parser)108 void DiscoverBackendsFactory::setupCommandLine(QCommandLineParser *parser)
109 {
110     parser->addOption(QCommandLineOption(QStringLiteral("backends"),
111                                          i18n("List all the backends we'll want to have loaded, separated by comma ','."),
112                                          QStringLiteral("names")));
113 }
114 
processCommandLine(QCommandLineParser * parser,bool test)115 void DiscoverBackendsFactory::processCommandLine(QCommandLineParser *parser, bool test)
116 {
117     QStringList backends = test //
118         ? QStringList{QStringLiteral("dummy-backend")} //
119         : parser->value(QStringLiteral("backends")).split(QLatin1Char(','), Qt::SkipEmptyParts);
120     for (auto &backend : backends) {
121         if (!backend.endsWith(QLatin1String("-backend")))
122             backend.append(QLatin1String("-backend"));
123     }
124     *s_requestedBackends = backends;
125 }
126