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.userType()) {
57 switch (id) {
58 case QVariant::Bool:
59 *reinterpret_cast<bool *>(to) = arg.toBool();
60 return;
61
62 case QMetaType::UChar:
63 *reinterpret_cast<uchar *>(to) = arg.value<uchar>();
64 return;
65
66 case QMetaType::Short:
67 *reinterpret_cast<short *>(to) = arg.value<short>();
68 return;
69
70 case QMetaType::UShort:
71 *reinterpret_cast<ushort *>(to) = arg.value<ushort>();
72 return;
73
74 case QVariant::Int:
75 *reinterpret_cast<int *>(to) = arg.toInt();
76 return;
77
78 case QVariant::UInt:
79 *reinterpret_cast<uint *>(to) = arg.toUInt();
80 return;
81
82 case QVariant::LongLong:
83 *reinterpret_cast<qlonglong *>(to) = arg.toLongLong();
84 return;
85
86 case QVariant::ULongLong:
87 *reinterpret_cast<qulonglong *>(to) = arg.toULongLong();
88 return;
89
90 case QVariant::Double:
91 *reinterpret_cast<double *>(to) = arg.toDouble();
92 return;
93
94 case QVariant::String:
95 *reinterpret_cast<QString *>(to) = arg.toString();
96 return;
97
98 case QVariant::ByteArray:
99 *reinterpret_cast<QByteArray *>(to) = arg.toByteArray();
100 return;
101
102 case QVariant::StringList:
103 *reinterpret_cast<QStringList *>(to) = arg.toStringList();
104 return;
105 }
106
107 if (id == QDBusMetaTypeId::variant()) {
108 *reinterpret_cast<QDBusVariant *>(to) = arg.value<QDBusVariant>();
109 return;
110 } else if (id == QDBusMetaTypeId::objectpath()) {
111 *reinterpret_cast<QDBusObjectPath *>(to) = arg.value<QDBusObjectPath>();
112 return;
113 } else if (id == QDBusMetaTypeId::signature()) {
114 *reinterpret_cast<QDBusSignature *>(to) = arg.value<QDBusSignature>();
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.userType() != QDBusMetaTypeId::argument()) {
125 // it's a mismatch
126 //qWarning?
127 return;
128 }
129
130 // is this type registered?
131 const char *userSignature = QDBusMetaType::typeToSignature(id);
132 if (!userSignature || !*userSignature) {
133 // type not registered
134 //qWarning?
135 return;
136 }
137
138 // is it the same signature?
139 QDBusArgument dbarg = arg.value<QDBusArgument>();
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, 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(0)
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 in that object.
208
209 \a parent is passed to the base class constructor.
210
211 If the remote service \a service is not present or if an error
212 occurs trying to obtain the description of the remote interface
213 \a interface, the object created will not be valid (see
214 isValid()).
215*/
216QDBusInterface::QDBusInterface(const QString &service, const QString &path, const QString &interface,
217 const QDBusConnection &connection, QObject *parent)
218 : QDBusAbstractInterface(*new QDBusInterfacePrivate(service, path, interface, connection),
219 parent)
220{
221}
222
223/*!
224 Destroy the object interface and frees up any resource used.
225*/
226QDBusInterface::~QDBusInterface()
227{
228 // resources are freed in QDBusInterfacePrivate::~QDBusInterfacePrivate()
229}
230
231/*!
232 \internal
233 Overrides QObject::metaObject to return our own copy.
234*/
235const QMetaObject *QDBusInterface::metaObject() const
236{
237 return d_func()->metaObject ? d_func()->metaObject : &QDBusAbstractInterface::staticMetaObject;
238}
239
240/*!
241 \internal
242 Override QObject::qt_metacast to catch the interface name too.
243*/
244void *QDBusInterface::qt_metacast(const char *_clname)
245{
246 if (!_clname) return 0;
247 if (!strcmp(_clname, "QDBusInterface"))
248 return static_cast<void*>(const_cast<QDBusInterface*>(this));
249 if (d_func()->interface.toLatin1() == _clname)
250 return static_cast<void*>(const_cast<QDBusInterface*>(this));
251 return QDBusAbstractInterface::qt_metacast(_clname);
252}
253
254/*!
255 \internal
256 Dispatch the call through the private.
257*/
258int QDBusInterface::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
259{
260 _id = QDBusAbstractInterface::qt_metacall(_c, _id, _a);
261 if (_id < 0 || !d_func()->isValid || !d_func()->metaObject)
262 return _id;
263 return d_func()->metacall(_c, _id, _a);
264}
265
266int QDBusInterfacePrivate::metacall(QMetaObject::Call c, int id, void **argv)
267{
268 Q_Q(QDBusInterface);
269
270 if (c == QMetaObject::InvokeMetaMethod) {
271 int offset = metaObject->methodOffset();
272 QMetaMethod mm = metaObject->method(id + offset);
273
274 if (mm.methodType() == QMetaMethod::Signal) {
275 // signal relay from D-Bus world to Qt world
276 QMetaObject::activate(q, metaObject, id, argv);
277
278 } else if (mm.methodType() == QMetaMethod::Slot || mm.methodType() == QMetaMethod::Method) {
279 // method call relay from Qt world to D-Bus world
280 // get D-Bus equivalent signature
281 QString methodName = QString::fromLatin1(mm.name());
282 const int *inputTypes = metaObject->inputTypesForMethod(id);
283 int inputTypesCount = *inputTypes;
284
285 // we will assume that the input arguments were passed correctly
286 QVariantList args;
287 args.reserve(inputTypesCount);
288 int i = 1;
289 for ( ; i <= inputTypesCount; ++i)
290 args << QVariant(inputTypes[i], argv[i]);
291
292 // make the call
293 QDBusMessage reply = q->callWithArgumentList(QDBus::Block, methodName, args);
294
295 if (reply.type() == QDBusMessage::ReplyMessage) {
296 // attempt to demarshall the return values
297 args = reply.arguments();
298 QVariantList::ConstIterator it = args.constBegin();
299 const int *outputTypes = metaObject->outputTypesForMethod(id);
300 int outputTypesCount = *outputTypes++;
301
302 if (mm.returnType() != QMetaType::UnknownType && mm.returnType() != QMetaType::Void) {
303 // this method has a return type
304 if (argv[0] && it != args.constEnd())
305 copyArgument(argv[0], *outputTypes++, *it);
306
307 // skip this argument even if we didn't copy it
308 --outputTypesCount;
309 ++it;
310 }
311
312 for (int j = 0; j < outputTypesCount && it != args.constEnd(); ++i, ++j, ++it) {
313 copyArgument(argv[i], outputTypes[j], *it);
314 }
315 }
316
317 // done
318 lastError = QDBusError(reply);
319 return -1;
320 }
321 }
322 return id;
323}
324
325QT_END_NAMESPACE
326
327#endif // QT_NO_DBUS
328