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 "qdbusintegrator_p.h"
42 
43 #include <qcoreapplication.h>
44 #include <qelapsedtimer.h>
45 #include <qdebug.h>
46 #include <qmetaobject.h>
47 #include <qobject.h>
48 #include <qsocketnotifier.h>
49 #include <qstringlist.h>
50 #include <qtimer.h>
51 #include <qthread.h>
52 #include <private/qlocking_p.h>
53 
54 #include "qdbusargument.h"
55 #include "qdbusconnection_p.h"
56 #include "qdbusconnectionmanager_p.h"
57 #include "qdbusinterface_p.h"
58 #include "qdbusmessage.h"
59 #include "qdbusmetatype.h"
60 #include "qdbusmetatype_p.h"
61 #include "qdbusabstractadaptor.h"
62 #include "qdbusabstractadaptor_p.h"
63 #include "qdbusserver.h"
64 #include "qdbusutil_p.h"
65 #include "qdbusvirtualobject.h"
66 #include "qdbusmessage_p.h"
67 #include "qdbuscontext_p.h"
68 #include "qdbuspendingcall_p.h"
69 
70 #include "qdbusthreaddebug_p.h"
71 
72 #include <algorithm>
73 #ifdef interface
74 #undef interface
75 #endif
76 
77 #ifndef QT_NO_DBUS
78 
79 QT_BEGIN_NAMESPACE
80 
81 // used with dbus_server_allocate_data_slot
82 static dbus_int32_t server_slot = -1;
83 
84 static QBasicAtomicInt isDebugging = Q_BASIC_ATOMIC_INITIALIZER(-1);
85 #define qDBusDebug              if (::isDebugging == 0); else qDebug
86 
operator <<(QDebug dbg,const QThread * th)87 static inline QDebug operator<<(QDebug dbg, const QThread *th)
88 {
89     QDebugStateSaver saver(dbg);
90     dbg.nospace() << "QThread(ptr=" << (const void*)th;
91     if (th && !th->objectName().isEmpty())
92         dbg.nospace() << ", name=" << th->objectName();
93     else if (th)
94         dbg.nospace() << ", name=" << th->metaObject()->className();
95     dbg.nospace() << ')';
96     return dbg;
97 }
98 
99 #if QDBUS_THREAD_DEBUG
operator <<(QDebug dbg,const QDBusConnectionPrivate * conn)100 static inline QDebug operator<<(QDebug dbg, const QDBusConnectionPrivate *conn)
101 {
102     QDebugStateSaver saver(dbg);
103     dbg.nospace() << "QDBusConnection("
104                   << "ptr=" << (const void*)conn
105                   << ", name=" << conn->name
106                   << ", baseService=" << conn->baseService
107                   << ')';
108     return dbg;
109 }
110 
qdbusDefaultThreadDebug(int action,int condition,QDBusConnectionPrivate * conn)111 void qdbusDefaultThreadDebug(int action, int condition, QDBusConnectionPrivate *conn)
112 {
113     qDBusDebug() << QThread::currentThread()
114                  << "Qt D-Bus threading action" << action
115                  << (condition == QDBusLockerBase::BeforeLock ? "before lock" :
116                      condition == QDBusLockerBase::AfterLock ? "after lock" :
117                      condition == QDBusLockerBase::BeforeUnlock ? "before unlock" :
118                      condition == QDBusLockerBase::AfterUnlock ? "after unlock" :
119                      condition == QDBusLockerBase::BeforePost ? "before event posting" :
120                      condition == QDBusLockerBase::AfterPost ? "after event posting" :
121                      condition == QDBusLockerBase::BeforeDeliver ? "before event delivery" :
122                      condition == QDBusLockerBase::AfterDeliver ? "after event delivery" :
123                      condition == QDBusLockerBase::BeforeAcquire ? "before acquire" :
124                      condition == QDBusLockerBase::AfterAcquire ? "after acquire" :
125                      condition == QDBusLockerBase::BeforeRelease ? "before release" :
126                      condition == QDBusLockerBase::AfterRelease ? "after release" :
127                      "condition unknown")
128                  << "in connection" << conn;
129 }
130 qdbusThreadDebugFunc qdbusThreadDebug = nullptr;
131 #endif
132 
133 typedef QVarLengthArray<QDBusSpyCallEvent::Hook, 4> QDBusSpyHookList;
Q_GLOBAL_STATIC(QDBusSpyHookList,qDBusSpyHookList)134 Q_GLOBAL_STATIC(QDBusSpyHookList, qDBusSpyHookList)
135 
136 extern "C" {
137 
138     // libdbus-1 callbacks
139 
140 static dbus_bool_t qDBusAddTimeout(DBusTimeout *timeout, void *data)
141 {
142     Q_ASSERT(timeout);
143     Q_ASSERT(data);
144 
145   //  qDebug("addTimeout %d", q_dbus_timeout_get_interval(timeout));
146 
147     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
148     Q_ASSERT(QThread::currentThread() == d->thread());
149 
150     // we may get called from qDBusToggleTimeout
151     if (Q_UNLIKELY(!q_dbus_timeout_get_enabled(timeout)))
152         return false;
153 
154     Q_ASSERT(d->timeouts.key(timeout, 0) == 0);
155 
156     int timerId = d->startTimer(q_dbus_timeout_get_interval(timeout));
157     Q_ASSERT_X(timerId, "QDBusConnection", "Failed to start a timer");
158     if (!timerId)
159         return false;
160 
161     d->timeouts[timerId] = timeout;
162     return true;
163 }
164 
165 static void qDBusRemoveTimeout(DBusTimeout *timeout, void *data)
166 {
167     Q_ASSERT(timeout);
168     Q_ASSERT(data);
169 
170   //  qDebug("removeTimeout");
171 
172     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
173     Q_ASSERT(QThread::currentThread() == d->thread());
174 
175     QDBusConnectionPrivate::TimeoutHash::iterator it = d->timeouts.begin();
176     while (it != d->timeouts.end()) {
177         if (it.value() == timeout) {
178             d->killTimer(it.key());
179             it = d->timeouts.erase(it);
180             break;
181         } else {
182             ++it;
183         }
184     }
185 }
186 
187 static void qDBusToggleTimeout(DBusTimeout *timeout, void *data)
188 {
189     Q_ASSERT(timeout);
190     Q_ASSERT(data);
191 
192     //qDebug("ToggleTimeout");
193 
194     qDBusRemoveTimeout(timeout, data);
195     qDBusAddTimeout(timeout, data);
196 }
197 
198 static dbus_bool_t qDBusAddWatch(DBusWatch *watch, void *data)
199 {
200     Q_ASSERT(watch);
201     Q_ASSERT(data);
202 
203     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
204     Q_ASSERT(QThread::currentThread() == d->thread());
205 
206     int flags = q_dbus_watch_get_flags(watch);
207     int fd = q_dbus_watch_get_unix_fd(watch);
208 
209     QDBusConnectionPrivate::Watcher watcher;
210 
211     if (flags & DBUS_WATCH_READABLE) {
212         //qDebug("addReadWatch %d", fd);
213         watcher.watch = watch;
214         watcher.read = new QSocketNotifier(fd, QSocketNotifier::Read, d);
215         watcher.read->setEnabled(q_dbus_watch_get_enabled(watch));
216         d->connect(watcher.read, &QSocketNotifier::activated, d, &QDBusConnectionPrivate::socketRead);
217     }
218     if (flags & DBUS_WATCH_WRITABLE) {
219         //qDebug("addWriteWatch %d", fd);
220         watcher.watch = watch;
221         watcher.write = new QSocketNotifier(fd, QSocketNotifier::Write, d);
222         watcher.write->setEnabled(q_dbus_watch_get_enabled(watch));
223         d->connect(watcher.write, &QSocketNotifier::activated, d, &QDBusConnectionPrivate::socketWrite);
224     }
225     d->watchers.insert(fd, watcher);
226 
227     return true;
228 }
229 
230 static void qDBusRemoveWatch(DBusWatch *watch, void *data)
231 {
232     Q_ASSERT(watch);
233     Q_ASSERT(data);
234 
235     //qDebug("remove watch");
236 
237     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
238     Q_ASSERT(QThread::currentThread() == d->thread());
239     int fd = q_dbus_watch_get_unix_fd(watch);
240 
241     QDBusConnectionPrivate::WatcherHash::iterator i = d->watchers.find(fd);
242     while (i != d->watchers.end() && i.key() == fd) {
243         if (i.value().watch == watch) {
244             delete i.value().read;
245             delete i.value().write;
246             i = d->watchers.erase(i);
247         } else {
248             ++i;
249         }
250     }
251 }
252 
253 static void qDBusToggleWatch(DBusWatch *watch, void *data)
254 {
255     Q_ASSERT(watch);
256     Q_ASSERT(data);
257 
258     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
259     Q_ASSERT(QThread::currentThread() == d->thread());
260     int fd = q_dbus_watch_get_unix_fd(watch);
261 
262     QDBusConnectionPrivate::WatcherHash::iterator i = d->watchers.find(fd);
263     while (i != d->watchers.end() && i.key() == fd) {
264         if (i.value().watch == watch) {
265             bool enabled = q_dbus_watch_get_enabled(watch);
266             int flags = q_dbus_watch_get_flags(watch);
267 
268             //qDebug("toggle watch %d to %d (write: %d, read: %d)", q_dbus_watch_get_fd(watch), enabled, flags & DBUS_WATCH_WRITABLE, flags & DBUS_WATCH_READABLE);
269 
270             if (flags & DBUS_WATCH_READABLE && i.value().read)
271                 i.value().read->setEnabled(enabled);
272             if (flags & DBUS_WATCH_WRITABLE && i.value().write)
273                 i.value().write->setEnabled(enabled);
274             return;
275         }
276         ++i;
277     }
278 }
279 
280 static void qDBusUpdateDispatchStatus(DBusConnection *connection, DBusDispatchStatus new_status, void *data)
281 {
282     Q_ASSERT(connection);
283     Q_UNUSED(connection);
284     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
285     if (new_status == DBUS_DISPATCH_DATA_REMAINS)
286         emit d->dispatchStatusChanged();
287 }
288 
289 static void qDBusNewConnection(DBusServer *server, DBusConnection *connection, void *data)
290 {
291     // ### We may want to separate the server from the QDBusConnectionPrivate
292     Q_ASSERT(server); Q_UNUSED(server);
293     Q_ASSERT(connection);
294     Q_ASSERT(data);
295 
296     if (!QDBusConnectionManager::instance())
297         return;
298 
299     // keep the connection alive
300     q_dbus_connection_ref(connection);
301     QDBusConnectionPrivate *serverConnection = static_cast<QDBusConnectionPrivate *>(data);
302 
303     // allow anonymous authentication
304     if (serverConnection->anonymousAuthenticationAllowed)
305         q_dbus_connection_set_allow_anonymous(connection, true);
306 
307     QDBusConnectionPrivate *newConnection = new QDBusConnectionPrivate(serverConnection->parent());
308     const auto locker = qt_scoped_lock(QDBusConnectionManager::instance()->mutex);
309     QDBusConnectionManager::instance()->setConnection(QLatin1String("QDBusServer-") + QString::number(reinterpret_cast<qulonglong>(newConnection), 16), newConnection);
310     serverConnection->serverConnectionNames << newConnection->name;
311 
312     // setPeer does the error handling for us
313     QDBusErrorInternal error;
314     newConnection->setPeer(connection, error);
315     newConnection->setDispatchEnabled(false);
316 
317     // this is a queued connection and will resume in the QDBusServer's thread
318     emit serverConnection->newServerConnection(newConnection);
319 
320     // we've disabled dispatching of events, so now we post an event to the
321     // QDBusServer's thread in order to enable it after the
322     // QDBusServer::newConnection() signal has been received by the
323     // application's code
324     newConnection->ref.ref();
325     QReadLocker serverLock(&serverConnection->lock);
326     QDBusConnectionDispatchEnabler *o = new QDBusConnectionDispatchEnabler(newConnection);
327     QTimer::singleShot(0, o, SLOT(execute()));
328     if (serverConnection->serverObject)
329         o->moveToThread(serverConnection->serverObject->thread());
330 }
331 
332 void QDBusConnectionPrivate::_q_newConnection(QDBusConnectionPrivate *newConnection)
333 {
334     Q_ASSERT(mode == ServerMode);
335     emit serverObject->newConnection(QDBusConnectionPrivate::q(newConnection));
336 }
337 
338 } // extern "C"
339 
buildMatchRule(const QString & service,const QString & objectPath,const QString & interface,const QString & member,const QDBusConnectionPrivate::ArgMatchRules & argMatch,const QString &)340 static QByteArray buildMatchRule(const QString &service,
341                                  const QString &objectPath, const QString &interface,
342                                  const QString &member, const QDBusConnectionPrivate::ArgMatchRules &argMatch, const QString & /*signature*/)
343 {
344     QString result;
345     result += QLatin1String("type='signal',");
346     const auto keyValue = QLatin1String("%1='%2',");
347 
348     if (!service.isEmpty())
349         result += keyValue.arg(QLatin1String("sender"), service);
350     if (!objectPath.isEmpty())
351         result += keyValue.arg(QLatin1String("path"), objectPath);
352     if (!interface.isEmpty())
353         result += keyValue.arg(QLatin1String("interface"), interface);
354     if (!member.isEmpty())
355         result += keyValue.arg(QLatin1String("member"), member);
356 
357     // add the argument string-matching now
358     if (!argMatch.args.isEmpty()) {
359         const QString keyValue = QLatin1String("arg%1='%2',");
360         for (int i = 0; i < argMatch.args.count(); ++i)
361             if (!argMatch.args.at(i).isNull())
362                 result += keyValue.arg(i).arg(argMatch.args.at(i));
363     }
364     if (!argMatch.arg0namespace.isEmpty()) {
365         result += QLatin1String("arg0namespace='%1',").arg(argMatch.arg0namespace);
366     }
367 
368     result.chop(1);             // remove ending comma
369     return result.toLatin1();
370 }
371 
findObject(const QDBusConnectionPrivate::ObjectTreeNode * root,const QString & fullpath,int & usedLength,QDBusConnectionPrivate::ObjectTreeNode & result)372 static bool findObject(const QDBusConnectionPrivate::ObjectTreeNode *root,
373                        const QString &fullpath, int &usedLength,
374                        QDBusConnectionPrivate::ObjectTreeNode &result)
375 {
376     if (!fullpath.compare(QLatin1String("/")) && root->obj) {
377         usedLength = 1;
378         result = *root;
379         return root;
380     }
381     int start = 0;
382     int length = fullpath.length();
383     if (fullpath.at(0) == QLatin1Char('/'))
384         start = 1;
385 
386     // walk the object tree
387     QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator node = root;
388     while (start < length && node) {
389         if (node->flags & QDBusConnection::ExportChildObjects)
390             break;
391         if ((node->flags & QDBusConnectionPrivate::VirtualObject) && (node->flags & QDBusConnection::SubPath))
392             break;
393         int end = fullpath.indexOf(QLatin1Char('/'), start);
394         end = (end == -1 ? length : end);
395         QStringRef pathComponent(&fullpath, start, end - start);
396 
397         QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator it =
398             std::lower_bound(node->children.constBegin(), node->children.constEnd(), pathComponent);
399         if (it != node->children.constEnd() && it->name == pathComponent)
400             // match
401             node = it;
402         else
403             node = nullptr;
404 
405         start = end + 1;
406     }
407 
408     // found our object
409     usedLength = (start > length ? length : start);
410     if (node) {
411         if (node->obj || !node->children.isEmpty())
412             result = *node;
413         else
414             // there really is no object here
415             // we're just looking at an unused space in the QVector
416             node = nullptr;
417     }
418     return node;
419 }
420 
findChildObject(const QDBusConnectionPrivate::ObjectTreeNode * root,const QString & fullpath,int start)421 static QObject *findChildObject(const QDBusConnectionPrivate::ObjectTreeNode *root,
422                                 const QString &fullpath, int start)
423 {
424     int length = fullpath.length();
425 
426     // any object in the tree can tell us to switch to its own object tree:
427     const QDBusConnectionPrivate::ObjectTreeNode *node = root;
428     if (node && node->flags & QDBusConnection::ExportChildObjects) {
429         QObject *obj = node->obj;
430 
431         while (obj) {
432             if (start >= length)
433                 // we're at the correct level
434                 return obj;
435 
436             int pos = fullpath.indexOf(QLatin1Char('/'), start);
437             pos = (pos == -1 ? length : pos);
438             QStringRef pathComponent(&fullpath, start, pos - start);
439 
440             const QObjectList children = obj->children();
441 
442             // find a child with the proper name
443             QObject *next = nullptr;
444             QObjectList::ConstIterator it = children.constBegin();
445             QObjectList::ConstIterator end = children.constEnd();
446             for ( ; it != end; ++it)
447                 if ((*it)->objectName() == pathComponent) {
448                     next = *it;
449                     break;
450                 }
451 
452             if (!next)
453                 break;
454 
455             obj = next;
456             start = pos + 1;
457         }
458     }
459 
460     // object not found
461     return nullptr;
462 }
463 
matchArgsForService(const QString & service,QDBusServiceWatcher::WatchMode mode)464 static QDBusConnectionPrivate::ArgMatchRules matchArgsForService(const QString &service, QDBusServiceWatcher::WatchMode mode)
465 {
466     QDBusConnectionPrivate::ArgMatchRules matchArgs;
467     if (service.endsWith(QLatin1Char('*'))) {
468         matchArgs.arg0namespace = service.chopped(1);
469         matchArgs.args << QString();
470     }
471     else
472         matchArgs.args << service;
473 
474     switch (mode) {
475     case QDBusServiceWatcher::WatchForOwnerChange:
476         break;
477 
478     case QDBusServiceWatcher::WatchForRegistration:
479         matchArgs.args << QString::fromLatin1("", 0);
480         break;
481 
482     case QDBusServiceWatcher::WatchForUnregistration:
483         matchArgs.args << QString() << QString::fromLatin1("", 0);
484         break;
485     }
486     return matchArgs;
487 }
488 
489 
490 extern Q_DBUS_EXPORT void qDBusAddSpyHook(QDBusSpyCallEvent::Hook);
qDBusAddSpyHook(QDBusSpyCallEvent::Hook hook)491 void qDBusAddSpyHook(QDBusSpyCallEvent::Hook hook)
492 {
493     qDBusSpyHookList()->append(hook);
494 }
495 
~QDBusSpyCallEvent()496 QDBusSpyCallEvent::~QDBusSpyCallEvent()
497 {
498     // Reinsert the message into the processing queue for the connection.
499     // This is done in the destructor so the message is reinserted even if
500     // QCoreApplication is destroyed.
501     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(const_cast<QObject *>(sender()));
502     qDBusDebug() << d << "message spies done for" << msg;
503     emit d->spyHooksFinished(msg);
504 }
505 
placeMetaCall(QObject *)506 void QDBusSpyCallEvent::placeMetaCall(QObject *)
507 {
508     invokeSpyHooks(msg, hooks, hookCount);
509 }
510 
invokeSpyHooks(const QDBusMessage & msg,const Hook * hooks,int hookCount)511 inline void QDBusSpyCallEvent::invokeSpyHooks(const QDBusMessage &msg, const Hook *hooks, int hookCount)
512 {
513     // call the spy hook list
514     for (int i = 0; i < hookCount; ++i)
515         hooks[i](msg);
516 }
517 
518 extern "C" {
519 static DBusHandlerResult
qDBusSignalFilter(DBusConnection * connection,DBusMessage * message,void * data)520 qDBusSignalFilter(DBusConnection *connection, DBusMessage *message, void *data)
521 {
522     Q_ASSERT(data);
523     Q_UNUSED(connection);
524     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
525     if (d->mode == QDBusConnectionPrivate::InvalidMode)
526         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
527 
528     QDBusMessage amsg = QDBusMessagePrivate::fromDBusMessage(message, d->connectionCapabilities());
529     qDBusDebug() << d << "got message (signal):" << amsg;
530 
531     return d->handleMessage(amsg) ?
532         DBUS_HANDLER_RESULT_HANDLED :
533         DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
534 }
535 }
536 
handleMessage(const QDBusMessage & amsg)537 bool QDBusConnectionPrivate::handleMessage(const QDBusMessage &amsg)
538 {
539     if (!ref.loadRelaxed())
540         return false;
541 
542     // local message are always delivered, regardless of filtering
543     // or whether the dispatcher is enabled
544     bool isLocal = QDBusMessagePrivate::isLocal(amsg);
545 
546     if (!dispatchEnabled && !isLocal) {
547         // queue messages only, we'll handle them later
548         qDBusDebug() << this << "delivery is suspended";
549         pendingMessages << amsg;
550         return amsg.type() == QDBusMessage::MethodCallMessage;
551     }
552 
553     switch (amsg.type()) {
554     case QDBusMessage::SignalMessage:
555         handleSignal(amsg);
556         // if there are any other filters in this DBusConnection,
557         // let them see the signal too
558         return false;
559     case QDBusMessage::MethodCallMessage:
560         // run it through the spy filters (if any) before the regular processing:
561         // a) if it's a local message, we're in the caller's thread, so invoke the filter directly
562         // b) if it's an external message, post to the main thread
563         if (Q_UNLIKELY(qDBusSpyHookList.exists()) && qApp) {
564             const QDBusSpyHookList &list = *qDBusSpyHookList;
565             if (isLocal) {
566                 Q_ASSERT(QThread::currentThread() != thread());
567                 qDBusDebug() << this << "invoking message spies directly";
568                 QDBusSpyCallEvent::invokeSpyHooks(amsg, list.constData(), list.size());
569             } else {
570                 qDBusDebug() << this << "invoking message spies via event";
571                 QCoreApplication::postEvent(qApp, new QDBusSpyCallEvent(this, QDBusConnection(this),
572                                                                         amsg, list.constData(), list.size()));
573 
574                 // we'll be called back, so return
575                 return true;
576             }
577         }
578 
579         handleObjectCall(amsg);
580         return true;
581     case QDBusMessage::ReplyMessage:
582     case QDBusMessage::ErrorMessage:
583     case QDBusMessage::InvalidMessage:
584         return false;           // we don't handle those here
585     }
586 
587     return false;
588 }
589 
huntAndDestroy(QObject * needle,QDBusConnectionPrivate::ObjectTreeNode & haystack)590 static void huntAndDestroy(QObject *needle, QDBusConnectionPrivate::ObjectTreeNode &haystack)
591 {
592     for (auto &node : haystack.children)
593         huntAndDestroy(needle, node);
594 
595     auto isInactive = [](QDBusConnectionPrivate::ObjectTreeNode &node) { return !node.isActive(); };
596 
597     haystack.children.erase(std::remove_if(haystack.children.begin(), haystack.children.end(),
598                                            isInactive),
599                             haystack.children.end());
600 
601     if (needle == haystack.obj) {
602         haystack.obj = nullptr;
603         haystack.flags = 0;
604     }
605 }
606 
huntAndUnregister(const QVector<QStringRef> & pathComponents,int i,QDBusConnection::UnregisterMode mode,QDBusConnectionPrivate::ObjectTreeNode * node)607 static void huntAndUnregister(const QVector<QStringRef> &pathComponents, int i, QDBusConnection::UnregisterMode mode,
608                               QDBusConnectionPrivate::ObjectTreeNode *node)
609 {
610     if (pathComponents.count() == i) {
611         // found it
612         node->obj = nullptr;
613         node->flags = 0;
614 
615         if (mode == QDBusConnection::UnregisterTree) {
616             // clear the sub-tree as well
617             node->children.clear();  // can't disconnect the objects because we really don't know if they can
618                             // be found somewhere else in the path too
619         }
620     } else {
621         // keep going
622         QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator end = node->children.end();
623         QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator it =
624             std::lower_bound(node->children.begin(), end, pathComponents.at(i));
625         if (it == end || it->name != pathComponents.at(i))
626             return;              // node not found
627 
628         huntAndUnregister(pathComponents, i + 1, mode, it);
629         if (!it->isActive())
630             node->children.erase(it);
631     }
632 }
633 
huntAndEmit(DBusConnection * connection,DBusMessage * msg,QObject * needle,const QDBusConnectionPrivate::ObjectTreeNode & haystack,bool isScriptable,bool isAdaptor,const QString & path=QString ())634 static void huntAndEmit(DBusConnection *connection, DBusMessage *msg,
635                         QObject *needle, const QDBusConnectionPrivate::ObjectTreeNode &haystack,
636                         bool isScriptable, bool isAdaptor, const QString &path = QString())
637 {
638     QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator it = haystack.children.constBegin();
639     QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator end = haystack.children.constEnd();
640     for ( ; it != end; ++it) {
641         if (it->isActive())
642             huntAndEmit(connection, msg, needle, *it, isScriptable, isAdaptor, path + QLatin1Char('/') + it->name);
643     }
644 
645     if (needle == haystack.obj) {
646         // is this a signal we should relay?
647         if (isAdaptor && (haystack.flags & QDBusConnection::ExportAdaptors) == 0)
648             return;             // no: it comes from an adaptor and we're not exporting adaptors
649         else if (!isAdaptor) {
650             int mask = isScriptable
651                        ? QDBusConnection::ExportScriptableSignals
652                        : QDBusConnection::ExportNonScriptableSignals;
653             if ((haystack.flags & mask) == 0)
654                 return;         // signal was not exported
655         }
656 
657         QByteArray p = path.toLatin1();
658         if (p.isEmpty())
659             p = "/";
660         qDBusDebug() << QThread::currentThread() << "emitting signal at" << p;
661         DBusMessage *msg2 = q_dbus_message_copy(msg);
662         q_dbus_message_set_path(msg2, p);
663         q_dbus_connection_send(connection, msg2, nullptr);
664         q_dbus_message_unref(msg2);
665     }
666 }
667 
findSlot(const QMetaObject * mo,const QByteArray & name,int flags,const QString & signature_,QVector<int> & metaTypes)668 static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags,
669                     const QString &signature_, QVector<int> &metaTypes)
670 {
671     QByteArray msgSignature = signature_.toLatin1();
672 
673     for (int idx = mo->methodCount() - 1 ; idx >= QObject::staticMetaObject.methodCount(); --idx) {
674         QMetaMethod mm = mo->method(idx);
675 
676         // check access:
677         if (mm.access() != QMetaMethod::Public)
678             continue;
679 
680         // check type:
681         if (mm.methodType() != QMetaMethod::Slot && mm.methodType() != QMetaMethod::Method)
682             continue;
683 
684         // check name:
685         if (mm.name() != name)
686             continue;
687 
688         int returnType = mm.returnType();
689         bool isAsync = qDBusCheckAsyncTag(mm.tag());
690         bool isScriptable = mm.attributes() & QMetaMethod::Scriptable;
691 
692         // consistency check:
693         if (isAsync && returnType != QMetaType::Void)
694             continue;
695 
696         QString errorMsg;
697         int inputCount = qDBusParametersForMethod(mm, metaTypes, errorMsg);
698         if (inputCount == -1)
699             continue;           // problem parsing
700 
701         metaTypes[0] = returnType;
702         bool hasMessage = false;
703         if (inputCount > 0 &&
704             metaTypes.at(inputCount) == QDBusMetaTypeId::message()) {
705             // "no input parameters" is allowed as long as the message meta type is there
706             hasMessage = true;
707             --inputCount;
708         }
709 
710         // try to match the parameters
711         int i;
712         QByteArray reconstructedSignature;
713         for (i = 1; i <= inputCount; ++i) {
714             const char *typeSignature = QDBusMetaType::typeToSignature( metaTypes.at(i) );
715             if (!typeSignature)
716                 break;          // invalid
717 
718             reconstructedSignature += typeSignature;
719             if (!msgSignature.startsWith(reconstructedSignature))
720                 break;
721         }
722 
723         if (reconstructedSignature != msgSignature)
724             continue;           // we didn't match them all
725 
726         if (hasMessage)
727             ++i;
728 
729         // make sure that the output parameters have signatures too
730         if (returnType != QMetaType::UnknownType && returnType != QMetaType::Void && QDBusMetaType::typeToSignature(returnType) == nullptr)
731             continue;
732 
733         bool ok = true;
734         for (int j = i; ok && j < metaTypes.count(); ++j)
735             if (QDBusMetaType::typeToSignature(metaTypes.at(i)) == nullptr)
736                 ok = false;
737         if (!ok)
738             continue;
739 
740         // consistency check:
741         if (isAsync && metaTypes.count() > i + 1)
742             continue;
743 
744         if (mm.methodType() == QMetaMethod::Slot) {
745             if (isScriptable && (flags & QDBusConnection::ExportScriptableSlots) == 0)
746                 continue;           // scriptable slots not exported
747             if (!isScriptable && (flags & QDBusConnection::ExportNonScriptableSlots) == 0)
748                 continue;           // non-scriptable slots not exported
749         } else {
750             if (isScriptable && (flags & QDBusConnection::ExportScriptableInvokables) == 0)
751                 continue;           // scriptable invokables not exported
752             if (!isScriptable && (flags & QDBusConnection::ExportNonScriptableInvokables) == 0)
753                 continue;           // non-scriptable invokables not exported
754         }
755 
756         // if we got here, this slot matched
757         return idx;
758     }
759 
760     // no slot matched
761     return -1;
762 }
763 
764 /*!
765     \internal
766     Enables or disables the delivery of incoming method calls and signals. If
767     \a enable is true, this will also cause any queued, pending messages to be
768     delivered.
769  */
setDispatchEnabled(bool enable)770 void QDBusConnectionPrivate::setDispatchEnabled(bool enable)
771 {
772     checkThread();
773     dispatchEnabled = enable;
774     if (enable)
775         emit dispatchStatusChanged();
776 }
777 
778 static QDBusCallDeliveryEvent * const DIRECT_DELIVERY = (QDBusCallDeliveryEvent *)1;
779 
prepareReply(QDBusConnectionPrivate * target,QObject * object,int idx,const QVector<int> & metaTypes,const QDBusMessage & msg)780 QDBusCallDeliveryEvent* QDBusConnectionPrivate::prepareReply(QDBusConnectionPrivate *target,
781                                                              QObject *object, int idx,
782                                                              const QVector<int> &metaTypes,
783                                                              const QDBusMessage &msg)
784 {
785     Q_ASSERT(object);
786     Q_UNUSED(object);
787 
788     int n = metaTypes.count() - 1;
789     if (metaTypes[n] == QDBusMetaTypeId::message())
790         --n;
791 
792     if (msg.arguments().count() < n)
793         return nullptr;               // too few arguments
794 
795     // check that types match
796     for (int i = 0; i < n; ++i)
797         if (metaTypes.at(i + 1) != msg.arguments().at(i).userType() &&
798             msg.arguments().at(i).userType() != qMetaTypeId<QDBusArgument>())
799             return nullptr;           // no match
800 
801     // we can deliver
802     // prepare for the call
803     if (target == object)
804         return DIRECT_DELIVERY;
805     return new QDBusCallDeliveryEvent(QDBusConnection(target), idx, target, msg, metaTypes);
806 }
807 
activateSignal(const QDBusConnectionPrivate::SignalHook & hook,const QDBusMessage & msg)808 void QDBusConnectionPrivate::activateSignal(const QDBusConnectionPrivate::SignalHook& hook,
809                                             const QDBusMessage &msg)
810 {
811     // This is called by QDBusConnectionPrivate::handleSignal to deliver a signal
812     // that was received from D-Bus
813     //
814     // Signals are delivered to slots if the parameters match
815     // Slots can have less parameters than there are on the message
816     // Slots can optionally have one final parameter that is a QDBusMessage
817     // Slots receive read-only copies of the message (i.e., pass by value or by const-ref)
818     QDBusCallDeliveryEvent *call = prepareReply(this, hook.obj, hook.midx, hook.params, msg);
819     if (call == DIRECT_DELIVERY) {
820         // short-circuit delivery
821         Q_ASSERT(this == hook.obj);
822         deliverCall(this, 0, msg, hook.params, hook.midx);
823         return;
824     }
825     if (call)
826         postEventToThread(ActivateSignalAction, hook.obj, call);
827 }
828 
activateCall(QObject * object,int flags,const QDBusMessage & msg)829 bool QDBusConnectionPrivate::activateCall(QObject* object, int flags, const QDBusMessage &msg)
830 {
831     // This is called by QDBusConnectionPrivate::handleObjectCall to place a call
832     // to a slot on the object.
833     //
834     // The call is delivered to the first slot that matches the following conditions:
835     //  - has the same name as the message's target member
836     //  - ALL of the message's types are found in slot's parameter list
837     //  - optionally has one more parameter of type QDBusMessage
838     // If none match, then the slot of the same name as the message target and with
839     // the first type of QDBusMessage is delivered.
840     //
841     // The D-Bus specification requires that all MethodCall messages be replied to, unless the
842     // caller specifically waived this requirement. This means that we inspect if the user slot
843     // generated a reply and, if it didn't, we will. Obviously, if the user slot doesn't take a
844     // QDBusMessage parameter, it cannot generate a reply.
845     //
846     // When a return message is generated, the slot's return type, if any, will be placed
847     // in the message's first position. If there are non-const reference parameters to the
848     // slot, they must appear at the end and will be placed in the subsequent message
849     // positions.
850 
851     static const char cachePropertyName[] = "_qdbus_slotCache";
852 
853     if (!object)
854         return false;
855 
856     Q_ASSERT_X(QThread::currentThread() == object->thread(),
857                "QDBusConnection: internal threading error",
858                "function called for an object that is in another thread!!");
859 
860     QDBusSlotCache slotCache =
861         qvariant_cast<QDBusSlotCache>(object->property(cachePropertyName));
862     QString cacheKey = msg.member(), signature = msg.signature();
863     if (!signature.isEmpty()) {
864         cacheKey.reserve(cacheKey.length() + 1 + signature.length());
865         cacheKey += QLatin1Char('.');
866         cacheKey += signature;
867     }
868 
869     QDBusSlotCache::Hash::ConstIterator cacheIt = slotCache.hash.constFind(cacheKey);
870     while (cacheIt != slotCache.hash.constEnd() && cacheIt->flags != flags &&
871            cacheIt.key() == cacheKey)
872         ++cacheIt;
873     if (cacheIt == slotCache.hash.constEnd() || cacheIt.key() != cacheKey)
874     {
875         // not cached, analyze the meta object
876         const QMetaObject *mo = object->metaObject();
877         QByteArray memberName = msg.member().toUtf8();
878 
879         // find a slot that matches according to the rules above
880         QDBusSlotCache::Data slotData;
881         slotData.flags = flags;
882         slotData.slotIdx = ::findSlot(mo, memberName, flags, msg.signature(), slotData.metaTypes);
883         if (slotData.slotIdx == -1) {
884             // ### this is where we want to add the connection as an arg too
885             // try with no parameters, but with a QDBusMessage
886             slotData.slotIdx = ::findSlot(mo, memberName, flags, QString(), slotData.metaTypes);
887             if (slotData.metaTypes.count() != 2 ||
888                 slotData.metaTypes.at(1) != QDBusMetaTypeId::message()) {
889                 // not found
890                 // save the negative lookup
891                 slotData.slotIdx = -1;
892                 slotData.metaTypes.clear();
893                 slotCache.hash.insert(cacheKey, slotData);
894                 object->setProperty(cachePropertyName, QVariant::fromValue(slotCache));
895                 return false;
896             }
897         }
898 
899         // save to the cache
900         slotCache.hash.insert(cacheKey, slotData);
901         object->setProperty(cachePropertyName, QVariant::fromValue(slotCache));
902 
903         // found the slot to be called
904         deliverCall(object, flags, msg, slotData.metaTypes, slotData.slotIdx);
905         return true;
906     } else if (cacheIt->slotIdx == -1) {
907         // negative cache
908         return false;
909     } else {
910         // use the cache
911         deliverCall(object, flags, msg, cacheIt->metaTypes, cacheIt->slotIdx);
912         return true;
913     }
914     return false;
915 }
916 
deliverCall(QObject * object,int,const QDBusMessage & msg,const QVector<int> & metaTypes,int slotIdx)917 void QDBusConnectionPrivate::deliverCall(QObject *object, int /*flags*/, const QDBusMessage &msg,
918                                          const QVector<int> &metaTypes, int slotIdx)
919 {
920     Q_ASSERT_X(!object || QThread::currentThread() == object->thread(),
921                "QDBusConnection: internal threading error",
922                "function called for an object that is in another thread!!");
923 
924     QVarLengthArray<void *, 10> params;
925     params.reserve(metaTypes.count());
926 
927     QVariantList auxParameters;
928     // let's create the parameter list
929 
930     // first one is the return type -- add it below
931     params.append(0);
932 
933     // add the input parameters
934     int i;
935     int pCount = qMin(msg.arguments().count(), metaTypes.count() - 1);
936     for (i = 1; i <= pCount; ++i) {
937         int id = metaTypes[i];
938         if (id == QDBusMetaTypeId::message())
939             break;
940 
941         const QVariant &arg = msg.arguments().at(i - 1);
942         if (arg.userType() == id)
943             // no conversion needed
944             params.append(const_cast<void *>(arg.constData()));
945         else if (arg.userType() == qMetaTypeId<QDBusArgument>()) {
946             // convert to what the function expects
947             void *null = nullptr;
948             auxParameters.append(QVariant(id, null));
949 
950             const QDBusArgument &in =
951                 *reinterpret_cast<const QDBusArgument *>(arg.constData());
952             QVariant &out = auxParameters[auxParameters.count() - 1];
953 
954             if (Q_UNLIKELY(!QDBusMetaType::demarshall(in, out.userType(), out.data())))
955                 qFatal("Internal error: demarshalling function for type '%s' (%d) failed!",
956                        out.typeName(), out.userType());
957 
958             params.append(const_cast<void *>(out.constData()));
959         } else {
960             qFatal("Internal error: got invalid meta type %d (%s) "
961                    "when trying to convert to meta type %d (%s)",
962                    arg.userType(), QMetaType::typeName(arg.userType()),
963                    id, QMetaType::typeName(id));
964         }
965     }
966 
967     if (metaTypes.count() > i && metaTypes[i] == QDBusMetaTypeId::message()) {
968         params.append(const_cast<void*>(static_cast<const void*>(&msg)));
969         ++i;
970     }
971 
972     // output arguments
973     const int numMetaTypes = metaTypes.count();
974     QVariantList outputArgs;
975     void *null = nullptr;
976     if (metaTypes[0] != QMetaType::Void && metaTypes[0] != QMetaType::UnknownType) {
977         outputArgs.reserve(numMetaTypes - i + 1);
978         QVariant arg(metaTypes[0], null);
979         outputArgs.append( arg );
980         params[0] = const_cast<void*>(outputArgs.at( outputArgs.count() - 1 ).constData());
981     } else {
982         outputArgs.reserve(numMetaTypes - i);
983     }
984 
985     for ( ; i < numMetaTypes; ++i) {
986         QVariant arg(metaTypes[i], null);
987         outputArgs.append( arg );
988         params.append(const_cast<void*>(outputArgs.at( outputArgs.count() - 1 ).constData()));
989     }
990 
991     // make call:
992     bool fail;
993     if (!object) {
994         fail = true;
995     } else {
996         // FIXME: save the old sender!
997         QDBusContextPrivate context(QDBusConnection(this), msg);
998         QDBusContextPrivate *old = QDBusContextPrivate::set(object, &context);
999 
1000         QPointer<QObject> ptr = object;
1001         fail = object->qt_metacall(QMetaObject::InvokeMetaMethod,
1002                                    slotIdx, params.data()) >= 0;
1003         // the object might be deleted in the slot
1004         if (!ptr.isNull())
1005             QDBusContextPrivate::set(object, old);
1006     }
1007 
1008     // do we create a reply? Only if the caller is waiting for a reply and one hasn't been sent
1009     // yet.
1010     if (msg.isReplyRequired() && !msg.isDelayedReply()) {
1011         if (!fail) {
1012             // normal reply
1013             qDBusDebug() << this << "Automatically sending reply:" << outputArgs;
1014             send(msg.createReply(outputArgs));
1015         } else {
1016             // generate internal error
1017             qWarning("Internal error: Failed to deliver message");
1018             send(msg.createErrorReply(QDBusError::InternalError,
1019                                       QLatin1String("Failed to deliver message")));
1020         }
1021     }
1022 
1023     return;
1024 }
1025 
1026 extern bool qDBusInitThreads();
1027 
QDBusConnectionPrivate(QObject * p)1028 QDBusConnectionPrivate::QDBusConnectionPrivate(QObject *p)
1029     : QObject(p),
1030       ref(1),
1031       mode(InvalidMode),
1032       busService(nullptr),
1033       connection(nullptr),
1034       rootNode(QString(QLatin1Char('/'))),
1035       anonymousAuthenticationAllowed(false),
1036       dispatchEnabled(true),
1037       isAuthenticated(false)
1038 {
1039     static const bool threads = q_dbus_threads_init_default();
1040     if (::isDebugging == -1)
1041        ::isDebugging = qEnvironmentVariableIntValue("QDBUS_DEBUG");
1042     Q_UNUSED(threads)
1043 
1044 #ifdef QDBUS_THREAD_DEBUG
1045     if (::isDebugging > 1)
1046         qdbusThreadDebug = qdbusDefaultThreadDebug;
1047 #endif
1048 
1049     QDBusMetaTypeId::init();
1050     connect(this, &QDBusConnectionPrivate::dispatchStatusChanged,
1051             this, &QDBusConnectionPrivate::doDispatch, Qt::QueuedConnection);
1052     connect(this, &QDBusConnectionPrivate::spyHooksFinished,
1053             this, &QDBusConnectionPrivate::handleObjectCall, Qt::QueuedConnection);
1054     connect(this, &QDBusConnectionPrivate::messageNeedsSending,
1055             this, &QDBusConnectionPrivate::sendInternal);
1056     connect(this, &QDBusConnectionPrivate::signalNeedsConnecting,
1057             this, &QDBusConnectionPrivate::addSignalHook, Qt::BlockingQueuedConnection);
1058     connect(this, &QDBusConnectionPrivate::signalNeedsDisconnecting,
1059             this, &QDBusConnectionPrivate::removeSignalHook, Qt::BlockingQueuedConnection);
1060 
1061     rootNode.flags = 0;
1062 
1063     // prepopulate watchedServices:
1064     // we know that the owner of org.freedesktop.DBus is itself
1065     watchedServices.insert(QDBusUtil::dbusService(), WatchedServiceData(QDBusUtil::dbusService(), 1));
1066 
1067     // prepopulate matchRefCounts:
1068     // we know that org.freedesktop.DBus will never change owners
1069     matchRefCounts.insert("type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='org.freedesktop.DBus'", 1);
1070 }
1071 
~QDBusConnectionPrivate()1072 QDBusConnectionPrivate::~QDBusConnectionPrivate()
1073 {
1074     if (thread() && thread() != QThread::currentThread())
1075         qWarning("QDBusConnection(name=\"%s\")'s last reference in not in its creation thread! "
1076                  "Timer and socket errors will follow and the program will probably crash",
1077                  qPrintable(name));
1078 
1079     auto lastMode = mode; // reset on connection close
1080     closeConnection();
1081     qDeleteAll(cachedMetaObjects);
1082 
1083     if (lastMode == ClientMode || lastMode == PeerMode) {
1084         // the bus service object holds a reference back to us;
1085         // we need to destroy it before we finish destroying ourselves
1086         Q_ASSERT(ref.loadRelaxed() == 0);
1087         QObject *obj = (QObject *)busService;
1088         if (obj) {
1089             disconnect(obj, nullptr, this, nullptr);
1090             delete obj;
1091         }
1092         if (connection)
1093             q_dbus_connection_unref(connection);
1094         connection = nullptr;
1095     } else if (lastMode == ServerMode) {
1096         if (server)
1097             q_dbus_server_unref(server);
1098         server = nullptr;
1099     }
1100 }
1101 
collectAllObjects(QDBusConnectionPrivate::ObjectTreeNode & haystack,QSet<QObject * > & set)1102 void QDBusConnectionPrivate::collectAllObjects(QDBusConnectionPrivate::ObjectTreeNode &haystack,
1103                                                QSet<QObject *> &set)
1104 {
1105     QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator it = haystack.children.begin();
1106 
1107     while (it != haystack.children.end()) {
1108         collectAllObjects(*it, set);
1109         it++;
1110     }
1111 
1112     if (haystack.obj)
1113         set.insert(haystack.obj);
1114 }
1115 
closeConnection()1116 void QDBusConnectionPrivate::closeConnection()
1117 {
1118     QDBusWriteLocker locker(CloseConnectionAction, this);
1119     qDBusDebug() << this << "Disconnected";
1120     ConnectionMode oldMode = mode;
1121     mode = InvalidMode; // prevent reentrancy
1122     baseService.clear();
1123 
1124     if (oldMode == ServerMode && server) {
1125         q_dbus_server_disconnect(server);
1126         q_dbus_server_free_data_slot(&server_slot);
1127     }
1128 
1129     if (oldMode == ClientMode || oldMode == PeerMode) {
1130         if (connection) {
1131             q_dbus_connection_close(connection);
1132             // send the "close" message
1133             while (q_dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS)
1134                 ;
1135         }
1136     }
1137 
1138     qDeleteAll(pendingCalls);
1139 
1140     // Disconnect all signals from signal hooks and from the object tree to
1141     // avoid QObject::destroyed being sent to dbus daemon thread which has
1142     // already quit. We need to make sure we disconnect exactly once per
1143     // object, because if we tried a second time, we might be hitting a
1144     // dangling pointer.
1145     QSet<QObject *> allObjects;
1146     collectAllObjects(rootNode, allObjects);
1147     SignalHookHash::const_iterator sit = signalHooks.constBegin();
1148     while (sit != signalHooks.constEnd()) {
1149         allObjects.insert(sit.value().obj);
1150         ++sit;
1151     }
1152 
1153     // now disconnect ourselves
1154     QSet<QObject *>::const_iterator oit = allObjects.constBegin();
1155     while (oit != allObjects.constEnd()) {
1156         (*oit)->disconnect(this);
1157         ++oit;
1158     }
1159 }
1160 
handleDBusDisconnection()1161 void QDBusConnectionPrivate::handleDBusDisconnection()
1162 {
1163     while (!pendingCalls.isEmpty())
1164         processFinishedCall(pendingCalls.first());
1165 }
1166 
checkThread()1167 void QDBusConnectionPrivate::checkThread()
1168 {
1169     Q_ASSERT(thread() == QDBusConnectionManager::instance());
1170     Q_ASSERT(QThread::currentThread() == thread());
1171 }
1172 
handleError(const QDBusErrorInternal & error)1173 bool QDBusConnectionPrivate::handleError(const QDBusErrorInternal &error)
1174 {
1175     if (!error)
1176         return false;           // no error
1177 
1178     //lock.lockForWrite();
1179     lastError = error;
1180     //lock.unlock();
1181     return true;
1182 }
1183 
timerEvent(QTimerEvent * e)1184 void QDBusConnectionPrivate::timerEvent(QTimerEvent *e)
1185 {
1186     {
1187         DBusTimeout *timeout = timeouts.value(e->timerId(), 0);
1188         if (timeout)
1189             q_dbus_timeout_handle(timeout);
1190     }
1191 
1192     doDispatch();
1193 }
1194 
doDispatch()1195 void QDBusConnectionPrivate::doDispatch()
1196 {
1197     if (mode == ClientMode || mode == PeerMode) {
1198         while (q_dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS) ;
1199         if (dispatchEnabled && !pendingMessages.isEmpty()) {
1200             // dispatch previously queued messages
1201             PendingMessageList::Iterator it = pendingMessages.begin();
1202             PendingMessageList::Iterator end = pendingMessages.end();
1203             for ( ; it != end; ++it) {
1204                 qDBusDebug() << this << "dequeueing message" << *it;
1205                 handleMessage(std::move(*it));
1206             }
1207             pendingMessages.clear();
1208         }
1209     }
1210 }
1211 
socketRead(qintptr fd)1212 void QDBusConnectionPrivate::socketRead(qintptr fd)
1213 {
1214     WatcherHash::ConstIterator it = watchers.constFind(fd);
1215     while (it != watchers.constEnd() && it.key() == fd) {
1216         if (it->watch && it->read && it->read->isEnabled()) {
1217             if (!q_dbus_watch_handle(it.value().watch, DBUS_WATCH_READABLE))
1218                 qDebug("OUT OF MEM");
1219             break;
1220         }
1221         ++it;
1222     }
1223     if ((mode == ClientMode || mode == PeerMode) && !isAuthenticated
1224         && q_dbus_connection_get_is_authenticated(connection))
1225         handleAuthentication();
1226     doDispatch();
1227 }
1228 
socketWrite(qintptr fd)1229 void QDBusConnectionPrivate::socketWrite(qintptr fd)
1230 {
1231     WatcherHash::ConstIterator it = watchers.constFind(fd);
1232     while (it != watchers.constEnd() && it.key() == fd) {
1233         if (it->watch && it->write && it->write->isEnabled()) {
1234             if (!q_dbus_watch_handle(it.value().watch, DBUS_WATCH_WRITABLE))
1235                 qDebug("OUT OF MEM");
1236             break;
1237         }
1238         ++it;
1239     }
1240     if ((mode == ClientMode || mode == PeerMode) && !isAuthenticated
1241         && q_dbus_connection_get_is_authenticated(connection))
1242         handleAuthentication();
1243 }
1244 
objectDestroyed(QObject * obj)1245 void QDBusConnectionPrivate::objectDestroyed(QObject *obj)
1246 {
1247     QDBusWriteLocker locker(ObjectDestroyedAction, this);
1248     huntAndDestroy(obj, rootNode);
1249 
1250     SignalHookHash::iterator sit = signalHooks.begin();
1251     while (sit != signalHooks.end()) {
1252         if (static_cast<QObject *>(sit.value().obj) == obj)
1253             sit = removeSignalHookNoLock(sit);
1254         else
1255             ++sit;
1256     }
1257 
1258     obj->disconnect(this);
1259 }
1260 
relaySignal(QObject * obj,const QMetaObject * mo,int signalId,const QVariantList & args)1261 void QDBusConnectionPrivate::relaySignal(QObject *obj, const QMetaObject *mo, int signalId,
1262                                          const QVariantList &args)
1263 {
1264     QString interface = qDBusInterfaceFromMetaObject(mo);
1265 
1266     QMetaMethod mm = mo->method(signalId);
1267     QByteArray memberName = mm.name();
1268 
1269     // check if it's scriptable
1270     bool isScriptable = mm.attributes() & QMetaMethod::Scriptable;
1271     bool isAdaptor = false;
1272     for ( ; mo; mo = mo->superClass())
1273         if (mo == &QDBusAbstractAdaptor::staticMetaObject) {
1274             isAdaptor = true;
1275             break;
1276         }
1277 
1278     checkThread();
1279     QDBusReadLocker locker(RelaySignalAction, this);
1280     QDBusMessage message = QDBusMessage::createSignal(QLatin1String("/"), interface,
1281                                                       QLatin1String(memberName));
1282     QDBusMessagePrivate::setParametersValidated(message, true);
1283     message.setArguments(args);
1284     QDBusError error;
1285     DBusMessage *msg =
1286             QDBusMessagePrivate::toDBusMessage(message, connectionCapabilities(), &error);
1287     if (!msg) {
1288         qWarning("QDBusConnection: Could not emit signal %s.%s: %s", qPrintable(interface), memberName.constData(),
1289                  qPrintable(error.message()));
1290         lastError = error;
1291         return;
1292     }
1293 
1294     //qDBusDebug() << "Emitting signal" << message;
1295     //qDBusDebug() << "for paths:";
1296     q_dbus_message_set_no_reply(msg, true); // the reply would not be delivered to anything
1297     huntAndEmit(connection, msg, obj, rootNode, isScriptable, isAdaptor);
1298     q_dbus_message_unref(msg);
1299 }
1300 
serviceOwnerChangedNoLock(const QString & name,const QString & oldOwner,const QString & newOwner)1301 void QDBusConnectionPrivate::serviceOwnerChangedNoLock(const QString &name,
1302                                                        const QString &oldOwner, const QString &newOwner)
1303 {
1304     Q_UNUSED(oldOwner);
1305 //    QDBusWriteLocker locker(UpdateSignalHookOwnerAction, this);
1306     WatchedServicesHash::Iterator it = watchedServices.find(name);
1307     if (it == watchedServices.end())
1308         return;
1309     if (oldOwner != it->owner)
1310         qWarning("QDBusConnection: name '%s' had owner '%s' but we thought it was '%s'",
1311                  qPrintable(name), qPrintable(oldOwner), qPrintable(it->owner));
1312 
1313     qDBusDebug() << this << "Updating name" << name << "from" << oldOwner << "to" << newOwner;
1314     it->owner = newOwner;
1315 }
1316 
findSlot(QObject * obj,const QByteArray & normalizedName,QVector<int> & params)1317 int QDBusConnectionPrivate::findSlot(QObject* obj, const QByteArray &normalizedName,
1318                                      QVector<int> &params)
1319 {
1320     int midx = obj->metaObject()->indexOfMethod(normalizedName);
1321     if (midx == -1)
1322         return -1;
1323 
1324     QString errorMsg;
1325     int inputCount = qDBusParametersForMethod(obj->metaObject()->method(midx), params, errorMsg);
1326     if ( inputCount == -1 || inputCount + 1 != params.count() )
1327         return -1;              // failed to parse or invalid arguments or output arguments
1328 
1329     return midx;
1330 }
1331 
prepareHook(QDBusConnectionPrivate::SignalHook & hook,QString & key,const QString & service,const QString & path,const QString & interface,const QString & name,const ArgMatchRules & argMatch,QObject * receiver,const char * signal,int minMIdx,bool buildSignature)1332 bool QDBusConnectionPrivate::prepareHook(QDBusConnectionPrivate::SignalHook &hook, QString &key,
1333                                          const QString &service,
1334                                          const QString &path, const QString &interface, const QString &name,
1335                                          const ArgMatchRules &argMatch,
1336                                          QObject *receiver, const char *signal, int minMIdx,
1337                                          bool buildSignature)
1338 {
1339     QByteArray normalizedName = signal + 1;
1340     hook.midx = findSlot(receiver, signal + 1, hook.params);
1341     if (hook.midx == -1) {
1342         normalizedName = QMetaObject::normalizedSignature(signal + 1);
1343         hook.midx = findSlot(receiver, normalizedName, hook.params);
1344     }
1345     if (hook.midx < minMIdx) {
1346         return false;
1347     }
1348 
1349     hook.service = service;
1350     hook.path = path;
1351     hook.obj = receiver;
1352     hook.argumentMatch = argMatch;
1353 
1354     // build the D-Bus signal name and signature
1355     // This should not happen for QDBusConnection::connect, use buildSignature here, since
1356     // QDBusConnection::connect passes false and everything else uses true
1357     QString mname = name;
1358     if (buildSignature && mname.isNull()) {
1359         normalizedName.truncate(normalizedName.indexOf('('));
1360         mname = QString::fromUtf8(normalizedName);
1361     }
1362     key = mname;
1363     key.reserve(interface.length() + 1 + mname.length());
1364     key += QLatin1Char(':');
1365     key += interface;
1366 
1367     if (buildSignature) {
1368         hook.signature.clear();
1369         for (int i = 1; i < hook.params.count(); ++i)
1370             if (hook.params.at(i) != QDBusMetaTypeId::message())
1371                 hook.signature += QLatin1String( QDBusMetaType::typeToSignature( hook.params.at(i) ) );
1372     }
1373 
1374     hook.matchRule = buildMatchRule(service, path, interface, mname, argMatch, hook.signature);
1375     return true;                // connect to this signal
1376 }
1377 
sendError(const QDBusMessage & msg,QDBusError::ErrorType code)1378 void QDBusConnectionPrivate::sendError(const QDBusMessage &msg, QDBusError::ErrorType code)
1379 {
1380     if (code == QDBusError::UnknownMethod) {
1381         QString interfaceMsg;
1382         if (msg.interface().isEmpty())
1383             interfaceMsg = QLatin1String("any interface");
1384         else
1385             interfaceMsg = QLatin1String("interface '%1'").arg(msg.interface());
1386 
1387         send(msg.createErrorReply(code,
1388                                   QLatin1String("No such method '%1' in %2 at object path '%3' "
1389                                                 "(signature '%4')")
1390                                   .arg(msg.member(), interfaceMsg, msg.path(), msg.signature())));
1391     } else if (code == QDBusError::UnknownInterface) {
1392         send(msg.createErrorReply(QDBusError::UnknownInterface,
1393                                   QLatin1String("No such interface '%1' at object path '%2'")
1394                                   .arg(msg.interface(), msg.path())));
1395     } else if (code == QDBusError::UnknownObject) {
1396         send(msg.createErrorReply(QDBusError::UnknownObject,
1397                                   QLatin1String("No such object path '%1'").arg(msg.path())));
1398     }
1399 }
1400 
activateInternalFilters(const ObjectTreeNode & node,const QDBusMessage & msg)1401 bool QDBusConnectionPrivate::activateInternalFilters(const ObjectTreeNode &node,
1402                                                      const QDBusMessage &msg)
1403 {
1404     // object may be null
1405     const QString interface = msg.interface();
1406 
1407     if (interface.isEmpty() || interface == QDBusUtil::dbusInterfaceIntrospectable()) {
1408         if (msg.member() == QLatin1String("Introspect") && msg.signature().isEmpty()) {
1409             //qDebug() << "QDBusConnectionPrivate::activateInternalFilters introspect" << msg.d_ptr->msg;
1410             QDBusMessage reply = msg.createReply(qDBusIntrospectObject(node, msg.path()));
1411             send(reply);
1412             return true;
1413         }
1414 
1415         if (!interface.isEmpty()) {
1416             sendError(msg, QDBusError::UnknownMethod);
1417             return true;
1418         }
1419     }
1420 
1421     if (node.obj && (interface.isEmpty() ||
1422                      interface == QDBusUtil::dbusInterfaceProperties())) {
1423         //qDebug() << "QDBusConnectionPrivate::activateInternalFilters properties" << msg.d_ptr->msg;
1424         if (msg.member() == QLatin1String("Get") && msg.signature() == QLatin1String("ss")) {
1425             QDBusMessage reply = qDBusPropertyGet(node, msg);
1426             send(reply);
1427             return true;
1428         } else if (msg.member() == QLatin1String("Set") && msg.signature() == QLatin1String("ssv")) {
1429             QDBusMessage reply = qDBusPropertySet(node, msg);
1430             send(reply);
1431             return true;
1432         } else if (msg.member() == QLatin1String("GetAll") && msg.signature() == QLatin1String("s")) {
1433             QDBusMessage reply = qDBusPropertyGetAll(node, msg);
1434             send(reply);
1435             return true;
1436         }
1437 
1438         if (!interface.isEmpty()) {
1439             sendError(msg, QDBusError::UnknownMethod);
1440             return true;
1441         }
1442     }
1443 
1444     return false;
1445 }
1446 
activateObject(ObjectTreeNode & node,const QDBusMessage & msg,int pathStartPos)1447 void QDBusConnectionPrivate::activateObject(ObjectTreeNode &node, const QDBusMessage &msg,
1448                                             int pathStartPos)
1449 {
1450     // This is called by QDBusConnectionPrivate::handleObjectCall to place a call to a slot
1451     // on the object.
1452     //
1453     // The call is routed through the adaptor sub-objects if we have any
1454 
1455     // object may be null
1456 
1457     if (node.flags & QDBusConnectionPrivate::VirtualObject) {
1458         if (node.treeNode->handleMessage(msg, q(this))) {
1459             return;
1460         } else {
1461             if (activateInternalFilters(node, msg))
1462                 return;
1463         }
1464     }
1465 
1466     if (pathStartPos != msg.path().length()) {
1467         node.flags &= ~QDBusConnection::ExportAllSignals;
1468         node.obj = findChildObject(&node, msg.path(), pathStartPos);
1469         if (!node.obj) {
1470             sendError(msg, QDBusError::UnknownObject);
1471             return;
1472         }
1473     }
1474 
1475     QDBusAdaptorConnector *connector;
1476     if (node.flags & QDBusConnection::ExportAdaptors &&
1477         (connector = qDBusFindAdaptorConnector(node.obj))) {
1478         int newflags = node.flags | QDBusConnection::ExportAllSlots;
1479 
1480         if (msg.interface().isEmpty()) {
1481             // place the call in all interfaces
1482             // let the first one that handles it to work
1483             QDBusAdaptorConnector::AdaptorMap::ConstIterator it =
1484                 connector->adaptors.constBegin();
1485             QDBusAdaptorConnector::AdaptorMap::ConstIterator end =
1486                 connector->adaptors.constEnd();
1487 
1488             for ( ; it != end; ++it)
1489                 if (activateCall(it->adaptor, newflags, msg))
1490                     return;
1491         } else {
1492             // check if we have an interface matching the name that was asked:
1493             QDBusAdaptorConnector::AdaptorMap::ConstIterator it;
1494             it = std::lower_bound(connector->adaptors.constBegin(), connector->adaptors.constEnd(),
1495                                   msg.interface());
1496             if (it != connector->adaptors.constEnd() && msg.interface() == QLatin1String(it->interface)) {
1497                 if (!activateCall(it->adaptor, newflags, msg))
1498                     sendError(msg, QDBusError::UnknownMethod);
1499                 return;
1500             }
1501         }
1502     }
1503 
1504     // no adaptors matched or were exported
1505     // try our standard filters
1506     if (activateInternalFilters(node, msg))
1507         return;                 // internal filters have already run or an error has been sent
1508 
1509     // try the object itself:
1510     if (node.flags & (QDBusConnection::ExportScriptableSlots|QDBusConnection::ExportNonScriptableSlots) ||
1511         node.flags & (QDBusConnection::ExportScriptableInvokables|QDBusConnection::ExportNonScriptableInvokables)) {
1512         bool interfaceFound = true;
1513         if (!msg.interface().isEmpty()) {
1514             if (!node.interfaceName.isEmpty())
1515                 interfaceFound = msg.interface() == node.interfaceName;
1516             else
1517                 interfaceFound = qDBusInterfaceInObject(node.obj, msg.interface());
1518         }
1519 
1520         if (interfaceFound) {
1521             if (!activateCall(node.obj, node.flags, msg))
1522                 sendError(msg, QDBusError::UnknownMethod);
1523             return;
1524         }
1525     }
1526 
1527     // nothing matched, send an error code
1528     if (msg.interface().isEmpty())
1529         sendError(msg, QDBusError::UnknownMethod);
1530     else
1531         sendError(msg, QDBusError::UnknownInterface);
1532 }
1533 
handleObjectCall(const QDBusMessage & msg)1534 void QDBusConnectionPrivate::handleObjectCall(const QDBusMessage &msg)
1535 {
1536     // if the msg is external, we were called from inside doDispatch
1537     // that means the dispatchLock mutex is locked
1538     // must not call out to user code in that case
1539     //
1540     // however, if the message is internal, handleMessage was called directly
1541     // (user's thread) and no lock is in place. We can therefore call out to
1542     // user code, if necessary.
1543     ObjectTreeNode result;
1544     int usedLength;
1545     QThread *objThread = nullptr;
1546     QSemaphore sem;
1547     bool semWait;
1548 
1549     {
1550         QDBusReadLocker locker(HandleObjectCallAction, this);
1551         if (!findObject(&rootNode, msg.path(), usedLength, result)) {
1552             // qDebug("Call failed: no object found at %s", qPrintable(msg.path()));
1553             sendError(msg, QDBusError::UnknownObject);
1554             return;
1555         }
1556 
1557         if (!result.obj) {
1558             // no object -> no threading issues
1559             // it's either going to be an error, or an internal filter
1560             activateObject(result, msg, usedLength);
1561             return;
1562         }
1563 
1564         objThread = result.obj->thread();
1565         if (!objThread) {
1566             send(msg.createErrorReply(QDBusError::InternalError,
1567                                       QLatin1String("Object '%1' (at path '%2')"
1568                                                     " has no thread. Cannot deliver message.")
1569                                       .arg(result.obj->objectName(), msg.path())));
1570             return;
1571         }
1572 
1573         if (!QDBusMessagePrivate::isLocal(msg)) {
1574             // external incoming message
1575             // post it and forget
1576             postEventToThread(HandleObjectCallPostEventAction, result.obj,
1577                               new QDBusActivateObjectEvent(QDBusConnection(this), this, result,
1578                                                            usedLength, msg));
1579             return;
1580         } else if (objThread != QThread::currentThread()) {
1581             // looped-back message, targeting another thread:
1582             // synchronize with it
1583             postEventToThread(HandleObjectCallPostEventAction, result.obj,
1584                               new QDBusActivateObjectEvent(QDBusConnection(this), this, result,
1585                                                            usedLength, msg, &sem));
1586             semWait = true;
1587         } else {
1588             // looped-back message, targeting current thread
1589             semWait = false;
1590         }
1591     } // release the lock
1592 
1593     if (semWait)
1594         SEM_ACQUIRE(HandleObjectCallSemaphoreAction, sem);
1595     else
1596         activateObject(result, msg, usedLength);
1597 }
1598 
~QDBusActivateObjectEvent()1599 QDBusActivateObjectEvent::~QDBusActivateObjectEvent()
1600 {
1601     if (!handled) {
1602         // we're being destroyed without delivering
1603         // it means the object was deleted between posting and delivering
1604         QDBusConnectionPrivate *that = QDBusConnectionPrivate::d(connection);
1605         that->sendError(message, QDBusError::UnknownObject);
1606     }
1607 
1608     // semaphore releasing happens in ~QMetaCallEvent
1609 }
1610 
placeMetaCall(QObject *)1611 void QDBusActivateObjectEvent::placeMetaCall(QObject *)
1612 {
1613     QDBusConnectionPrivate *that = QDBusConnectionPrivate::d(connection);
1614 
1615     QDBusLockerBase::reportThreadAction(HandleObjectCallPostEventAction,
1616                                         QDBusLockerBase::BeforeDeliver, that);
1617     that->activateObject(node, message, pathStartPos);
1618     QDBusLockerBase::reportThreadAction(HandleObjectCallPostEventAction,
1619                                         QDBusLockerBase::AfterDeliver, that);
1620 
1621     handled = true;
1622 }
1623 
handleSignal(const QString & key,const QDBusMessage & msg)1624 void QDBusConnectionPrivate::handleSignal(const QString &key, const QDBusMessage& msg)
1625 {
1626     SignalHookHash::const_iterator it = signalHooks.constFind(key);
1627     SignalHookHash::const_iterator end = signalHooks.constEnd();
1628     //qDebug("looking for: %s", path.toLocal8Bit().constData());
1629     //qDBusDebug() << signalHooks.keys();
1630     for ( ; it != end && it.key() == key; ++it) {
1631         const SignalHook &hook = it.value();
1632         if (!hook.service.isEmpty()) {
1633             QString owner = watchedServices.value(hook.service, WatchedServiceData(hook.service)).owner;
1634             if (owner != msg.service())
1635                 continue;
1636         }
1637         if (!hook.path.isEmpty() && hook.path != msg.path())
1638             continue;
1639         if (!hook.signature.isEmpty() && hook.signature != msg.signature())
1640             continue;
1641         if (hook.signature.isEmpty() && !hook.signature.isNull() && !msg.signature().isEmpty())
1642             continue;
1643         if (!hook.argumentMatch.args.isEmpty()) {
1644             const QVariantList arguments = msg.arguments();
1645             if (hook.argumentMatch.args.size() > arguments.size())
1646                 continue;
1647 
1648             bool matched = true;
1649             for (int i = 0; i < hook.argumentMatch.args.size(); ++i) {
1650                 const QString &param = hook.argumentMatch.args.at(i);
1651                 if (param.isNull())
1652                     continue;   // don't try to match against this
1653                 if (param == arguments.at(i).toString())
1654                     continue;   // matched
1655                 matched = false;
1656                 break;
1657             }
1658             if (!matched)
1659                 continue;
1660         }
1661         if (!hook.argumentMatch.arg0namespace.isEmpty()) {
1662             const QVariantList arguments = msg.arguments();
1663             if (arguments.size() < 1)
1664                 continue;
1665             const QString param = arguments.at(0).toString();
1666             if (param != hook.argumentMatch.arg0namespace
1667                 && !param.startsWith(hook.argumentMatch.arg0namespace + QLatin1Char('.')))
1668                 continue;
1669         }
1670         activateSignal(hook, msg);
1671     }
1672 }
1673 
handleSignal(const QDBusMessage & msg)1674 void QDBusConnectionPrivate::handleSignal(const QDBusMessage& msg)
1675 {
1676     // We call handlesignal(QString, QDBusMessage) three times:
1677     //  one with member:interface
1678     //  one with member:
1679     //  one with :interface
1680     // This allows us to match signals with wildcards on member or interface
1681     // (but not both)
1682 
1683     QString key = msg.member();
1684     key.reserve(key.length() + 1 + msg.interface().length());
1685     key += QLatin1Char(':');
1686     key += msg.interface();
1687 
1688     QDBusReadLocker locker(HandleSignalAction, this);
1689     handleSignal(key, msg);                  // one try
1690 
1691     key.truncate(msg.member().length() + 1); // keep the ':'
1692     handleSignal(key, msg);                  // second try
1693 
1694     key = QLatin1Char(':');
1695     key += msg.interface();
1696     handleSignal(key, msg);                  // third try
1697 }
1698 
watchForDBusDisconnection()1699 void QDBusConnectionPrivate::watchForDBusDisconnection()
1700 {
1701     SignalHook hook;
1702     // Initialize the hook for Disconnected signal
1703     hook.service.clear(); // org.freedesktop.DBus.Local.Disconnected uses empty service name
1704     hook.path = QDBusUtil::dbusPathLocal();
1705     hook.obj = this;
1706     hook.params << QMetaType::Void;
1707     hook.midx = staticMetaObject.indexOfSlot("handleDBusDisconnection()");
1708     Q_ASSERT(hook.midx != -1);
1709     signalHooks.insert(QLatin1String("Disconnected:" DBUS_INTERFACE_LOCAL), hook);
1710 }
1711 
setServer(QDBusServer * object,DBusServer * s,const QDBusErrorInternal & error)1712 void QDBusConnectionPrivate::setServer(QDBusServer *object, DBusServer *s, const QDBusErrorInternal &error)
1713 {
1714     mode = ServerMode;
1715     serverObject = object;
1716     object->d = this;
1717     if (!s) {
1718         handleError(error);
1719         return;
1720     }
1721 
1722     server = s;
1723 
1724     dbus_bool_t data_allocated = q_dbus_server_allocate_data_slot(&server_slot);
1725     if (data_allocated && server_slot < 0)
1726         return;
1727 
1728     dbus_bool_t watch_functions_set = q_dbus_server_set_watch_functions(server,
1729                                                                       qDBusAddWatch,
1730                                                                       qDBusRemoveWatch,
1731                                                                       qDBusToggleWatch,
1732                                                                       this, nullptr);
1733     //qDebug() << "watch_functions_set" << watch_functions_set;
1734     Q_UNUSED(watch_functions_set);
1735 
1736     dbus_bool_t time_functions_set = q_dbus_server_set_timeout_functions(server,
1737                                                                        qDBusAddTimeout,
1738                                                                        qDBusRemoveTimeout,
1739                                                                        qDBusToggleTimeout,
1740                                                                        this, nullptr);
1741     //qDebug() << "time_functions_set" << time_functions_set;
1742     Q_UNUSED(time_functions_set);
1743 
1744     q_dbus_server_set_new_connection_function(server, qDBusNewConnection, this, nullptr);
1745 
1746     dbus_bool_t data_set = q_dbus_server_set_data(server, server_slot, this, nullptr);
1747     //qDebug() << "data_set" << data_set;
1748     Q_UNUSED(data_set);
1749 }
1750 
setPeer(DBusConnection * c,const QDBusErrorInternal & error)1751 void QDBusConnectionPrivate::setPeer(DBusConnection *c, const QDBusErrorInternal &error)
1752 {
1753     mode = PeerMode;
1754     if (!c) {
1755         handleError(error);
1756         return;
1757     }
1758 
1759     connection = c;
1760 
1761     q_dbus_connection_set_exit_on_disconnect(connection, false);
1762     q_dbus_connection_set_watch_functions(connection,
1763                                         qDBusAddWatch,
1764                                         qDBusRemoveWatch,
1765                                         qDBusToggleWatch,
1766                                         this, nullptr);
1767     q_dbus_connection_set_timeout_functions(connection,
1768                                           qDBusAddTimeout,
1769                                           qDBusRemoveTimeout,
1770                                           qDBusToggleTimeout,
1771                                           this, nullptr);
1772     q_dbus_connection_set_dispatch_status_function(connection, qDBusUpdateDispatchStatus, this, nullptr);
1773     q_dbus_connection_add_filter(connection,
1774                                qDBusSignalFilter,
1775                                this, nullptr);
1776 
1777     watchForDBusDisconnection();
1778 
1779     QMetaObject::invokeMethod(this, "doDispatch", Qt::QueuedConnection);
1780 }
1781 
connectionCapabilies(DBusConnection * connection)1782 static QDBusConnection::ConnectionCapabilities connectionCapabilies(DBusConnection *connection)
1783 {
1784     QDBusConnection::ConnectionCapabilities result;
1785     typedef dbus_bool_t (*can_send_type_t)(DBusConnection *, int);
1786     static can_send_type_t can_send_type = nullptr;
1787 
1788 #if defined(QT_LINKED_LIBDBUS)
1789 # if DBUS_VERSION-0 >= 0x010400
1790     can_send_type = dbus_connection_can_send_type;
1791 # endif
1792 #elif QT_CONFIG(library)
1793     // run-time check if the next functions are available
1794     can_send_type = (can_send_type_t)qdbus_resolve_conditionally("dbus_connection_can_send_type");
1795 #endif
1796 
1797 #ifndef DBUS_TYPE_UNIX_FD
1798 # define DBUS_TYPE_UNIX_FD int('h')
1799 #endif
1800     if (can_send_type && can_send_type(connection, DBUS_TYPE_UNIX_FD))
1801         result |= QDBusConnection::UnixFileDescriptorPassing;
1802 
1803     return result;
1804 }
1805 
handleAuthentication()1806 void QDBusConnectionPrivate::handleAuthentication()
1807 {
1808     capabilities.storeRelaxed(connectionCapabilies(connection));
1809     isAuthenticated = true;
1810 }
1811 
setConnection(DBusConnection * dbc,const QDBusErrorInternal & error)1812 void QDBusConnectionPrivate::setConnection(DBusConnection *dbc, const QDBusErrorInternal &error)
1813 {
1814     mode = ClientMode;
1815     if (!dbc) {
1816         handleError(error);
1817         return;
1818     }
1819 
1820     connection = dbc;
1821 
1822     const char *service = q_dbus_bus_get_unique_name(connection);
1823     Q_ASSERT(service);
1824     baseService = QString::fromUtf8(service);
1825     // bus connections are already authenticated here because q_dbus_bus_register() has been called
1826     handleAuthentication();
1827 
1828     q_dbus_connection_set_exit_on_disconnect(connection, false);
1829     q_dbus_connection_set_watch_functions(connection, qDBusAddWatch, qDBusRemoveWatch,
1830                                           qDBusToggleWatch, this, nullptr);
1831     q_dbus_connection_set_timeout_functions(connection, qDBusAddTimeout, qDBusRemoveTimeout,
1832                                             qDBusToggleTimeout, this, nullptr);
1833     q_dbus_connection_set_dispatch_status_function(connection, qDBusUpdateDispatchStatus, this, nullptr);
1834     q_dbus_connection_add_filter(connection, qDBusSignalFilter, this, nullptr);
1835 
1836     // Initialize the hooks for the NameAcquired and NameLost signals
1837     // we don't use connectSignal here because we don't need the rules to be sent to the bus
1838     // the bus will always send us these two signals
1839     SignalHook hook;
1840     hook.service = QDBusUtil::dbusService();
1841     hook.path.clear(); // no matching
1842     hook.obj = this;
1843     hook.params << QMetaType::Void << QMetaType::QString; // both functions take a QString as parameter and return void
1844 
1845     hook.midx = staticMetaObject.indexOfSlot("registerServiceNoLock(QString)");
1846     Q_ASSERT(hook.midx != -1);
1847     signalHooks.insert(QLatin1String("NameAcquired:" DBUS_INTERFACE_DBUS), hook);
1848 
1849     hook.midx = staticMetaObject.indexOfSlot("unregisterServiceNoLock(QString)");
1850     Q_ASSERT(hook.midx != -1);
1851     signalHooks.insert(QLatin1String("NameLost:" DBUS_INTERFACE_DBUS), hook);
1852 
1853     // And initialize the hook for the NameOwnerChanged signal;
1854     // we don't use connectSignal here because the rules are added by connectSignal on a per-need basis
1855     hook.params.clear();
1856     hook.params.reserve(4);
1857     hook.params << QMetaType::Void << QMetaType::QString << QMetaType::QString << QMetaType::QString;
1858     hook.midx = staticMetaObject.indexOfSlot("serviceOwnerChangedNoLock(QString,QString,QString)");
1859     Q_ASSERT(hook.midx != -1);
1860     signalHooks.insert(QLatin1String("NameOwnerChanged:" DBUS_INTERFACE_DBUS), hook);
1861 
1862     watchForDBusDisconnection();
1863 
1864     qDBusDebug() << this << ": connected successfully";
1865 
1866     // schedule a dispatch:
1867     QMetaObject::invokeMethod(this, "doDispatch", Qt::QueuedConnection);
1868 }
1869 
1870 extern "C"{
qDBusResultReceived(DBusPendingCall * pending,void * user_data)1871 static void qDBusResultReceived(DBusPendingCall *pending, void *user_data)
1872 {
1873     QDBusPendingCallPrivate *call = reinterpret_cast<QDBusPendingCallPrivate *>(user_data);
1874     Q_ASSERT(call->pending == pending);
1875     Q_UNUSED(pending);
1876     QDBusConnectionPrivate::processFinishedCall(call);
1877 }
1878 }
1879 
processFinishedCall(QDBusPendingCallPrivate * call)1880 void QDBusConnectionPrivate::processFinishedCall(QDBusPendingCallPrivate *call)
1881 {
1882     QDBusConnectionPrivate *connection = const_cast<QDBusConnectionPrivate *>(call->connection);
1883 
1884     auto locker = qt_unique_lock(call->mutex);
1885 
1886     connection->pendingCalls.removeOne(call);
1887 
1888     QDBusMessage &msg = call->replyMessage;
1889     if (call->pending) {
1890         // when processFinishedCall is called and pending call is not completed,
1891         // it means we received disconnected signal from libdbus
1892         if (q_dbus_pending_call_get_completed(call->pending)) {
1893             // decode the message
1894             DBusMessage *reply = q_dbus_pending_call_steal_reply(call->pending);
1895             msg = QDBusMessagePrivate::fromDBusMessage(reply, connection->connectionCapabilities());
1896             q_dbus_message_unref(reply);
1897         } else {
1898             msg = QDBusMessage::createError(QDBusError::Disconnected, QDBusUtil::disconnectedErrorMessage());
1899         }
1900     }
1901     qDBusDebug() << connection << "got message reply:" << msg;
1902 
1903     // Check if the reply has the expected signature
1904     call->checkReceivedSignature();
1905 
1906     if (!call->receiver.isNull() && call->methodIdx != -1 && msg.type() == QDBusMessage::ReplyMessage) {
1907         // Deliver the return values of a remote function call.
1908         //
1909         // There is only one connection and it is specified by idx
1910         // The slot must have the same parameter types that the message does
1911         // The slot may have less parameters than the message
1912         // The slot may optionally have one final parameter that is QDBusMessage
1913         // The slot receives read-only copies of the message (i.e., pass by value or by const-ref)
1914 
1915         QDBusCallDeliveryEvent *e = prepareReply(connection, call->receiver, call->methodIdx,
1916                                                  call->metaTypes, msg);
1917         if (e)
1918             connection->postEventToThread(MessageResultReceivedAction, call->receiver, e);
1919         else
1920             qDBusDebug("Deliver failed!");
1921     }
1922 
1923     if (call->pending) {
1924         q_dbus_pending_call_unref(call->pending);
1925         call->pending = nullptr;
1926     }
1927 
1928     // Are there any watchers?
1929     if (call->watcherHelper)
1930         call->watcherHelper->emitSignals(msg, call->sentMessage);
1931 
1932     call->waitForFinishedCondition.wakeAll();
1933     locker.unlock();
1934 
1935     if (msg.type() == QDBusMessage::ErrorMessage)
1936         emit connection->callWithCallbackFailed(QDBusError(msg), call->sentMessage);
1937 
1938     if (!call->ref.deref())
1939         delete call;
1940 }
1941 
send(const QDBusMessage & message)1942 bool QDBusConnectionPrivate::send(const QDBusMessage& message)
1943 {
1944     if (QDBusMessagePrivate::isLocal(message))
1945         return true;            // don't send; the reply will be retrieved by the caller
1946                                 // through the d_ptr->localReply link
1947 
1948     QDBusError error;
1949     DBusMessage *msg =
1950             QDBusMessagePrivate::toDBusMessage(message, connectionCapabilities(), &error);
1951     if (!msg) {
1952         if (message.type() == QDBusMessage::MethodCallMessage)
1953             qWarning("QDBusConnection: error: could not send message to service \"%s\" path \"%s\" interface \"%s\" member \"%s\": %s",
1954                      qPrintable(message.service()), qPrintable(message.path()),
1955                      qPrintable(message.interface()), qPrintable(message.member()),
1956                      qPrintable(error.message()));
1957         else if (message.type() == QDBusMessage::SignalMessage)
1958             qWarning("QDBusConnection: error: could not send signal to service \"%s\" path \"%s\" interface \"%s\" member \"%s\": %s",
1959                      qPrintable(message.service()),
1960                      qPrintable(message.path()), qPrintable(message.interface()),
1961                      qPrintable(message.member()),
1962                      qPrintable(error.message()));
1963         else
1964             qWarning("QDBusConnection: error: could not send %s message to service \"%s\": %s",
1965                      message.type() == QDBusMessage::ReplyMessage ? "reply" :
1966                      message.type() == QDBusMessage::ErrorMessage ? "error" :
1967                      "invalid", qPrintable(message.service()),
1968                      qPrintable(error.message()));
1969         lastError = error;
1970         return false;
1971     }
1972 
1973     q_dbus_message_set_no_reply(msg, true); // the reply would not be delivered to anything
1974     qDBusDebug() << this << "sending message (no reply):" << message;
1975     emit messageNeedsSending(nullptr, msg);
1976     return true;
1977 }
1978 
1979 // small helper to note long running blocking dbus calls.
1980 // these are generally a sign of fragile software (too long a call can either
1981 // lead to bad user experience, if it's running on the GUI thread for instance)
1982 // or break completely under load (hitting the call timeout).
1983 //
1984 // as a result, this is something we want to watch for.
1985 class QDBusBlockingCallWatcher
1986 {
1987 public:
QDBusBlockingCallWatcher(const QDBusMessage & message)1988     QDBusBlockingCallWatcher(const QDBusMessage &message)
1989         : m_message(message), m_maxCallTimeoutMs(0)
1990     {
1991 #if defined(QT_NO_DEBUG)
1992         // when in a release build, we default these to off.
1993         // this means that we only affect code that explicitly enables the warning.
1994         static int mainThreadWarningAmount = -1;
1995         static int otherThreadWarningAmount = -1;
1996 #else
1997         static int mainThreadWarningAmount = 200;
1998         static int otherThreadWarningAmount = 500;
1999 #endif
2000         static bool initializedAmounts = false;
2001         static QBasicMutex initializeMutex;
2002         auto locker = qt_unique_lock(initializeMutex);
2003 
2004         if (!initializedAmounts) {
2005             int tmp = 0;
2006             QByteArray env;
2007             bool ok = true;
2008 
2009             env = qgetenv("Q_DBUS_BLOCKING_CALL_MAIN_THREAD_WARNING_MS");
2010             if (!env.isEmpty()) {
2011                 tmp = env.toInt(&ok);
2012                 if (ok)
2013                     mainThreadWarningAmount = tmp;
2014                 else
2015                     qWarning("QDBusBlockingCallWatcher: Q_DBUS_BLOCKING_CALL_MAIN_THREAD_WARNING_MS must be an integer; value ignored");
2016             }
2017 
2018             env = qgetenv("Q_DBUS_BLOCKING_CALL_OTHER_THREAD_WARNING_MS");
2019             if (!env.isEmpty()) {
2020                 tmp = env.toInt(&ok);
2021                 if (ok)
2022                     otherThreadWarningAmount = tmp;
2023                 else
2024                     qWarning("QDBusBlockingCallWatcher: Q_DBUS_BLOCKING_CALL_OTHER_THREAD_WARNING_MS must be an integer; value ignored");
2025             }
2026 
2027             initializedAmounts = true;
2028         }
2029 
2030         locker.unlock();
2031 
2032         // if this call is running on the main thread, we have a much lower
2033         // tolerance for delay because any long-term delay will wreck user
2034         // interactivity.
2035         if (qApp && qApp->thread() == QThread::currentThread())
2036             m_maxCallTimeoutMs = mainThreadWarningAmount;
2037         else
2038             m_maxCallTimeoutMs = otherThreadWarningAmount;
2039 
2040         m_callTimer.start();
2041     }
2042 
~QDBusBlockingCallWatcher()2043     ~QDBusBlockingCallWatcher()
2044     {
2045         if (m_maxCallTimeoutMs < 0)
2046             return; // disabled
2047 
2048         if (m_callTimer.elapsed() >= m_maxCallTimeoutMs) {
2049             qWarning("QDBusConnection: warning: blocking call took a long time (%d ms, max for this thread is %d ms) to service \"%s\" path \"%s\" interface \"%s\" member \"%s\"",
2050                      int(m_callTimer.elapsed()), m_maxCallTimeoutMs,
2051                      qPrintable(m_message.service()), qPrintable(m_message.path()),
2052                      qPrintable(m_message.interface()), qPrintable(m_message.member()));
2053         }
2054     }
2055 
2056 private:
2057     QDBusMessage m_message;
2058     int m_maxCallTimeoutMs;
2059     QElapsedTimer m_callTimer;
2060 };
2061 
2062 
sendWithReply(const QDBusMessage & message,int sendMode,int timeout)2063 QDBusMessage QDBusConnectionPrivate::sendWithReply(const QDBusMessage &message,
2064                                                    int sendMode, int timeout)
2065 {
2066     QDBusBlockingCallWatcher watcher(message);
2067 
2068     QDBusPendingCallPrivate *pcall = sendWithReplyAsync(message, nullptr, nullptr, nullptr, timeout);
2069     Q_ASSERT(pcall);
2070 
2071     if (pcall->replyMessage.type() == QDBusMessage::InvalidMessage) {
2072         // need to wait for the reply
2073         if (sendMode == QDBus::BlockWithGui) {
2074             pcall->watcherHelper = new QDBusPendingCallWatcherHelper;
2075             QEventLoop loop;
2076             loop.connect(pcall->watcherHelper, &QDBusPendingCallWatcherHelper::reply, &loop, &QEventLoop::quit);
2077             loop.connect(pcall->watcherHelper, &QDBusPendingCallWatcherHelper::error, &loop, &QEventLoop::quit);
2078 
2079             // enter the event loop and wait for a reply
2080             loop.exec(QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents);
2081         } else {
2082             pcall->waitForFinished();
2083         }
2084     }
2085 
2086     QDBusMessage reply = pcall->replyMessage;
2087     lastError = QDBusError(reply);      // set or clear error
2088 
2089     if (!pcall->ref.deref())
2090         delete pcall;
2091     return reply;
2092 }
2093 
sendWithReplyLocal(const QDBusMessage & message)2094 QDBusMessage QDBusConnectionPrivate::sendWithReplyLocal(const QDBusMessage &message)
2095 {
2096     qDBusDebug() << this << "sending message via local-loop:" << message;
2097 
2098     QDBusMessage localCallMsg = QDBusMessagePrivate::makeLocal(*this, message);
2099     bool handled = handleMessage(localCallMsg);
2100 
2101     if (!handled) {
2102         QString interface = message.interface();
2103         if (interface.isEmpty())
2104             interface = QLatin1String("<no-interface>");
2105         return QDBusMessage::createError(QDBusError::InternalError,
2106                                          QLatin1String("Internal error trying to call %1.%2 at %3 (signature '%4'")
2107                                          .arg(interface, message.member(),
2108                                               message.path(), message.signature()));
2109     }
2110 
2111     // if the message was handled, there might be a reply
2112     QDBusMessage localReplyMsg = QDBusMessagePrivate::makeLocalReply(*this, localCallMsg);
2113     if (localReplyMsg.type() == QDBusMessage::InvalidMessage) {
2114         qWarning("QDBusConnection: cannot call local method '%s' at object %s (with signature '%s') "
2115                  "on blocking mode", qPrintable(message.member()), qPrintable(message.path()),
2116                  qPrintable(message.signature()));
2117         return QDBusMessage::createError(
2118             QDBusError(QDBusError::InternalError,
2119                        QLatin1String("local-loop message cannot have delayed replies")));
2120     }
2121 
2122     // there is a reply
2123     qDBusDebug() << this << "got message via local-loop:" << localReplyMsg;
2124     return localReplyMsg;
2125 }
2126 
sendWithReplyAsync(const QDBusMessage & message,QObject * receiver,const char * returnMethod,const char * errorMethod,int timeout)2127 QDBusPendingCallPrivate *QDBusConnectionPrivate::sendWithReplyAsync(const QDBusMessage &message,
2128                                                                     QObject *receiver, const char *returnMethod,
2129                                                                     const char *errorMethod, int timeout)
2130 {
2131     QDBusPendingCallPrivate *pcall = new QDBusPendingCallPrivate(message, this);
2132     bool isLoopback;
2133     if ((isLoopback = isServiceRegisteredByThread(message.service()))) {
2134         // special case for local calls
2135         pcall->replyMessage = sendWithReplyLocal(message);
2136     }
2137 
2138     if (receiver && returnMethod)
2139         pcall->setReplyCallback(receiver, returnMethod);
2140 
2141     if (errorMethod) {
2142         pcall->watcherHelper = new QDBusPendingCallWatcherHelper;
2143         connect(pcall->watcherHelper, SIGNAL(error(QDBusError,QDBusMessage)), receiver, errorMethod,
2144                 Qt::QueuedConnection);
2145         pcall->watcherHelper->moveToThread(thread());
2146     }
2147 
2148     if ((receiver && returnMethod) || errorMethod) {
2149        // no one waiting, will delete pcall in processFinishedCall()
2150        pcall->ref.storeRelaxed(1);
2151     } else {
2152        // set double ref to prevent race between processFinishedCall() and ref counting
2153        // by QDBusPendingCall::QExplicitlySharedDataPointer<QDBusPendingCallPrivate>
2154        pcall->ref.storeRelaxed(2);
2155     }
2156 
2157     if (isLoopback) {
2158         // a loopback call
2159         processFinishedCall(pcall);
2160         return pcall;
2161     }
2162 
2163     QDBusError error;
2164     DBusMessage *msg =
2165             QDBusMessagePrivate::toDBusMessage(message, connectionCapabilities(), &error);
2166     if (!msg) {
2167         qWarning("QDBusConnection: error: could not send message to service \"%s\" path \"%s\" interface \"%s\" member \"%s\": %s",
2168                  qPrintable(message.service()), qPrintable(message.path()),
2169                  qPrintable(message.interface()), qPrintable(message.member()),
2170                  qPrintable(error.message()));
2171         pcall->replyMessage = QDBusMessage::createError(error);
2172         lastError = error;
2173         processFinishedCall(pcall);
2174     } else {
2175         qDBusDebug() << this << "sending message:" << message;
2176         emit messageNeedsSending(pcall, msg, timeout);
2177     }
2178     return pcall;
2179 }
2180 
sendInternal(QDBusPendingCallPrivate * pcall,void * message,int timeout)2181 void QDBusConnectionPrivate::sendInternal(QDBusPendingCallPrivate *pcall, void *message, int timeout)
2182 {
2183     QDBusError error;
2184     DBusPendingCall *pending = nullptr;
2185     DBusMessage *msg = static_cast<DBusMessage *>(message);
2186     bool isNoReply = !pcall;
2187     Q_ASSERT(isNoReply == !!q_dbus_message_get_no_reply(msg));
2188 
2189     checkThread();
2190 
2191     if (isNoReply && q_dbus_connection_send(connection, msg, nullptr)) {
2192         // success
2193     } else if (!isNoReply && q_dbus_connection_send_with_reply(connection, msg, &pending, timeout)) {
2194         if (pending) {
2195             q_dbus_message_unref(msg);
2196 
2197             pcall->pending = pending;
2198             q_dbus_pending_call_set_notify(pending, qDBusResultReceived, pcall, nullptr);
2199 
2200             // DBus won't notify us when a peer disconnects or server terminates so we need to track these ourselves
2201             if (mode == QDBusConnectionPrivate::PeerMode || mode == QDBusConnectionPrivate::ClientMode)
2202                 pendingCalls.append(pcall);
2203 
2204             return;
2205         } else {
2206             // we're probably disconnected at this point
2207             lastError = error = QDBusError(QDBusError::Disconnected, QDBusUtil::disconnectedErrorMessage());
2208         }
2209     } else {
2210         lastError = error = QDBusError(QDBusError::NoMemory, QStringLiteral("Out of memory"));
2211     }
2212 
2213     q_dbus_message_unref(msg);
2214     if (pcall) {
2215         pcall->replyMessage = QDBusMessage::createError(error);
2216         processFinishedCall(pcall);
2217     }
2218 }
2219 
2220 
connectSignal(const QString & service,const QString & path,const QString & interface,const QString & name,const QStringList & argumentMatch,const QString & signature,QObject * receiver,const char * slot)2221 bool QDBusConnectionPrivate::connectSignal(const QString &service,
2222                                            const QString &path, const QString &interface, const QString &name,
2223                                            const QStringList &argumentMatch, const QString &signature,
2224                                            QObject *receiver, const char *slot)
2225 {
2226     ArgMatchRules rules;
2227     rules.args = argumentMatch;
2228     return connectSignal(service, path, interface, name, rules, signature, receiver, slot);
2229 }
2230 
connectSignal(const QString & service,const QString & path,const QString & interface,const QString & name,const ArgMatchRules & argumentMatch,const QString & signature,QObject * receiver,const char * slot)2231 bool QDBusConnectionPrivate::connectSignal(const QString &service,
2232                                            const QString &path, const QString &interface, const QString &name,
2233                                            const ArgMatchRules &argumentMatch, const QString &signature,
2234                                            QObject *receiver, const char *slot)
2235 {
2236     // check the slot
2237     QDBusConnectionPrivate::SignalHook hook;
2238     QString key;
2239 
2240     hook.signature = signature;
2241     if (!prepareHook(hook, key, service, path, interface, name, argumentMatch, receiver, slot, 0, false))
2242         return false;           // don't connect
2243 
2244     Q_ASSERT(thread() != QThread::currentThread());
2245     return emit signalNeedsConnecting(key, hook);
2246 }
2247 
addSignalHook(const QString & key,const SignalHook & hook)2248 bool QDBusConnectionPrivate::addSignalHook(const QString &key, const SignalHook &hook)
2249 {
2250     QDBusWriteLocker locker(ConnectAction, this);
2251 
2252     // avoid duplicating:
2253     QDBusConnectionPrivate::SignalHookHash::ConstIterator it = signalHooks.constFind(key);
2254     QDBusConnectionPrivate::SignalHookHash::ConstIterator end = signalHooks.constEnd();
2255     for ( ; it != end && it.key() == key; ++it) {
2256         const QDBusConnectionPrivate::SignalHook &entry = it.value();
2257         if (entry.service == hook.service &&
2258             entry.path == hook.path &&
2259             entry.signature == hook.signature &&
2260             entry.obj == hook.obj &&
2261             entry.midx == hook.midx &&
2262             entry.argumentMatch == hook.argumentMatch) {
2263             // no need to compare the parameters if it's the same slot
2264             return false;     // already there
2265         }
2266     }
2267 
2268     signalHooks.insert(key, hook);
2269     connect(hook.obj, &QObject::destroyed, this, &QDBusConnectionPrivate::objectDestroyed,
2270             Qt::ConnectionType(Qt::BlockingQueuedConnection | Qt::UniqueConnection));
2271 
2272     MatchRefCountHash::iterator mit = matchRefCounts.find(hook.matchRule);
2273 
2274     if (mit != matchRefCounts.end()) { // Match already present
2275         mit.value() = mit.value() + 1;
2276         return true;
2277     }
2278 
2279     matchRefCounts.insert(hook.matchRule, 1);
2280 
2281     if (connection) {
2282         if (mode != QDBusConnectionPrivate::PeerMode) {
2283             qDBusDebug() << this << "Adding rule:" << hook.matchRule;
2284             q_dbus_bus_add_match(connection, hook.matchRule, nullptr);
2285 
2286             // Successfully connected the signal
2287             // Do we need to watch for this name?
2288             if (shouldWatchService(hook.service)) {
2289                 WatchedServicesHash::mapped_type &data = watchedServices[hook.service];
2290                 if (++data.refcount == 1) {
2291                     // we need to watch for this service changing
2292                     ArgMatchRules rules;
2293                     rules.args << hook.service;
2294                     q_dbus_bus_add_match(connection,
2295                                          buildMatchRule(QDBusUtil::dbusService(), QString(), QDBusUtil::dbusInterface(),
2296                                                         QDBusUtil::nameOwnerChanged(), rules, QString()),
2297                                          nullptr);
2298                     data.owner = getNameOwnerNoCache(hook.service);
2299                     qDBusDebug() << this << "Watching service" << hook.service << "for owner changes (current owner:"
2300                                  << data.owner << ")";
2301                 }
2302             }
2303         }
2304     }
2305     return true;
2306 }
2307 
disconnectSignal(const QString & service,const QString & path,const QString & interface,const QString & name,const QStringList & argumentMatch,const QString & signature,QObject * receiver,const char * slot)2308 bool QDBusConnectionPrivate::disconnectSignal(const QString &service,
2309                                            const QString &path, const QString &interface, const QString &name,
2310                                            const QStringList &argumentMatch, const QString &signature,
2311                                            QObject *receiver, const char *slot)
2312 {
2313     ArgMatchRules rules;
2314     rules.args = argumentMatch;
2315     return disconnectSignal(service, path, interface, name, rules, signature, receiver, slot);
2316 }
2317 
disconnectSignal(const QString & service,const QString & path,const QString & interface,const QString & name,const ArgMatchRules & argumentMatch,const QString & signature,QObject * receiver,const char * slot)2318 bool QDBusConnectionPrivate::disconnectSignal(const QString &service,
2319                                               const QString &path, const QString &interface, const QString &name,
2320                                               const ArgMatchRules &argumentMatch, const QString &signature,
2321                                               QObject *receiver, const char *slot)
2322 {
2323     // check the slot
2324     QDBusConnectionPrivate::SignalHook hook;
2325     QString key;
2326     QString name2 = name;
2327     if (name2.isNull())
2328         name2.detach();
2329 
2330     hook.signature = signature;
2331     if (!prepareHook(hook, key, service, path, interface, name, argumentMatch, receiver, slot, 0, false))
2332         return false;           // don't disconnect
2333 
2334     Q_ASSERT(thread() != QThread::currentThread());
2335     return emit signalNeedsDisconnecting(key, hook);
2336 }
2337 
removeSignalHook(const QString & key,const SignalHook & hook)2338 bool QDBusConnectionPrivate::removeSignalHook(const QString &key, const SignalHook &hook)
2339 {
2340     // remove it from our list:
2341     QDBusWriteLocker locker(ConnectAction, this);
2342     QDBusConnectionPrivate::SignalHookHash::Iterator it = signalHooks.find(key);
2343     QDBusConnectionPrivate::SignalHookHash::Iterator end = signalHooks.end();
2344     for ( ; it != end && it.key() == key; ++it) {
2345         const QDBusConnectionPrivate::SignalHook &entry = it.value();
2346         if (entry.service == hook.service &&
2347             entry.path == hook.path &&
2348             entry.signature == hook.signature &&
2349             entry.obj == hook.obj &&
2350             entry.midx == hook.midx &&
2351             entry.argumentMatch.args == hook.argumentMatch.args) {
2352             // no need to compare the parameters if it's the same slot
2353             removeSignalHookNoLock(it);
2354             return true;        // it was there
2355         }
2356     }
2357 
2358     // the slot was not found
2359     return false;
2360 }
2361 
2362 QDBusConnectionPrivate::SignalHookHash::Iterator
removeSignalHookNoLock(SignalHookHash::Iterator it)2363 QDBusConnectionPrivate::removeSignalHookNoLock(SignalHookHash::Iterator it)
2364 {
2365     const SignalHook &hook = it.value();
2366 
2367     bool erase = false;
2368     MatchRefCountHash::iterator i = matchRefCounts.find(hook.matchRule);
2369     if (i == matchRefCounts.end()) {
2370         qWarning("QDBusConnectionPrivate::disconnectSignal: MatchRule not found in matchRefCounts!!");
2371     } else {
2372         if (i.value() == 1) {
2373             erase = true;
2374             matchRefCounts.erase(i);
2375         }
2376         else {
2377             i.value() = i.value() - 1;
2378         }
2379     }
2380 
2381     // we don't care about errors here
2382     if (connection && erase) {
2383         if (mode != QDBusConnectionPrivate::PeerMode) {
2384             qDBusDebug() << this << "Removing rule:" << hook.matchRule;
2385             q_dbus_bus_remove_match(connection, hook.matchRule, nullptr);
2386 
2387             // Successfully disconnected the signal
2388             // Were we watching for this name?
2389             WatchedServicesHash::Iterator sit = watchedServices.find(hook.service);
2390             if (sit != watchedServices.end()) {
2391                 if (--sit.value().refcount == 0) {
2392                     watchedServices.erase(sit);
2393                     ArgMatchRules rules;
2394                     rules.args << hook.service;
2395                     q_dbus_bus_remove_match(connection,
2396                                             buildMatchRule(QDBusUtil::dbusService(), QString(), QDBusUtil::dbusInterface(),
2397                                                            QDBusUtil::nameOwnerChanged(), rules, QString()),
2398                                             nullptr);
2399                 }
2400             }
2401         }
2402 
2403     }
2404 
2405     return signalHooks.erase(it);
2406 }
2407 
registerObject(const ObjectTreeNode * node)2408 void QDBusConnectionPrivate::registerObject(const ObjectTreeNode *node)
2409 {
2410     connect(node->obj, &QObject::destroyed, this, &QDBusConnectionPrivate::objectDestroyed,
2411             Qt::ConnectionType(Qt::BlockingQueuedConnection | Qt::UniqueConnection));
2412 
2413     if (node->flags & (QDBusConnection::ExportAdaptors
2414                        | QDBusConnection::ExportScriptableSignals
2415                        | QDBusConnection::ExportNonScriptableSignals)) {
2416         QDBusAdaptorConnector *connector = qDBusCreateAdaptorConnector(node->obj);
2417 
2418         if (node->flags & (QDBusConnection::ExportScriptableSignals
2419                            | QDBusConnection::ExportNonScriptableSignals)) {
2420             connector->disconnectAllSignals(node->obj);
2421             connector->connectAllSignals(node->obj);
2422         }
2423 
2424         connect(connector, SIGNAL(relaySignal(QObject*,const QMetaObject*,int,QVariantList)),
2425                 this, SLOT(relaySignal(QObject*,const QMetaObject*,int,QVariantList)),
2426                 Qt::ConnectionType(Qt::QueuedConnection | Qt::UniqueConnection));
2427     }
2428 }
2429 
unregisterObject(const QString & path,QDBusConnection::UnregisterMode mode)2430 void QDBusConnectionPrivate::unregisterObject(const QString &path, QDBusConnection::UnregisterMode mode)
2431 {
2432     QDBusConnectionPrivate::ObjectTreeNode *node = &rootNode;
2433     QVector<QStringRef> pathComponents;
2434     int i;
2435     if (path == QLatin1String("/")) {
2436         i = 0;
2437     } else {
2438         pathComponents = path.splitRef(QLatin1Char('/'));
2439         i = 1;
2440     }
2441 
2442     huntAndUnregister(pathComponents, i, mode, node);
2443 }
2444 
connectRelay(const QString & service,const QString & path,const QString & interface,QDBusAbstractInterface * receiver,const QMetaMethod & signal)2445 void QDBusConnectionPrivate::connectRelay(const QString &service,
2446                                           const QString &path, const QString &interface,
2447                                           QDBusAbstractInterface *receiver,
2448                                           const QMetaMethod &signal)
2449 {
2450     // this function is called by QDBusAbstractInterface when one of its signals is connected
2451     // we set up a relay from D-Bus into it
2452     SignalHook hook;
2453     QString key;
2454 
2455     QByteArray sig;
2456     sig.append(QSIGNAL_CODE + '0');
2457     sig.append(signal.methodSignature());
2458     if (!prepareHook(hook, key, service, path, interface, QString(), ArgMatchRules(), receiver, sig,
2459                      QDBusAbstractInterface::staticMetaObject.methodCount(), true))
2460         return;                 // don't connect
2461 
2462     Q_ASSERT(thread() != QThread::currentThread());
2463     emit signalNeedsConnecting(key, hook);
2464 }
2465 
disconnectRelay(const QString & service,const QString & path,const QString & interface,QDBusAbstractInterface * receiver,const QMetaMethod & signal)2466 void QDBusConnectionPrivate::disconnectRelay(const QString &service,
2467                                              const QString &path, const QString &interface,
2468                                              QDBusAbstractInterface *receiver,
2469                                              const QMetaMethod &signal)
2470 {
2471     // this function is called by QDBusAbstractInterface when one of its signals is disconnected
2472     // we remove relay from D-Bus into it
2473     SignalHook hook;
2474     QString key;
2475 
2476     QByteArray sig;
2477     sig.append(QSIGNAL_CODE + '0');
2478     sig.append(signal.methodSignature());
2479     if (!prepareHook(hook, key, service, path, interface, QString(), ArgMatchRules(), receiver, sig,
2480                      QDBusAbstractInterface::staticMetaObject.methodCount(), true))
2481         return;                 // don't disconnect
2482 
2483     Q_ASSERT(thread() != QThread::currentThread());
2484     emit signalNeedsDisconnecting(key, hook);
2485 }
2486 
shouldWatchService(const QString & service)2487 bool QDBusConnectionPrivate::shouldWatchService(const QString &service)
2488 {
2489     // we don't have to watch anything in peer mode
2490     if (mode != ClientMode)
2491         return false;
2492     // we don't have to watch wildcard services (empty strings)
2493     if (service.isEmpty())
2494         return false;
2495     // we don't have to watch the bus driver
2496     if (service == QDBusUtil::dbusService())
2497         return false;
2498     return true;
2499 }
2500 
2501 /*!
2502     Sets up a watch rule for service \a service for the change described by
2503     mode \a mode. When the change happens, slot \a member in object \a obj will
2504     be called.
2505 
2506     The caller should call QDBusConnectionPrivate::shouldWatchService() before
2507     calling this function to check whether the service needs to be watched at
2508     all. Failing to do so may add rules that are never activated.
2509 */
watchService(const QString & service,QDBusServiceWatcher::WatchMode mode,QObject * obj,const char * member)2510 void QDBusConnectionPrivate::watchService(const QString &service, QDBusServiceWatcher::WatchMode mode, QObject *obj, const char *member)
2511 {
2512     ArgMatchRules matchArgs = matchArgsForService(service, mode);
2513     connectSignal(QDBusUtil::dbusService(), QString(), QDBusUtil::dbusInterface(), QDBusUtil::nameOwnerChanged(),
2514                   matchArgs, QString(), obj, member);
2515 }
2516 
2517 /*!
2518     Removes a watch rule set up by QDBusConnectionPrivate::watchService(). The
2519     arguments to this function must be the same as the ones for that function.
2520 
2521     Sets up a watch rule for service \a service for the change described by
2522     mode \a mode. When the change happens, slot \a member in object \a obj will
2523     be called.
2524 */
unwatchService(const QString & service,QDBusServiceWatcher::WatchMode mode,QObject * obj,const char * member)2525 void QDBusConnectionPrivate::unwatchService(const QString &service, QDBusServiceWatcher::WatchMode mode, QObject *obj, const char *member)
2526 {
2527     ArgMatchRules matchArgs = matchArgsForService(service, mode);
2528     disconnectSignal(QDBusUtil::dbusService(), QString(), QDBusUtil::dbusInterface(), QDBusUtil::nameOwnerChanged(),
2529                      matchArgs, QString(), obj, member);
2530 }
2531 
getNameOwner(const QString & serviceName)2532 QString QDBusConnectionPrivate::getNameOwner(const QString& serviceName)
2533 {
2534     if (QDBusUtil::isValidUniqueConnectionName(serviceName))
2535         return serviceName;
2536     if (!connection)
2537         return QString();
2538 
2539     {
2540         // acquire a read lock for the cache
2541         QReadLocker locker(&lock);
2542         WatchedServicesHash::ConstIterator it = watchedServices.constFind(serviceName);
2543         if (it != watchedServices.constEnd())
2544             return it->owner;
2545     }
2546 
2547     // not cached
2548     return getNameOwnerNoCache(serviceName);
2549 }
2550 
getNameOwnerNoCache(const QString & serviceName)2551 QString QDBusConnectionPrivate::getNameOwnerNoCache(const QString &serviceName)
2552 {
2553     QDBusMessage msg = QDBusMessage::createMethodCall(QDBusUtil::dbusService(),
2554             QDBusUtil::dbusPath(), QDBusUtil::dbusInterface(),
2555             QStringLiteral("GetNameOwner"));
2556     QDBusMessagePrivate::setParametersValidated(msg, true);
2557     msg << serviceName;
2558 
2559     QDBusPendingCallPrivate *pcall = sendWithReplyAsync(msg, nullptr, nullptr, nullptr);
2560     if (thread() == QThread::currentThread()) {
2561         // this function may be called in our own thread and
2562         // QDBusPendingCallPrivate::waitForFinished() would deadlock there
2563         q_dbus_pending_call_block(pcall->pending);
2564     }
2565     pcall->waitForFinished();
2566     msg = pcall->replyMessage;
2567 
2568     if (!pcall->ref.deref())
2569         delete pcall;
2570 
2571     if (msg.type() == QDBusMessage::ReplyMessage)
2572         return msg.arguments().at(0).toString();
2573     return QString();
2574 }
2575 
2576 QDBusMetaObject *
findMetaObject(const QString & service,const QString & path,const QString & interface,QDBusError & error)2577 QDBusConnectionPrivate::findMetaObject(const QString &service, const QString &path,
2578                                        const QString &interface, QDBusError &error)
2579 {
2580     // service must be a unique connection name
2581     if (!interface.isEmpty()) {
2582         QDBusReadLocker locker(FindMetaObject1Action, this);
2583         QDBusMetaObject *mo = cachedMetaObjects.value(interface, 0);
2584         if (mo)
2585             return mo;
2586     }
2587 
2588     // introspect the target object
2589     QDBusMessage msg = QDBusMessage::createMethodCall(service, path,
2590                                                 QDBusUtil::dbusInterfaceIntrospectable(),
2591                                                 QStringLiteral("Introspect"));
2592     QDBusMessagePrivate::setParametersValidated(msg, true);
2593 
2594     QDBusMessage reply = sendWithReply(msg, QDBus::Block);
2595 
2596     // it doesn't exist yet, we have to create it
2597     QDBusWriteLocker locker(FindMetaObject2Action, this);
2598     QDBusMetaObject *mo = nullptr;
2599     if (!interface.isEmpty())
2600         mo = cachedMetaObjects.value(interface, 0);
2601     if (mo)
2602         // maybe it got created when we switched from read to write lock
2603         return mo;
2604 
2605     QString xml;
2606     if (reply.type() == QDBusMessage::ReplyMessage) {
2607         if (reply.signature() == QLatin1String("s"))
2608             // fetch the XML description
2609             xml = reply.arguments().at(0).toString();
2610     } else {
2611         error = QDBusError(reply);
2612         lastError = error;
2613         if (reply.type() != QDBusMessage::ErrorMessage || error.type() != QDBusError::UnknownMethod)
2614             return nullptr; // error
2615     }
2616 
2617     // release the lock and return
2618     QDBusMetaObject *result = QDBusMetaObject::createMetaObject(interface, xml,
2619                                                                 cachedMetaObjects, error);
2620     lastError = error;
2621     return result;
2622 }
2623 
registerService(const QString & serviceName)2624 void QDBusConnectionPrivate::registerService(const QString &serviceName)
2625 {
2626     QDBusWriteLocker locker(RegisterServiceAction, this);
2627     registerServiceNoLock(serviceName);
2628 }
2629 
registerServiceNoLock(const QString & serviceName)2630 void QDBusConnectionPrivate::registerServiceNoLock(const QString &serviceName)
2631 {
2632     serviceNames.append(serviceName);
2633 }
2634 
unregisterService(const QString & serviceName)2635 void QDBusConnectionPrivate::unregisterService(const QString &serviceName)
2636 {
2637     QDBusWriteLocker locker(UnregisterServiceAction, this);
2638     unregisterServiceNoLock(serviceName);
2639 }
2640 
unregisterServiceNoLock(const QString & serviceName)2641 void QDBusConnectionPrivate::unregisterServiceNoLock(const QString &serviceName)
2642 {
2643     serviceNames.removeAll(serviceName);
2644 }
2645 
isServiceRegisteredByThread(const QString & serviceName)2646 bool QDBusConnectionPrivate::isServiceRegisteredByThread(const QString &serviceName)
2647 {
2648     if (!serviceName.isEmpty() && serviceName == baseService)
2649         return true;
2650     if (serviceName == QDBusUtil::dbusService())
2651         return false;
2652 
2653     QDBusReadLocker locker(UnregisterServiceAction, this);
2654     return serviceNames.contains(serviceName);
2655 }
2656 
postEventToThread(int action,QObject * object,QEvent * ev)2657 void QDBusConnectionPrivate::postEventToThread(int action, QObject *object, QEvent *ev)
2658 {
2659     QDBusLockerBase::reportThreadAction(action, QDBusLockerBase::BeforePost, this);
2660     QCoreApplication::postEvent(object, ev);
2661     QDBusLockerBase::reportThreadAction(action, QDBusLockerBase::AfterPost, this);
2662 }
2663 
2664 QT_END_NAMESPACE
2665 
2666 #endif // QT_NO_DBUS
2667