1/****************************************************************************
2**
3** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4** Contact: http://www.qt-project.org/legal
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 Digia. For licensing terms and
14** conditions see http://qt.digia.com/licensing. For further information
15** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 2.1 requirements
23** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24**
25** In addition, as a special exception, Digia gives you certain additional
26** rights. These rights are described in the Digia Qt LGPL Exception
27** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28**
29** GNU General Public License Usage
30** Alternatively, this file may be used under the terms of the GNU
31** General Public License version 3.0 as published by the Free Software
32** Foundation and appearing in the file LICENSE.GPL included in the
33** packaging of this file. Please review the following information to
34** ensure the GNU General Public License version 3.0 requirements will be
35** met: http://www.gnu.org/copyleft/gpl.html.
36**
37**
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qdbusinterface.h"
43
44#include "qdbus_symbols_p.h"
45#include <QtCore/qpointer.h>
46#include <QtCore/qstringlist.h>
47
48#include "qdbusmetatype_p.h"
49#include "qdbusinterface_p.h"
50#include "qdbusconnection_p.h"
51
52#ifndef QT_NO_DBUS
53
54QT_BEGIN_NAMESPACE
55
56static void copyArgument(void *to, int id, const QVariant &arg)
57{
58 if (id == arg.userType()) {
59 switch (id) {
60 case QVariant::Bool:
61 *reinterpret_cast<bool *>(to) = arg.toBool();
62 return;
63
64 case QMetaType::UChar:
65 *reinterpret_cast<uchar *>(to) = arg.value<uchar>();
66 return;
67
68 case QMetaType::Short:
69 *reinterpret_cast<short *>(to) = arg.value<short>();
70 return;
71
72 case QMetaType::UShort:
73 *reinterpret_cast<ushort *>(to) = arg.value<ushort>();
74 return;
75
76 case QVariant::Int:
77 *reinterpret_cast<int *>(to) = arg.toInt();
78 return;
79
80 case QVariant::UInt:
81 *reinterpret_cast<uint *>(to) = arg.toUInt();
82 return;
83
84 case QVariant::LongLong:
85 *reinterpret_cast<qlonglong *>(to) = arg.toLongLong();
86 return;
87
88 case QVariant::ULongLong:
89 *reinterpret_cast<qulonglong *>(to) = arg.toULongLong();
90 return;
91
92 case QVariant::Double:
93 *reinterpret_cast<double *>(to) = arg.toDouble();
94 return;
95
96 case QVariant::String:
97 *reinterpret_cast<QString *>(to) = arg.toString();
98 return;
99
100 case QVariant::ByteArray:
101 *reinterpret_cast<QByteArray *>(to) = arg.toByteArray();
102 return;
103
104 case QVariant::StringList:
105 *reinterpret_cast<QStringList *>(to) = arg.toStringList();
106 return;
107 }
108
109 if (id == QDBusMetaTypeId::variant) {
110 *reinterpret_cast<QDBusVariant *>(to) = arg.value<QDBusVariant>();
111 return;
112 } else if (id == QDBusMetaTypeId::objectpath) {
113 *reinterpret_cast<QDBusObjectPath *>(to) = arg.value<QDBusObjectPath>();
114 return;
115 } else if (id == QDBusMetaTypeId::signature) {
116 *reinterpret_cast<QDBusSignature *>(to) = arg.value<QDBusSignature>();
117 return;
118 }
119
120 // those above are the only types possible
121 // the demarshaller code doesn't demarshall anything else
122 qFatal("Found a decoded basic type in a D-Bus reply that shouldn't be there");
123 }
124
125 // if we got here, it's either an un-dermarshalled type or a mismatch
126 if (arg.userType() != QDBusMetaTypeId::argument) {
127 // it's a mismatch
128 //qWarning?
129 return;
130 }
131
132 // is this type registered?
133 const char *userSignature = QDBusMetaType::typeToSignature(id);
134 if (!userSignature || !*userSignature) {
135 // type not registered
136 //qWarning?
137 return;
138 }
139
140 // is it the same signature?
141 QDBusArgument dbarg = arg.value<QDBusArgument>();
142 if (dbarg.currentSignature() != QLatin1String(userSignature)) {
143 // not the same signature, another mismatch
144 //qWarning?
145 return;
146 }
147
148 // we can demarshall
149 QDBusMetaType::demarshall(dbarg, id, to);
150}
151
152QDBusInterfacePrivate::QDBusInterfacePrivate(const QString &serv, const QString &p,
153 const QString &iface, const QDBusConnection &con)
154 : QDBusAbstractInterfacePrivate(serv, p, iface, con, true), metaObject(0)
155{
156 // QDBusAbstractInterfacePrivate's constructor checked the parameters for us
157 if (connection.isConnected()) {
158 metaObject = connectionPrivate()->findMetaObject(service, path, interface, lastError);
159
160 if (!metaObject) {
161 // creation failed, somehow
162 // most common causes are that the service doesn't exist or doesn't support introspection
163 // those are not fatal errors, so we continue working
164
165 if (!lastError.isValid())
166 lastError = QDBusError(QDBusError::InternalError, QLatin1String("Unknown error"));
167 }
168 }
169}
170
171QDBusInterfacePrivate::~QDBusInterfacePrivate()
172{
173 if (metaObject && !metaObject->cached)
174 delete metaObject;
175}
176
177
178/*!
179 \class QDBusInterface
180 \inmodule QtDBus
181 \since 4.2
182
183 \brief The QDBusInterface class is a proxy for interfaces on remote objects.
184
185 QDBusInterface is a generic accessor class that is used to place calls to remote objects,
186 connect to signals exported by remote objects and get/set the value of remote properties. This
187 class is useful for dynamic access to remote objects: that is, when you do not have a generated
188 code that represents the remote interface.
189
190 Calls are usually placed by using the call() function, which constructs the message, sends it
191 over the bus, waits for the reply and decodes the reply. Signals are connected to by using the
192 normal QObject::connect() function. Finally, properties are accessed using the
193 QObject::property() and QObject::setProperty() functions.
194
195 The following code snippet demonstrates how to perform a
196 mathematical operation of \tt{"2 + 2"} in a remote application
197 called \c com.example.Calculator, accessed via the session bus.
198
199 \snippet doc/src/snippets/code/src_qdbus_qdbusinterface.cpp 0
200
201 \sa {QtDBus XML compiler (qdbusxml2cpp)}
202*/
203
204/*!
205 Creates a dynamic QDBusInterface object associated with the
206 interface \a interface on object at path \a path on service \a
207 service, using the given \a connection. If \a interface is an
208 empty string, the object created will refer to the merging of all
209 interfaces found in that object.
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 0;
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 = QLatin1String(metaObject->dbusNameForMethod(id));
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 int i = 1;
290 for ( ; i <= inputTypesCount; ++i)
291 args << QVariant(inputTypes[i], argv[i]);
292
293 // make the call
294 QDBusMessage reply = q->callWithArgumentList(QDBus::Block, methodName, args);
295
296 if (reply.type() == QDBusMessage::ReplyMessage) {
297 // attempt to demarshall the return values
298 args = reply.arguments();
299 QVariantList::ConstIterator it = args.constBegin();
300 const int *outputTypes = metaObject->outputTypesForMethod(id);
301 int outputTypesCount = *outputTypes++;
302
303 if (*mm.typeName()) {
304 // this method has a return type
305 if (argv[0] && it != args.constEnd())
306 copyArgument(argv[0], *outputTypes++, *it);
307
308 // skip this argument even if we didn't copy it
309 --outputTypesCount;
310 ++it;
311 }
312
313 for (int j = 0; j < outputTypesCount && it != args.constEnd(); ++i, ++j, ++it) {
314 copyArgument(argv[i], outputTypes[j], *it);
315 }
316 }
317
318 // done
319 lastError = reply;
320 return -1;
321 }
322 }
323 return id;
324}
325
326QT_END_NAMESPACE
327
328#endif // QT_NO_DBUS
329