1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Copyright (C) 2016 Intel Corporation.
5 ** Contact: https://www.qt.io/licensing/
6 **
7 ** This file is part of the QtDBus module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** Commercial License Usage
11 ** Licensees holding valid commercial Qt licenses may use this file in
12 ** accordance with the commercial license agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and The Qt Company. For licensing terms
15 ** and conditions see https://www.qt.io/terms-conditions. For further
16 ** information use the contact form at https://www.qt.io/contact-us.
17 **
18 ** GNU Lesser General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU Lesser
20 ** General Public License version 3 as published by the Free Software
21 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
22 ** packaging of this file. Please review the following information to
23 ** ensure the GNU Lesser General Public License version 3 requirements
24 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25 **
26 ** GNU General Public License Usage
27 ** Alternatively, this file may be used under the terms of the GNU
28 ** General Public License version 2.0 or (at your option) the GNU General
29 ** Public license version 3 or any later version approved by the KDE Free
30 ** Qt Foundation. The licenses are as published by the Free Software
31 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32 ** included in the packaging of this file. Please review the following
33 ** information to ensure the GNU General Public License requirements will
34 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35 ** https://www.gnu.org/licenses/gpl-3.0.html.
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40 
41 #include "qdbusabstractadaptor.h"
42 #include "qdbusabstractadaptor_p.h"
43 
44 #include <QtCore/qcoreapplication.h>
45 #include <QtCore/qmetaobject.h>
46 #include <QtCore/qset.h>
47 #include <QtCore/qtimer.h>
48 #include <QtCore/qthread.h>
49 
50 #include "qdbusconnection.h"
51 
52 #include "qdbusconnection_p.h"  // for qDBusParametersForMethod
53 #include "qdbusmetatype_p.h"
54 
55 #include <algorithm>
56 
57 #ifndef QT_NO_DBUS
58 
59 QT_BEGIN_NAMESPACE
60 
61 static int cachedRelaySlotMethodIndex = -1;
62 
relaySlotMethodIndex()63 int QDBusAdaptorConnector::relaySlotMethodIndex()
64 {
65     if (cachedRelaySlotMethodIndex == -1) {
66         cachedRelaySlotMethodIndex = staticMetaObject.indexOfMethod("relaySlot()");
67         Q_ASSERT(cachedRelaySlotMethodIndex != -1);
68     }
69     return cachedRelaySlotMethodIndex;
70 }
71 
qDBusFindAdaptorConnector(QObject * obj)72 QDBusAdaptorConnector *qDBusFindAdaptorConnector(QObject *obj)
73 {
74     if (!obj)
75         return nullptr;
76     const QObjectList &children = obj->children();
77     QObjectList::ConstIterator it = children.constBegin();
78     QObjectList::ConstIterator end = children.constEnd();
79     for ( ; it != end; ++it) {
80         QDBusAdaptorConnector *connector = qobject_cast<QDBusAdaptorConnector *>(*it);
81         if (connector) {
82             connector->polish();
83             return connector;
84         }
85     }
86     return nullptr;
87 }
88 
qDBusFindAdaptorConnector(QDBusAbstractAdaptor * adaptor)89 QDBusAdaptorConnector *qDBusFindAdaptorConnector(QDBusAbstractAdaptor *adaptor)
90 {
91     return qDBusFindAdaptorConnector(adaptor->parent());
92 }
93 
qDBusCreateAdaptorConnector(QObject * obj)94 QDBusAdaptorConnector *qDBusCreateAdaptorConnector(QObject *obj)
95 {
96     QDBusAdaptorConnector *connector = qDBusFindAdaptorConnector(obj);
97     if (connector)
98         return connector;
99     return new QDBusAdaptorConnector(obj);
100 }
101 
retrieveIntrospectionXml(QDBusAbstractAdaptor * adaptor)102 QString QDBusAbstractAdaptorPrivate::retrieveIntrospectionXml(QDBusAbstractAdaptor *adaptor)
103 {
104     return adaptor->d_func()->xml;
105 }
106 
saveIntrospectionXml(QDBusAbstractAdaptor * adaptor,const QString & xml)107 void QDBusAbstractAdaptorPrivate::saveIntrospectionXml(QDBusAbstractAdaptor *adaptor,
108                                                        const QString &xml)
109 {
110     adaptor->d_func()->xml = xml;
111 }
112 
113 /*!
114     \class QDBusAbstractAdaptor
115     \inmodule QtDBus
116     \since 4.2
117 
118     \brief The QDBusAbstractAdaptor class is the base class of D-Bus adaptor classes.
119 
120     The QDBusAbstractAdaptor class is the starting point for all objects intending to provide
121     interfaces to the external world using D-Bus. This is accomplished by attaching a one or more
122     classes derived from QDBusAbstractAdaptor to a normal QObject and then registering that QObject
123     with QDBusConnection::registerObject. QDBusAbstractAdaptor objects are intended to be
124     light-weight wrappers, mostly just relaying calls into the real object (its parent) and the
125     signals from it.
126 
127     Each QDBusAbstractAdaptor-derived class should define the D-Bus interface it is implementing
128     using the Q_CLASSINFO macro in the class definition. Note that only one interface can be
129     exposed in this way.
130 
131     QDBusAbstractAdaptor uses the standard QObject mechanism of signals, slots and properties to
132     determine what signals, methods and properties to export to the bus. Any signal emitted by
133     QDBusAbstractAdaptor-derived classes will be automatically be relayed through any D-Bus
134     connections the object is registered on.
135 
136     Classes derived from QDBusAbstractAdaptor must be created on the heap using the \a new operator
137     and must not be deleted by the user (they will be deleted automatically when the object they are
138     connected to is also deleted).
139 
140     \sa {usingadaptors.html}{Using adaptors}, QDBusConnection
141 */
142 
143 /*!
144     Constructs a QDBusAbstractAdaptor with \a obj as the parent object.
145 */
QDBusAbstractAdaptor(QObject * obj)146 QDBusAbstractAdaptor::QDBusAbstractAdaptor(QObject* obj)
147     : QObject(*new QDBusAbstractAdaptorPrivate, obj)
148 {
149     QDBusAdaptorConnector *connector = qDBusCreateAdaptorConnector(obj);
150 
151     connector->waitingForPolish = true;
152     QMetaObject::invokeMethod(connector, "polish", Qt::QueuedConnection);
153 }
154 
155 /*!
156     Destroys the adaptor.
157 
158     \warning Adaptors are destroyed automatically when the real object they refer to is
159              destroyed. Do not delete the adaptors yourself.
160 */
~QDBusAbstractAdaptor()161 QDBusAbstractAdaptor::~QDBusAbstractAdaptor()
162 {
163 }
164 
165 /*!
166     Toggles automatic signal relaying from the real object (see object()).
167 
168     Automatic signal relaying consists of signal-to-signal connection of the signals on the parent
169     that have the exact same method signatue in both classes.
170 
171     If \a enable is set to true, connect the signals; if set to false, disconnect all signals.
172 */
setAutoRelaySignals(bool enable)173 void QDBusAbstractAdaptor::setAutoRelaySignals(bool enable)
174 {
175     const QMetaObject *us = metaObject();
176     const QMetaObject *them = parent()->metaObject();
177     bool connected = false;
178     for (int idx = staticMetaObject.methodCount(); idx < us->methodCount(); ++idx) {
179         QMetaMethod mm = us->method(idx);
180 
181         if (mm.methodType() != QMetaMethod::Signal)
182             continue;
183 
184         // try to connect/disconnect to a signal on the parent that has the same method signature
185         QByteArray sig = QMetaObject::normalizedSignature(mm.methodSignature().constData());
186         if (them->indexOfSignal(sig) == -1)
187             continue;
188         sig.prepend(QSIGNAL_CODE + '0');
189         parent()->disconnect(sig, this, sig);
190         if (enable)
191             connected = connect(parent(), sig, sig) || connected;
192     }
193     d_func()->autoRelaySignals = connected;
194 }
195 
196 /*!
197     Returns \c true if automatic signal relaying from the real object (see object()) is enabled,
198     otherwiser returns \c false.
199 
200     \sa setAutoRelaySignals()
201 */
autoRelaySignals() const202 bool QDBusAbstractAdaptor::autoRelaySignals() const
203 {
204     return d_func()->autoRelaySignals;
205 }
206 
QDBusAdaptorConnector(QObject * obj)207 QDBusAdaptorConnector::QDBusAdaptorConnector(QObject *obj)
208     : QObject(obj), waitingForPolish(false)
209 {
210 }
211 
~QDBusAdaptorConnector()212 QDBusAdaptorConnector::~QDBusAdaptorConnector()
213 {
214 }
215 
addAdaptor(QDBusAbstractAdaptor * adaptor)216 void QDBusAdaptorConnector::addAdaptor(QDBusAbstractAdaptor *adaptor)
217 {
218     // find the interface name
219     const QMetaObject *mo = adaptor->metaObject();
220     int ciid = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTERFACE);
221     if (ciid != -1) {
222         QMetaClassInfo mci = mo->classInfo(ciid);
223         if (*mci.value()) {
224             // find out if this interface exists first
225             const char *interface = mci.value();
226             AdaptorMap::Iterator it = std::lower_bound(adaptors.begin(), adaptors.end(),
227                                                        QByteArray(interface));
228             if (it != adaptors.end() && qstrcmp(interface, it->interface) == 0) {
229                 // exists. Replace it (though it's probably the same)
230                 if (it->adaptor != adaptor) {
231                     // reconnect the signals
232                     disconnectAllSignals(it->adaptor);
233                     connectAllSignals(adaptor);
234                 }
235                 it->adaptor = adaptor;
236             } else {
237                 // create a new one
238                 AdaptorData entry;
239                 entry.interface = interface;
240                 entry.adaptor = adaptor;
241                 adaptors << entry;
242 
243                 // connect the adaptor's signals to our relaySlot slot
244                 connectAllSignals(adaptor);
245             }
246         }
247     }
248 }
249 
disconnectAllSignals(QObject * obj)250 void QDBusAdaptorConnector::disconnectAllSignals(QObject *obj)
251 {
252     QMetaObject::disconnect(obj, -1, this, relaySlotMethodIndex());
253 }
254 
connectAllSignals(QObject * obj)255 void QDBusAdaptorConnector::connectAllSignals(QObject *obj)
256 {
257     QMetaObject::connect(obj, -1, this, relaySlotMethodIndex(), Qt::DirectConnection);
258 }
259 
polish()260 void QDBusAdaptorConnector::polish()
261 {
262     if (!waitingForPolish)
263         return;                 // avoid working multiple times if multiple adaptors were added
264 
265     waitingForPolish = false;
266     const QObjectList &objs = parent()->children();
267     QObjectList::ConstIterator it = objs.constBegin();
268     QObjectList::ConstIterator end = objs.constEnd();
269     for ( ; it != end; ++it) {
270         QDBusAbstractAdaptor *adaptor = qobject_cast<QDBusAbstractAdaptor *>(*it);
271         if (adaptor)
272             addAdaptor(adaptor);
273     }
274 
275     // sort the adaptor list
276     std::sort(adaptors.begin(), adaptors.end());
277 }
278 
relaySlot(void ** argv)279 void QDBusAdaptorConnector::relaySlot(void **argv)
280 {
281     QObject *sndr = sender();
282     if (Q_LIKELY(sndr)) {
283         relay(sndr, senderSignalIndex(), argv);
284     } else {
285         qWarning("QtDBus: cannot relay signals from parent %s(%p \"%s\") unless they are emitted in the object's thread %s(%p \"%s\"). "
286                  "Current thread is %s(%p \"%s\").",
287                  parent()->metaObject()->className(), parent(), qPrintable(parent()->objectName()),
288                  parent()->thread()->metaObject()->className(), parent()->thread(), qPrintable(parent()->thread()->objectName()),
289                  QThread::currentThread()->metaObject()->className(), QThread::currentThread(), qPrintable(QThread::currentThread()->objectName()));
290     }
291 }
292 
relay(QObject * senderObj,int lastSignalIdx,void ** argv)293 void QDBusAdaptorConnector::relay(QObject *senderObj, int lastSignalIdx, void **argv)
294 {
295     if (lastSignalIdx < QObject::staticMetaObject.methodCount())
296         // QObject signal (destroyed(QObject *)) -- ignore
297         return;
298 
299     const QMetaObject *senderMetaObject = senderObj->metaObject();
300     QMetaMethod mm = senderMetaObject->method(lastSignalIdx);
301 
302     QObject *realObject = senderObj;
303     if (qobject_cast<QDBusAbstractAdaptor *>(senderObj))
304         // it's an adaptor, so the real object is in fact its parent
305         realObject = realObject->parent();
306 
307     // break down the parameter list
308     QVector<int> types;
309     QString errorMsg;
310     int inputCount = qDBusParametersForMethod(mm, types, errorMsg);
311     if (inputCount == -1) {
312         // invalid signal signature
313         qWarning("QDBusAbstractAdaptor: Cannot relay signal %s::%s: %s",
314                  senderMetaObject->className(), mm.methodSignature().constData(),
315                  qPrintable(errorMsg));
316         return;
317     }
318     if (inputCount + 1 != types.count() ||
319         types.at(inputCount) == QDBusMetaTypeId::message()) {
320         // invalid signal signature
321         qWarning("QDBusAbstractAdaptor: Cannot relay signal %s::%s",
322                  senderMetaObject->className(), mm.methodSignature().constData());
323         return;
324     }
325 
326     QVariantList args;
327     const int numTypes = types.count();
328     args.reserve(numTypes - 1);
329     for (int i = 1; i < numTypes; ++i)
330         args << QVariant(types.at(i), argv[i]);
331 
332     // now emit the signal with all the information
333     emit relaySignal(realObject, senderMetaObject, lastSignalIdx, args);
334 }
335 
336 // our Meta Object
337 // modify carefully: this has been hand-edited!
338 // the relaySlot slot gets called with the void** array
339 
340 struct qt_meta_stringdata_QDBusAdaptorConnector_t {
341     QByteArrayData data[10];
342     char stringdata[96];
343 };
344 #define QT_MOC_LITERAL(idx, ofs, len) \
345     Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
346         offsetof(qt_meta_stringdata_QDBusAdaptorConnector_t, stringdata) + ofs \
347         - idx * sizeof(QByteArrayData) \
348     )
349 static const qt_meta_stringdata_QDBusAdaptorConnector_t qt_meta_stringdata_QDBusAdaptorConnector = {
350     {
351 QT_MOC_LITERAL(0, 0, 21),
352 QT_MOC_LITERAL(1, 22, 11),
353 QT_MOC_LITERAL(2, 34, 0),
354 QT_MOC_LITERAL(3, 35, 3),
355 QT_MOC_LITERAL(4, 39, 18),
356 QT_MOC_LITERAL(5, 58, 10),
357 QT_MOC_LITERAL(6, 69, 3),
358 QT_MOC_LITERAL(7, 73, 4),
359 QT_MOC_LITERAL(8, 78, 9),
360 QT_MOC_LITERAL(9, 88, 6)
361     },
362     "QDBusAdaptorConnector\0relaySignal\0\0"
363     "obj\0const QMetaObject*\0metaObject\0sid\0"
364     "args\0relaySlot\0polish\0"
365 };
366 #undef QT_MOC_LITERAL
367 
368 static const uint qt_meta_data_QDBusAdaptorConnector[] = {
369 
370  // content:
371        7,       // revision
372        0,       // classname
373        0,    0, // classinfo
374        3,   14, // methods
375        0,    0, // properties
376        0,    0, // enums/sets
377        0,    0, // constructors
378        0,       // flags
379        1,       // signalCount
380 
381  // signals: name, argc, parameters, tag, flags
382        1,    4,   29,    2, 0x05,
383 
384  // slots: name, argc, parameters, tag, flags
385        8,    0,   38,    2, 0x0a,
386        9,    0,   39,    2, 0x0a,
387 
388  // signals: parameters
389     QMetaType::Void, QMetaType::QObjectStar, 0x80000000 | 4, QMetaType::Int, QMetaType::QVariantList,    3,    5,    6,    7,
390 
391  // slots: parameters
392     QMetaType::Void,
393     QMetaType::Void,
394 
395        0        // eod
396 };
397 
qt_static_metacall(QObject * _o,QMetaObject::Call _c,int _id,void ** _a)398 void QDBusAdaptorConnector::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
399 {
400     if (_c == QMetaObject::InvokeMetaMethod) {
401         Q_ASSERT(staticMetaObject.cast(_o));
402         QDBusAdaptorConnector *_t = static_cast<QDBusAdaptorConnector *>(_o);
403         switch (_id) {
404         case 0: _t->relaySignal((*reinterpret_cast< QObject*(*)>(_a[1])),(*reinterpret_cast< const QMetaObject*(*)>(_a[2])),(*reinterpret_cast< int(*)>(_a[3])),(*reinterpret_cast< const QVariantList(*)>(_a[4]))); break;
405         case 1: _t->relaySlot(_a); break; // HAND EDIT: add the _a parameter
406         case 2: _t->polish(); break;
407         default: ;
408         }
409     }
410 }
411 
412 const QMetaObject QDBusAdaptorConnector::staticMetaObject = {
413     { &QObject::staticMetaObject, qt_meta_stringdata_QDBusAdaptorConnector.data,
414       qt_meta_data_QDBusAdaptorConnector, qt_static_metacall, nullptr, nullptr }
415 };
416 
metaObject() const417 const QMetaObject *QDBusAdaptorConnector::metaObject() const
418 {
419     return &staticMetaObject;
420 }
421 
qt_metacast(const char * _clname)422 void *QDBusAdaptorConnector::qt_metacast(const char *_clname)
423 {
424     if (!_clname) return nullptr;
425     if (!strcmp(_clname, qt_meta_stringdata_QDBusAdaptorConnector.stringdata))
426         return static_cast<void*>(const_cast< QDBusAdaptorConnector*>(this));
427     return QObject::qt_metacast(_clname);
428 }
429 
qt_metacall(QMetaObject::Call _c,int _id,void ** _a)430 int QDBusAdaptorConnector::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
431 {
432     _id = QObject::qt_metacall(_c, _id, _a);
433     if (_id < 0)
434         return _id;
435     if (_c == QMetaObject::InvokeMetaMethod) {
436         if (_id < 3)
437             qt_static_metacall(this, _c, _id, _a);
438         _id -= 3;
439     }
440     return _id;
441 }
442 
443 // SIGNAL 0
relaySignal(QObject * _t1,const QMetaObject * _t2,int _t3,const QVariantList & _t4)444 void QDBusAdaptorConnector::relaySignal(QObject * _t1, const QMetaObject * _t2, int _t3, const QVariantList & _t4)
445 {
446     void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)), const_cast<void*>(reinterpret_cast<const void*>(&_t2)), const_cast<void*>(reinterpret_cast<const void*>(&_t3)), const_cast<void*>(reinterpret_cast<const void*>(&_t4)) };
447     QMetaObject::activate(this, &staticMetaObject, 0, _a);
448 }
449 
450 QT_END_NAMESPACE
451 
452 #endif // QT_NO_DBUS
453