1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qdbusinterface.h"
5#include "qdbusinterface_p.h"
6
7#include "qdbus_symbols_p.h"
8#include <QtCore/qpointer.h>
9#include <QtCore/qstringlist.h>
10
11#include "qdbusmetatype_p.h"
12#include "qdbusconnection_p.h"
13
14#ifndef QT_NO_DBUS
15
16QT_BEGIN_NAMESPACE
17
18using namespace Qt::StringLiterals;
19
20static void copyArgument(void *to, int id, const QVariant &arg)
21{
22 if (id == arg.metaType().id()) {
23 switch (id) {
24 case QMetaType::Bool:
25 *reinterpret_cast<bool *>(to) = arg.toBool();
26 return;
27
28 case QMetaType::UChar:
29 *reinterpret_cast<uchar *>(to) = qvariant_cast<uchar>(v: arg);
30 return;
31
32 case QMetaType::Short:
33 *reinterpret_cast<short *>(to) = qvariant_cast<short>(v: arg);
34 return;
35
36 case QMetaType::UShort:
37 *reinterpret_cast<ushort *>(to) = qvariant_cast<ushort>(v: arg);
38 return;
39
40 case QMetaType::Int:
41 *reinterpret_cast<int *>(to) = arg.toInt();
42 return;
43
44 case QMetaType::UInt:
45 *reinterpret_cast<uint *>(to) = arg.toUInt();
46 return;
47
48 case QMetaType::LongLong:
49 *reinterpret_cast<qlonglong *>(to) = arg.toLongLong();
50 return;
51
52 case QMetaType::ULongLong:
53 *reinterpret_cast<qulonglong *>(to) = arg.toULongLong();
54 return;
55
56 case QMetaType::Double:
57 *reinterpret_cast<double *>(to) = arg.toDouble();
58 return;
59
60 case QMetaType::QString:
61 *reinterpret_cast<QString *>(to) = arg.toString();
62 return;
63
64 case QMetaType::QByteArray:
65 *reinterpret_cast<QByteArray *>(to) = arg.toByteArray();
66 return;
67
68 case QMetaType::QStringList:
69 *reinterpret_cast<QStringList *>(to) = arg.toStringList();
70 return;
71 }
72
73 if (id == QDBusMetaTypeId::variant().id()) {
74 *reinterpret_cast<QDBusVariant *>(to) = qvariant_cast<QDBusVariant>(v: arg);
75 return;
76 } else if (id == QDBusMetaTypeId::objectpath().id()) {
77 *reinterpret_cast<QDBusObjectPath *>(to) = qvariant_cast<QDBusObjectPath>(v: arg);
78 return;
79 } else if (id == QDBusMetaTypeId::signature().id()) {
80 *reinterpret_cast<QDBusSignature *>(to) = qvariant_cast<QDBusSignature>(v: arg);
81 return;
82 }
83
84 // those above are the only types possible
85 // the demarshaller code doesn't demarshall anything else
86 qFatal(msg: "Found a decoded basic type in a D-Bus reply that shouldn't be there");
87 }
88
89 // if we got here, it's either an un-dermarshalled type or a mismatch
90 if (arg.metaType() != QDBusMetaTypeId::argument()) {
91 // it's a mismatch
92 //qWarning?
93 return;
94 }
95
96 // is this type registered?
97 const char *userSignature = QDBusMetaType::typeToSignature(type: QMetaType(id));
98 if (!userSignature || !*userSignature) {
99 // type not registered
100 //qWarning?
101 return;
102 }
103
104 // is it the same signature?
105 QDBusArgument dbarg = qvariant_cast<QDBusArgument>(v: arg);
106 if (dbarg.currentSignature() != QLatin1StringView(userSignature)) {
107 // not the same signature, another mismatch
108 //qWarning?
109 return;
110 }
111
112 // we can demarshall
113 QDBusMetaType::demarshall(dbarg, id: QMetaType(id), data: to);
114}
115
116QDBusInterfacePrivate::QDBusInterfacePrivate(const QString &serv, const QString &p,
117 const QString &iface, const QDBusConnection &con)
118 : QDBusAbstractInterfacePrivate(serv, p, iface, con, true), metaObject(nullptr)
119{
120 // QDBusAbstractInterfacePrivate's constructor checked the parameters for us
121 if (connection.isConnected()) {
122 metaObject = connectionPrivate()->findMetaObject(service, path, interface, error&: lastError);
123
124 if (!metaObject) {
125 // creation failed, somehow
126 // most common causes are that the service doesn't exist or doesn't support introspection
127 // those are not fatal errors, so we continue working
128
129 if (!lastError.isValid())
130 lastError = QDBusError(QDBusError::InternalError, "Unknown error"_L1);
131 }
132 }
133}
134
135QDBusInterfacePrivate::~QDBusInterfacePrivate()
136{
137 if (metaObject && !metaObject->cached)
138 delete metaObject;
139}
140
141
142/*!
143 \class QDBusInterface
144 \inmodule QtDBus
145 \since 4.2
146
147 \brief The QDBusInterface class is a proxy for interfaces on remote objects.
148
149 QDBusInterface is a generic accessor class that is used to place calls to remote objects,
150 connect to signals exported by remote objects and get/set the value of remote properties. This
151 class is useful for dynamic access to remote objects: that is, when you do not have a generated
152 code that represents the remote interface.
153
154 Calls are usually placed by using the call() function, which constructs the message, sends it
155 over the bus, waits for the reply and decodes the reply. Signals are connected to by using the
156 normal QObject::connect() function. Finally, properties are accessed using the
157 QObject::property() and QObject::setProperty() functions.
158
159 The following code snippet demonstrates how to perform a
160 mathematical operation of \tt{"2 + 2"} in a remote application
161 called \c com.example.Calculator, accessed via the session bus.
162
163 \snippet code/src_qdbus_qdbusinterface.cpp 0
164
165 \sa {Qt D-Bus XML compiler (qdbusxml2cpp)}
166*/
167
168/*!
169 Creates a dynamic QDBusInterface object associated with the
170 interface \a interface on object at path \a path on service \a
171 service, using the given \a connection. If \a interface is an
172 empty string, the object created will refer to the merging of all
173 interfaces found by introspecting that object. Otherwise if
174 \a interface is not empty, the QDBusInterface object will be cached
175 to speedup further creations of the same interface.
176
177 \a parent is passed to the base class constructor.
178
179 If the remote service \a service is not present or if an error
180 occurs trying to obtain the description of the remote interface
181 \a interface, the object created will not be valid (see
182 isValid()).
183*/
184QDBusInterface::QDBusInterface(const QString &service, const QString &path, const QString &interface,
185 const QDBusConnection &connection, QObject *parent)
186 : QDBusAbstractInterface(*new QDBusInterfacePrivate(service, path, interface, connection),
187 parent)
188{
189}
190
191/*!
192 Destroy the object interface and frees up any resource used.
193*/
194QDBusInterface::~QDBusInterface()
195{
196 // resources are freed in QDBusInterfacePrivate::~QDBusInterfacePrivate()
197}
198
199/*!
200 \internal
201 Overrides QObject::metaObject to return our own copy.
202*/
203const QMetaObject *QDBusInterface::metaObject() const
204{
205 return d_func()->metaObject ? d_func()->metaObject : &QDBusAbstractInterface::staticMetaObject;
206}
207
208/*!
209 \internal
210 Override QObject::qt_metacast to catch the interface name too.
211*/
212void *QDBusInterface::qt_metacast(const char *_clname)
213{
214 if (!_clname) return nullptr;
215 if (!strcmp(s1: _clname, s2: "QDBusInterface"))
216 return static_cast<void*>(const_cast<QDBusInterface*>(this));
217 if (d_func()->interface.toLatin1() == _clname)
218 return static_cast<void*>(const_cast<QDBusInterface*>(this));
219 return QDBusAbstractInterface::qt_metacast(_clname);
220}
221
222/*!
223 \internal
224 Dispatch the call through the private.
225*/
226int QDBusInterface::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
227{
228 _id = QDBusAbstractInterface::qt_metacall(_c, _id, _a);
229 if (_id < 0 || !d_func()->isValid || !d_func()->metaObject)
230 return _id;
231 return d_func()->metacall(c: _c, id: _id, argv: _a);
232}
233
234int QDBusInterfacePrivate::metacall(QMetaObject::Call c, int id, void **argv)
235{
236 Q_Q(QDBusInterface);
237
238 if (c == QMetaObject::InvokeMetaMethod) {
239 int offset = metaObject->methodOffset();
240 QMetaMethod mm = metaObject->method(index: id + offset);
241
242 if (mm.methodType() == QMetaMethod::Signal) {
243 // signal relay from D-Bus world to Qt world
244 QMetaObject::activate(sender: q, metaObject, local_signal_index: id, argv);
245
246 } else if (mm.methodType() == QMetaMethod::Slot || mm.methodType() == QMetaMethod::Method) {
247 // method call relay from Qt world to D-Bus world
248 // get D-Bus equivalent signature
249 QString methodName = QString::fromLatin1(ba: mm.name());
250 const int *inputTypes = metaObject->inputTypesForMethod(id);
251 int inputTypesCount = *inputTypes;
252
253 // we will assume that the input arguments were passed correctly
254 QVariantList args;
255 args.reserve(asize: inputTypesCount);
256 int i = 1;
257 for ( ; i <= inputTypesCount; ++i)
258 args << QVariant(QMetaType(inputTypes[i]), argv[i]);
259
260 // make the call
261 QDBusMessage reply = q->callWithArgumentList(mode: QDBus::Block, method: methodName, args);
262
263 if (reply.type() == QDBusMessage::ReplyMessage) {
264 // attempt to demarshall the return values
265 args = reply.arguments();
266 QVariantList::ConstIterator it = args.constBegin();
267 const int *outputTypes = metaObject->outputTypesForMethod(id);
268 int outputTypesCount = *outputTypes++;
269
270 if (mm.returnType() != QMetaType::UnknownType && mm.returnType() != QMetaType::Void) {
271 // this method has a return type
272 if (argv[0] && it != args.constEnd())
273 copyArgument(to: argv[0], id: *outputTypes++, arg: *it);
274
275 // skip this argument even if we didn't copy it
276 --outputTypesCount;
277 ++it;
278 }
279
280 for (int j = 0; j < outputTypesCount && it != args.constEnd(); ++i, ++j, ++it) {
281 copyArgument(to: argv[i], id: outputTypes[j], arg: *it);
282 }
283 }
284
285 // done
286 lastError = QDBusError(reply);
287 return -1;
288 }
289 }
290 return id;
291}
292
293QT_END_NAMESPACE
294
295#endif // QT_NO_DBUS
296

source code of qtbase/src/dbus/qdbusinterface.cpp