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 
52 QT_BEGIN_NAMESPACE
53 
copyArgument(void * to,int id,const QVariant & arg)54 static void copyArgument(void *to, int id, const QVariant &arg)
55 {
56     if (id == arg.userType()) {
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()) {
108             *reinterpret_cast<QDBusVariant *>(to) = qvariant_cast<QDBusVariant>(arg);
109             return;
110         } else if (id == QDBusMetaTypeId::objectpath()) {
111             *reinterpret_cast<QDBusObjectPath *>(to) = qvariant_cast<QDBusObjectPath>(arg);
112             return;
113         } else if (id == QDBusMetaTypeId::signature()) {
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.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 = 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, id, to);
148 }
149 
QDBusInterfacePrivate(const QString & serv,const QString & p,const QString & iface,const QDBusConnection & con)150 QDBusInterfacePrivate::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 
~QDBusInterfacePrivate()169 QDBusInterfacePrivate::~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 */
QDBusInterface(const QString & service,const QString & path,const QString & interface,const QDBusConnection & connection,QObject * parent)218 QDBusInterface::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 */
~QDBusInterface()228 QDBusInterface::~QDBusInterface()
229 {
230     // resources are freed in QDBusInterfacePrivate::~QDBusInterfacePrivate()
231 }
232 
233 /*!
234     \internal
235     Overrides QObject::metaObject to return our own copy.
236 */
metaObject() const237 const 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 */
qt_metacast(const char * _clname)246 void *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 */
qt_metacall(QMetaObject::Call _c,int _id,void ** _a)260 int 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 
metacall(QMetaObject::Call c,int id,void ** argv)268 int 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(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 
327 QT_END_NAMESPACE
328 
329 #endif // QT_NO_DBUS
330