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