Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qdbusinternalfilters.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2016 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:significant reason:default
5
7
9#include <QtCore/qcoreapplication.h>
10#include <QtCore/qmetaobject.h>
11#include <QtCore/qstringlist.h>
12#include <QtCore/qthread.h>
13
18#include "qdbusmessage.h"
19#include "qdbusmetatype.h"
21#include "qdbusmessage_p.h"
22#include "qdbusutil_p.h"
24
25#include <algorithm>
26
27#ifndef QT_NO_DBUS
28
29QT_BEGIN_NAMESPACE
30
31using namespace Qt::StringLiterals;
32
33// defined in qdbusxmlgenerator.cpp
34extern Q_DBUS_EXPORT QString qDBusGenerateMetaObjectXml(QString interface, const QMetaObject *mo,
35 const QMetaObject *base, int flags);
36
37static const char introspectableInterfaceXml[] =
38 " <interface name=\"org.freedesktop.DBus.Introspectable\">\n"
39 " <method name=\"Introspect\">\n"
40 " <arg name=\"xml_data\" type=\"s\" direction=\"out\"/>\n"
41 " </method>\n"
42 " </interface>\n";
43
44static const char propertiesInterfaceXml[] =
45 " <interface name=\"org.freedesktop.DBus.Properties\">\n"
46 " <method name=\"Get\">\n"
47 " <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
48 " <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
49 " <arg name=\"value\" type=\"v\" direction=\"out\"/>\n"
50 " </method>\n"
51 " <method name=\"Set\">\n"
52 " <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
53 " <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
54 " <arg name=\"value\" type=\"v\" direction=\"in\"/>\n"
55 " </method>\n"
56 " <method name=\"GetAll\">\n"
57 " <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
58 " <arg name=\"values\" type=\"a{sv}\" direction=\"out\"/>\n"
59 " <annotation name=\"org.qtproject.QtDBus.QtTypeName.Out0\" value=\"QVariantMap\"/>\n"
60 " </method>\n"
61 " <signal name=\"PropertiesChanged\">\n"
62 " <arg name=\"interface_name\" type=\"s\" direction=\"out\"/>\n"
63 " <arg name=\"changed_properties\" type=\"a{sv}\" direction=\"out\"/>\n"
64 " <annotation name=\"org.qtproject.QtDBus.QtTypeName.Out1\" value=\"QVariantMap\"/>\n"
65 " <arg name=\"invalidated_properties\" type=\"as\" direction=\"out\"/>\n"
66 " </signal>\n"
67 " </interface>\n";
68
69static const char peerInterfaceXml[] =
70 " <interface name=\"org.freedesktop.DBus.Peer\">\n"
71 " <method name=\"Ping\"/>\n"
72 " <method name=\"GetMachineId\">\n"
73 " <arg name=\"machine_uuid\" type=\"s\" direction=\"out\"/>\n"
74 " </method>\n"
75 " </interface>\n";
76
77static QString generateSubObjectXml(const QObject *object)
78{
79 QString retval;
80 for (const QObject *child : object->children()) {
81 QString name = child->objectName();
82 if (!name.isEmpty() && QDBusUtil::isValidPartOfObjectPath(name))
83 retval += " <node name=\""_L1 + name + "\"/>\n"_L1;
84 }
85 return retval;
86}
87
88// declared as extern in qdbusconnection_p.h
89
90QString qDBusIntrospectObject(const QDBusConnectionPrivate::ObjectTreeNode &node, const QString &path)
91{
92 // object may be null
93
94 QString xml_data(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE ""_L1);
95 xml_data += "<node>\n"_L1;
96
97 if (node.obj) {
98 Q_ASSERT_X(QThread::currentThread() == node.obj->thread(),
99 "QDBusConnection: internal threading error",
100 "function called for an object that is in another thread!!");
101
102 if (node.flags & (QDBusConnection::ExportScriptableContents
103 | QDBusConnection::ExportNonScriptableContents)) {
104 // create XML for the object itself
105 const QMetaObject *mo = node.obj->metaObject();
106 for ( ; mo != &QObject::staticMetaObject; mo = mo->superClass())
107 xml_data += qDBusGenerateMetaObjectXml(node.interfaceName, mo, mo->superClass(),
108 node.flags);
109 }
110
111 // does this object have adaptors?
112 QDBusAdaptorConnector *connector;
113 if (node.flags & QDBusConnection::ExportAdaptors &&
114 (connector = qDBusFindAdaptorConnector(node.obj))) {
115
116 // trasverse every adaptor in this object
117 for (const QDBusAdaptorConnector::AdaptorData &adaptorData :
118 std::as_const(connector->adaptors)) {
119 // add the interface:
120 QString ifaceXml =
121 QDBusAbstractAdaptorPrivate::retrieveIntrospectionXml(adaptorData.adaptor);
122 if (ifaceXml.isEmpty()) {
123 // add the interface's contents:
124 ifaceXml += qDBusGenerateMetaObjectXml(
125 QString::fromLatin1(adaptorData.interface),
126 adaptorData.adaptor->metaObject(),
127 &QDBusAbstractAdaptor::staticMetaObject,
128 QDBusConnection::ExportScriptableContents
129 | QDBusConnection::ExportNonScriptableContents);
130
131 QDBusAbstractAdaptorPrivate::saveIntrospectionXml(adaptorData.adaptor,
132 ifaceXml);
133 }
134
135 xml_data += ifaceXml;
136 }
137 }
138
139 // is it a virtual node that handles introspection itself?
140 if (node.flags & QDBusConnectionPrivate::VirtualObject) {
141 xml_data += node.treeNode->introspect(path);
142 }
143
144 xml_data += QLatin1StringView(propertiesInterfaceXml);
145 }
146
147 xml_data += QLatin1StringView(introspectableInterfaceXml);
148 xml_data += QLatin1StringView(peerInterfaceXml);
149
150 if (node.flags & QDBusConnection::ExportChildObjects) {
151 xml_data += generateSubObjectXml(node.obj);
152 } else {
153 // generate from the object tree
154 for (const QDBusConnectionPrivate::ObjectTreeNode &node : node.children) {
155 if (node.obj || !node.children.isEmpty())
156 xml_data += " <node name=\""_L1 + node.name + "\"/>\n"_L1;
157 }
158 }
159
160 xml_data += "</node>\n"_L1;
161 return xml_data;
162}
163
164// implement the D-Bus interface org.freedesktop.DBus.Properties
165
166static inline QDBusMessage interfaceNotFoundError(const QDBusMessage &msg, const QString &interface_name)
167{
168 return msg.createErrorReply(QDBusError::UnknownInterface,
169 "Interface %1 was not found in object %2"_L1
170 .arg(interface_name, msg.path()));
171}
172
173static inline QDBusMessage
174propertyNotFoundError(const QDBusMessage &msg, const QString &interface_name, const QByteArray &property_name)
175{
176 return msg.createErrorReply(QDBusError::UnknownProperty,
177 "Property %1%2%3 was not found in object %4"_L1
178 .arg(interface_name,
179 interface_name.isEmpty() ? ""_L1 : "."_L1,
180 QLatin1StringView(property_name),
181 msg.path()));
182}
183
184QDBusMessage qDBusPropertyGet(const QDBusConnectionPrivate::ObjectTreeNode &node,
185 const QDBusMessage &msg)
186{
187 Q_ASSERT(msg.arguments().size() == 2);
188 Q_ASSERT_X(!node.obj || QThread::currentThread() == node.obj->thread(),
189 "QDBusConnection: internal threading error",
190 "function called for an object that is in another thread!!");
191
192 QString interface_name = msg.arguments().at(0).toString();
193 QByteArray property_name = msg.arguments().at(1).toString().toUtf8();
194
195 const QDBusAdaptorConnector *connector;
196 QVariant value;
197 bool interfaceFound = false;
198 if (node.flags & QDBusConnection::ExportAdaptors &&
199 (connector = qDBusFindAdaptorConnector(node.obj))) {
200
201 // find the class that implements interface_name or try until we've found the property
202 // in case of an empty interface
203 if (interface_name.isEmpty()) {
204 for (const QDBusAdaptorConnector::AdaptorData &adaptorData : connector->adaptors) {
205 const QMetaObject *mo = adaptorData.adaptor->metaObject();
206 int pidx = mo->indexOfProperty(property_name);
207 if (pidx != -1) {
208 value = mo->property(pidx).read(adaptorData.adaptor);
209 break;
210 }
211 }
212 } else {
213 QDBusAdaptorConnector::AdaptorMap::ConstIterator it;
214 it = std::lower_bound(connector->adaptors.constBegin(), connector->adaptors.constEnd(),
215 interface_name);
216 if (it != connector->adaptors.constEnd() && interface_name == QLatin1StringView(it->interface)) {
217 interfaceFound = true;
218 value = it->adaptor->property(property_name);
219 }
220 }
221 }
222
223 if (!interfaceFound && !value.isValid()
224 && node.flags & (QDBusConnection::ExportAllProperties |
225 QDBusConnection::ExportNonScriptableProperties)) {
226 // try the object itself
227 if (!interface_name.isEmpty())
228 interfaceFound = qDBusInterfaceInObject(node.obj, interface_name);
229
230 if (interfaceFound) {
231 int pidx = node.obj->metaObject()->indexOfProperty(property_name);
232 if (pidx != -1) {
233 QMetaProperty mp = node.obj->metaObject()->property(pidx);
234 if ((mp.isScriptable() && (node.flags & QDBusConnection::ExportScriptableProperties)) ||
235 (!mp.isScriptable() && (node.flags & QDBusConnection::ExportNonScriptableProperties)))
236 value = mp.read(node.obj);
237 }
238 }
239 }
240
241 if (!value.isValid()) {
242 // the property was not found
243 if (!interfaceFound)
244 return interfaceNotFoundError(msg, interface_name);
245 return propertyNotFoundError(msg, interface_name, property_name);
246 }
247
248 return msg.createReply(QVariant::fromValue(QDBusVariant(value)));
249}
250
258
259static QDBusMessage propertyWriteReply(const QDBusMessage &msg, const QString &interface_name,
260 const QByteArray &property_name, int status)
261{
262 switch (status) {
263 case PropertyNotFound:
264 return propertyNotFoundError(msg, interface_name, property_name);
265 case PropertyTypeMismatch:
266 return msg.createErrorReply(QDBusError::InvalidArgs,
267 "Invalid arguments for writing to property %1%2%3"_L1
268 .arg(interface_name,
269 interface_name.isEmpty() ? ""_L1 : "."_L1,
270 QLatin1StringView(property_name)));
271 case PropertyReadOnly:
272 return msg.createErrorReply(QDBusError::PropertyReadOnly,
273 "Property %1%2%3 is read-only"_L1
274 .arg(interface_name,
275 interface_name.isEmpty() ? ""_L1 : "."_L1,
276 QLatin1StringView(property_name)));
277 case PropertyWriteFailed:
278 return msg.createErrorReply(QDBusError::InternalError,
279 QString::fromLatin1("Internal error"));
280
282 return msg.createReply();
283 }
284 Q_ASSERT_X(false, "", "Should not be reached");
285 return QDBusMessage();
286}
287
288static int writeProperty(QObject *obj, const QByteArray &property_name, QVariant value,
289 int propFlags = QDBusConnection::ExportAllProperties)
290{
291 const QMetaObject *mo = obj->metaObject();
292 int pidx = mo->indexOfProperty(property_name);
293 if (pidx == -1) {
294 // this object has no property by that name
295 return PropertyNotFound;
296 }
297
298 QMetaProperty mp = mo->property(pidx);
299
300 // check if this property is writable
301 if (!mp.isWritable())
302 return PropertyReadOnly;
303
304 // check if this property is exported
305 bool isScriptable = mp.isScriptable();
306 if (!(propFlags & QDBusConnection::ExportScriptableProperties) && isScriptable)
307 return PropertyNotFound;
308 if (!(propFlags & QDBusConnection::ExportNonScriptableProperties) && !isScriptable)
309 return PropertyNotFound;
310
311 // we found our property
312 // do we have the right type?
313 QMetaType id = mp.metaType();
314 if (!id.isValid()){
315 // type not registered or invalid / void?
316 qWarning("QDBusConnection: Unable to handle unregistered datatype '%s' for property '%s::%s'",
317 mp.typeName(), mo->className(), property_name.constData());
318 return PropertyWriteFailed;
319 }
320
321 if (id.id() != QMetaType::QVariant && value.metaType() == QDBusMetaTypeId::argument()) {
322 // we have to demarshall before writing
323 QVariant other{QMetaType(id)};
324 if (!QDBusMetaType::demarshall(qvariant_cast<QDBusArgument>(value), other.metaType(), other.data())) {
325 qWarning("QDBusConnection: type '%s' (%d) is not registered with QtDBus. "
326 "Use qDBusRegisterMetaType to register it",
327 mp.typeName(), id.id());
328 return PropertyWriteFailed;
329 }
330
331 value = std::move(other);
332 }
333
334 if (mp.metaType() == QMetaType::fromType<QDBusVariant>())
335 value = QVariant::fromValue(QDBusVariant(value));
336
337 // the property type here should match
338 return mp.write(obj, std::move(value)) ? PropertyWriteSuccess : PropertyWriteFailed;
339}
340
341QDBusMessage qDBusPropertySet(const QDBusConnectionPrivate::ObjectTreeNode &node,
342 const QDBusMessage &msg)
343{
344 Q_ASSERT(msg.arguments().size() == 3);
345 Q_ASSERT_X(!node.obj || QThread::currentThread() == node.obj->thread(),
346 "QDBusConnection: internal threading error",
347 "function called for an object that is in another thread!!");
348
349 QString interface_name = msg.arguments().at(0).toString();
350 QByteArray property_name = msg.arguments().at(1).toString().toUtf8();
351 QVariant value = qvariant_cast<QDBusVariant>(msg.arguments().at(2)).variant();
352
353 QDBusAdaptorConnector *connector;
354 if (node.flags & QDBusConnection::ExportAdaptors &&
355 (connector = qDBusFindAdaptorConnector(node.obj))) {
356
357 // find the class that implements interface_name or try until we've found the property
358 // in case of an empty interface
359 if (interface_name.isEmpty()) {
360 for (const QDBusAdaptorConnector::AdaptorData &adaptorData :
361 std::as_const(connector->adaptors)) {
362 int status = writeProperty(adaptorData.adaptor, property_name, value);
363 if (status == PropertyNotFound)
364 continue;
365 return propertyWriteReply(msg, interface_name, property_name, status);
366 }
367 } else {
368 QDBusAdaptorConnector::AdaptorMap::ConstIterator it;
369 it = std::lower_bound(connector->adaptors.constBegin(), connector->adaptors.constEnd(),
370 interface_name);
371 if (it != connector->adaptors.cend() && interface_name == QLatin1StringView(it->interface)) {
372 return propertyWriteReply(msg, interface_name, property_name,
373 writeProperty(it->adaptor, property_name, value));
374 }
375 }
376 }
377
378 if (node.flags & (QDBusConnection::ExportScriptableProperties |
379 QDBusConnection::ExportNonScriptableProperties)) {
380 // try the object itself
381 bool interfaceFound = true;
382 if (!interface_name.isEmpty())
383 interfaceFound = qDBusInterfaceInObject(node.obj, interface_name);
384
385 if (interfaceFound) {
386 return propertyWriteReply(msg, interface_name, property_name,
387 writeProperty(node.obj, property_name, value, node.flags));
388 }
389 }
390
391 // the property was not found
392 if (!interface_name.isEmpty())
393 return interfaceNotFoundError(msg, interface_name);
394 return propertyWriteReply(msg, interface_name, property_name, PropertyNotFound);
395}
396
397// unite two QVariantMaps, but don't generate duplicate keys
398static QVariantMap &operator+=(QVariantMap &lhs, const QVariantMap &rhs)
399{
400 for (const auto &[key, value] : rhs.asKeyValueRange())
401 lhs.insert(key, value);
402 return lhs;
403}
404
405static QVariantMap readAllProperties(QObject *object, int flags)
406{
407 QVariantMap result;
408 const QMetaObject *mo = object->metaObject();
409
410 // QObject has properties, so don't start from 0
411 for (int i = QObject::staticMetaObject.propertyCount(); i < mo->propertyCount(); ++i) {
412 QMetaProperty mp = mo->property(i);
413
414 // is it readable?
415 if (!mp.isReadable())
416 continue;
417
418 // is it a registered property?
419 QMetaType type = mp.metaType();
420 if (!type.isValid())
421 continue;
422 const char *signature = QDBusMetaType::typeToSignature(type);
423 if (!signature)
424 continue;
425
426 // is this property visible from the outside?
427 if ((mp.isScriptable() && flags & QDBusConnection::ExportScriptableProperties) ||
428 (!mp.isScriptable() && flags & QDBusConnection::ExportNonScriptableProperties)) {
429 // yes, it's visible
430 QVariant value = mp.read(object);
431 if (value.isValid())
432 result.insert(QString::fromLatin1(mp.name()), value);
433 }
434 }
435
436 return result;
437}
438
439QDBusMessage qDBusPropertyGetAll(const QDBusConnectionPrivate::ObjectTreeNode &node,
440 const QDBusMessage &msg)
441{
442 Q_ASSERT(msg.arguments().size() == 1);
443 Q_ASSERT_X(!node.obj || QThread::currentThread() == node.obj->thread(),
444 "QDBusConnection: internal threading error",
445 "function called for an object that is in another thread!!");
446
447 QString interface_name = msg.arguments().at(0).toString();
448
449 bool interfaceFound = false;
450 QVariantMap result;
451
452 QDBusAdaptorConnector *connector;
453 if (node.flags & QDBusConnection::ExportAdaptors &&
454 (connector = qDBusFindAdaptorConnector(node.obj))) {
455
456 if (interface_name.isEmpty()) {
457 // iterate over all interfaces
458 for (const QDBusAdaptorConnector::AdaptorData &adaptorData :
459 std::as_const(connector->adaptors)) {
460 result += readAllProperties(adaptorData.adaptor,
461 QDBusConnection::ExportAllProperties);
462 }
463 } else {
464 // find the class that implements interface_name
465 QDBusAdaptorConnector::AdaptorMap::ConstIterator it;
466 it = std::lower_bound(connector->adaptors.constBegin(), connector->adaptors.constEnd(),
467 interface_name);
468 if (it != connector->adaptors.constEnd() && interface_name == QLatin1StringView(it->interface)) {
469 interfaceFound = true;
470 result = readAllProperties(it->adaptor, QDBusConnection::ExportAllProperties);
471 }
472 }
473 }
474
475 if (node.flags & QDBusConnection::ExportAllProperties &&
476 (!interfaceFound || interface_name.isEmpty())) {
477 // try the object itself
478 result += readAllProperties(node.obj, node.flags);
479 interfaceFound = true;
480 }
481
482 if (!interfaceFound && !interface_name.isEmpty()) {
483 // the interface was not found
484 return interfaceNotFoundError(msg, interface_name);
485 }
486
487 return msg.createReply(QVariant::fromValue(result));
488}
489
490QT_END_NAMESPACE
491
492#endif // QT_NO_DBUS
#define DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
QDBusMessage qDBusPropertyGetAll(const QDBusConnectionPrivate::ObjectTreeNode &node, const QDBusMessage &msg)
QDBusMessage qDBusPropertyGet(const QDBusConnectionPrivate::ObjectTreeNode &node, const QDBusMessage &msg)
QDBusMessage qDBusPropertySet(const QDBusConnectionPrivate::ObjectTreeNode &node, const QDBusMessage &msg)
static QDBusMessage propertyWriteReply(const QDBusMessage &msg, const QString &interface_name, const QByteArray &property_name, int status)
static QVariantMap & operator+=(QVariantMap &lhs, const QVariantMap &rhs)
static QString generateSubObjectXml(const QObject *object)
static QVariantMap readAllProperties(QObject *object, int flags)
@ PropertyWriteFailed
@ PropertyWriteSuccess
@ PropertyTypeMismatch
static const char peerInterfaceXml[]
QString qDBusIntrospectObject(const QDBusConnectionPrivate::ObjectTreeNode &node, const QString &path)
static int writeProperty(QObject *obj, const QByteArray &property_name, QVariant value, int propFlags=QDBusConnection::ExportAllProperties)
static const char propertiesInterfaceXml[]
static QDBusMessage propertyNotFoundError(const QDBusMessage &msg, const QString &interface_name, const QByteArray &property_name)
static const char introspectableInterfaceXml[]
static QDBusMessage interfaceNotFoundError(const QDBusMessage &msg, const QString &interface_name)