1 /*
2     SPDX-FileCopyrightText: 2006 Michaël Larouche <michael.larouche@kdemail.net>
3 
4     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5 */
6 #include "fakemanager.h"
7 
8 #include "fakedevice.h"
9 
10 // Qt includes
11 #include <QDebug>
12 #include <QDomDocument>
13 #include <QDomElement>
14 #include <QDomNode>
15 #include <QFile>
16 #include <QString>
17 #ifdef QT_DBUS_LIB
18 #include <QDBusConnection>
19 #endif
20 
21 using namespace Solid::Backends::Fake;
22 
23 class FakeManager::Private
24 {
25 public:
26     QMap<QString, FakeDevice *> loadedDevices;
27     QMap<QString, QMap<QString, QVariant>> hiddenDevices;
28     QString xmlFile;
29     QSet<Solid::DeviceInterface::Type> supportedInterfaces;
30 };
31 
FakeManager(QObject * parent,const QString & xmlFile)32 FakeManager::FakeManager(QObject *parent, const QString &xmlFile)
33     : Solid::Ifaces::DeviceManager(parent)
34     , d(new Private)
35 {
36     QString machineXmlFile = xmlFile;
37     d->xmlFile = machineXmlFile;
38 
39 #ifdef QT_DBUS_LIB
40     QDBusConnection::sessionBus().registerObject("/org/kde/solid/fakehw", this, QDBusConnection::ExportNonScriptableSlots);
41 #endif
42 
43     parseMachineFile();
44 
45     // clang-format off
46     d->supportedInterfaces << Solid::DeviceInterface::GenericInterface
47                            << Solid::DeviceInterface::Processor
48                            << Solid::DeviceInterface::Block
49                            << Solid::DeviceInterface::StorageAccess
50                            << Solid::DeviceInterface::StorageDrive
51                            << Solid::DeviceInterface::OpticalDrive
52                            << Solid::DeviceInterface::StorageVolume
53                            << Solid::DeviceInterface::OpticalDisc
54                            << Solid::DeviceInterface::Camera
55                            << Solid::DeviceInterface::PortableMediaPlayer
56                            << Solid::DeviceInterface::Battery
57                            << Solid::DeviceInterface::NetworkShare;
58     // clang-format on
59 }
60 
~FakeManager()61 FakeManager::~FakeManager()
62 {
63 #ifdef QT_DBUS_LIB
64     QDBusConnection::sessionBus().unregisterObject("/org/kde/solid/fakehw", QDBusConnection::UnregisterTree);
65 #endif
66     qDeleteAll(d->loadedDevices);
67     delete d;
68 }
69 
udiPrefix() const70 QString FakeManager::udiPrefix() const
71 {
72     return "/org/kde/solid/fakehw";
73 }
74 
supportedInterfaces() const75 QSet<Solid::DeviceInterface::Type> FakeManager::supportedInterfaces() const
76 {
77     return d->supportedInterfaces;
78 }
79 
allDevices()80 QStringList FakeManager::allDevices()
81 {
82     QStringList deviceUdiList;
83 
84     for (const FakeDevice *device : std::as_const(d->loadedDevices)) {
85         deviceUdiList.append(device->udi());
86     }
87 
88     return deviceUdiList;
89 }
90 
devicesFromQuery(const QString & parentUdi,Solid::DeviceInterface::Type type)91 QStringList FakeManager::devicesFromQuery(const QString &parentUdi, Solid::DeviceInterface::Type type)
92 {
93     if (!parentUdi.isEmpty()) {
94         QStringList found = findDeviceStringMatch(QLatin1String("parent"), parentUdi);
95 
96         if (type == Solid::DeviceInterface::Unknown) {
97             return found;
98         }
99 
100         QStringList result;
101 
102         QStringList::ConstIterator it = found.constBegin();
103         QStringList::ConstIterator end = found.constEnd();
104 
105         for (; it != end; ++it) {
106             FakeDevice *device = d->loadedDevices[*it];
107 
108             if (device->queryDeviceInterface(type)) {
109                 result << *it;
110             }
111         }
112 
113         return result;
114     } else if (type != Solid::DeviceInterface::Unknown) {
115         return findDeviceByDeviceInterface(type);
116     } else {
117         return allDevices();
118     }
119 }
120 
createDevice(const QString & udi)121 QObject *FakeManager::createDevice(const QString &udi)
122 {
123     if (d->loadedDevices.contains(udi)) {
124         return new FakeDevice(*d->loadedDevices[udi]);
125     }
126 
127     return nullptr;
128 }
129 
findDevice(const QString & udi)130 FakeDevice *FakeManager::findDevice(const QString &udi)
131 {
132     return d->loadedDevices.value(udi);
133 }
134 
findDeviceStringMatch(const QString & key,const QString & value)135 QStringList FakeManager::findDeviceStringMatch(const QString &key, const QString &value)
136 {
137     QStringList result;
138     for (const FakeDevice *device : std::as_const(d->loadedDevices)) {
139         if (device->property(key).toString() == value) {
140             result.append(device->udi());
141         }
142     }
143 
144     return result;
145 }
146 
findDeviceByDeviceInterface(Solid::DeviceInterface::Type type)147 QStringList FakeManager::findDeviceByDeviceInterface(Solid::DeviceInterface::Type type)
148 {
149     QStringList result;
150     for (const FakeDevice *device : std::as_const(d->loadedDevices)) {
151         if (device->queryDeviceInterface(type)) {
152             result.append(device->udi());
153         }
154     }
155 
156     return result;
157 }
158 
plug(const QString & udi)159 void FakeManager::plug(const QString &udi)
160 {
161     if (d->hiddenDevices.contains(udi)) {
162         QMap<QString, QVariant> properties = d->hiddenDevices.take(udi);
163         d->loadedDevices[udi] = new FakeDevice(udi, properties);
164         Q_EMIT deviceAdded(udi);
165     }
166 }
167 
unplug(const QString & udi)168 void FakeManager::unplug(const QString &udi)
169 {
170     if (d->loadedDevices.contains(udi)) {
171         FakeDevice *dev = d->loadedDevices.take(udi);
172         d->hiddenDevices[udi] = dev->allProperties();
173         Q_EMIT deviceRemoved(udi);
174         delete dev;
175     }
176 }
177 
parseMachineFile()178 void FakeManager::parseMachineFile()
179 {
180     QFile machineFile(d->xmlFile);
181     if (!machineFile.open(QIODevice::ReadOnly)) {
182         qWarning() << Q_FUNC_INFO << "Error while opening " << d->xmlFile;
183         return;
184     }
185 
186     QDomDocument fakeDocument;
187     if (!fakeDocument.setContent(&machineFile)) {
188         qWarning() << Q_FUNC_INFO << "Error while creating the QDomDocument.";
189         machineFile.close();
190         return;
191     }
192     machineFile.close();
193 
194     qDebug() << Q_FUNC_INFO << "Parsing fake computer XML: " << d->xmlFile;
195     QDomElement mainElement = fakeDocument.documentElement();
196     QDomNode node = mainElement.firstChild();
197     while (!node.isNull()) {
198         QDomElement tempElement = node.toElement();
199         if (!tempElement.isNull() && tempElement.tagName() == QLatin1String("device")) {
200             FakeDevice *tempDevice = parseDeviceElement(tempElement);
201             if (tempDevice) {
202                 Q_ASSERT(!d->loadedDevices.contains(tempDevice->udi()));
203                 d->loadedDevices.insert(tempDevice->udi(), tempDevice);
204                 Q_EMIT deviceAdded(tempDevice->udi());
205             }
206         }
207 
208         node = node.nextSibling();
209     }
210 }
211 
parseDeviceElement(const QDomElement & deviceElement)212 FakeDevice *FakeManager::parseDeviceElement(const QDomElement &deviceElement)
213 {
214     FakeDevice *device = nullptr;
215     QMap<QString, QVariant> propertyMap;
216     QString udi = deviceElement.attribute("udi");
217 
218     QDomNode propertyNode = deviceElement.firstChild();
219     while (!propertyNode.isNull()) {
220         QDomElement propertyElement = propertyNode.toElement();
221         if (!propertyElement.isNull() && propertyElement.tagName() == QLatin1String("property")) {
222             QString propertyKey;
223             QVariant propertyValue;
224 
225             propertyKey = propertyElement.attribute("key");
226             propertyValue = QVariant(propertyElement.text());
227 
228             propertyMap.insert(propertyKey, propertyValue);
229         }
230 
231         propertyNode = propertyNode.nextSibling();
232     }
233 
234     if (!propertyMap.isEmpty()) {
235         device = new FakeDevice(udi, propertyMap);
236     }
237 
238     return device;
239 }
240