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