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
79QT_BEGIN_NAMESPACE
80
81// used with dbus_server_allocate_data_slot
82static dbus_int32_t server_slot = -1;
83
84static QBasicAtomicInt isDebugging = Q_BASIC_ATOMIC_INITIALIZER(-1);
85#define qDBusDebug if (::isDebugging == 0); else qDebug
86
87static 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
100static 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
111void 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}
130qdbusThreadDebugFunc qdbusThreadDebug = nullptr;
131#endif
132
133typedef QVarLengthArray<QDBusSpyCallEvent::Hook, 4> QDBusSpyHookList;
134Q_GLOBAL_STATIC(QDBusSpyHookList, qDBusSpyHookList)
135
136extern "C" {
137
138 // libdbus-1 callbacks
139
140static 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
165static 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
187static 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
198static 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
230static 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
253static 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
280static 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
289static void qDBusNewConnection(DBusServer *server, DBusConnection *connection, void *data)
290{
291 // ### We may want to separate the server from the QDBusConnectionPrivate
292 Q_ASSERT(server);
293 Q_UNUSED(server);
294 Q_ASSERT(connection);
295 Q_ASSERT(data);
296
297 if (!QDBusConnectionManager::instance())
298 return;
299
300 // keep the connection alive
301 q_dbus_connection_ref(connection);
302 QDBusConnectionPrivate *serverConnection = static_cast<QDBusConnectionPrivate *>(data);
303
304 // allow anonymous authentication
305 if (serverConnection->anonymousAuthenticationAllowed)
306 q_dbus_connection_set_allow_anonymous(connection, true);
307
308 QDBusConnectionPrivate *newConnection = new QDBusConnectionPrivate(serverConnection->parent());
309 const auto locker = qt_scoped_lock(QDBusConnectionManager::instance()->mutex);
310 QDBusConnectionManager::instance()->setConnection(QLatin1String("QDBusServer-") + QString::number(reinterpret_cast<qulonglong>(newConnection), 16), newConnection);
311 serverConnection->serverConnectionNames << newConnection->name;
312
313 // setPeer does the error handling for us
314 QDBusErrorInternal error;
315 newConnection->setPeer(connection, error);
316 newConnection->setDispatchEnabled(false);
317
318 // this is a queued connection and will resume in the QDBusServer's thread
319 emit serverConnection->newServerConnection(newConnection);
320
321 // we've disabled dispatching of events, so now we post an event to the
322 // QDBusServer's thread in order to enable it after the
323 // QDBusServer::newConnection() signal has been received by the
324 // application's code
325 newConnection->ref.ref();
326 QReadLocker serverLock(&serverConnection->lock);
327 QDBusConnectionDispatchEnabler *o = new QDBusConnectionDispatchEnabler(newConnection);
328 QTimer::singleShot(0, o, SLOT(execute()));
329 if (serverConnection->serverObject)
330 o->moveToThread(serverConnection->serverObject->thread());
331}
332
333void QDBusConnectionPrivate::_q_newConnection(QDBusConnectionPrivate *newConnection)
334{
335 Q_ASSERT(mode == ServerMode);
336 emit serverObject->newConnection(QDBusConnectionPrivate::q(newConnection));
337}
338
339} // extern "C"
340
341static QByteArray buildMatchRule(const QString &service,
342 const QString &objectPath, const QString &interface,
343 const QString &member, const QDBusConnectionPrivate::ArgMatchRules &argMatch, const QString & /*signature*/)
344{
345 QString result;
346 result += QLatin1String("type='signal',");
347 const auto keyValue = QLatin1String("%1='%2',");
348
349 if (!service.isEmpty())
350 result += keyValue.arg(QLatin1String("sender"), service);
351 if (!objectPath.isEmpty())
352 result += keyValue.arg(QLatin1String("path"), objectPath);
353 if (!interface.isEmpty())
354 result += keyValue.arg(QLatin1String("interface"), interface);
355 if (!member.isEmpty())
356 result += keyValue.arg(QLatin1String("member"), member);
357
358 // add the argument string-matching now
359 if (!argMatch.args.isEmpty()) {
360 const QString keyValue = QLatin1String("arg%1='%2',");
361 for (int i = 0; i < argMatch.args.count(); ++i)
362 if (!argMatch.args.at(i).isNull())
363 result += keyValue.arg(i).arg(argMatch.args.at(i));
364 }
365 if (!argMatch.arg0namespace.isEmpty()) {
366 result += QLatin1String("arg0namespace='%1',").arg(argMatch.arg0namespace);
367 }
368
369 result.chop(1); // remove ending comma
370 return result.toLatin1();
371}
372
373static bool findObject(const QDBusConnectionPrivate::ObjectTreeNode *root,
374 const QString &fullpath, int &usedLength,
375 QDBusConnectionPrivate::ObjectTreeNode &result)
376{
377 if (!fullpath.compare(QLatin1String("/")) && root->obj) {
378 usedLength = 1;
379 result = *root;
380 return root;
381 }
382 int start = 0;
383 int length = fullpath.length();
384 if (fullpath.at(0) == QLatin1Char('/'))
385 start = 1;
386
387 // walk the object tree
388 QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator node = root;
389 while (start < length && node) {
390 if (node->flags & QDBusConnection::ExportChildObjects)
391 break;
392 if ((node->flags & QDBusConnectionPrivate::VirtualObject) && (node->flags & QDBusConnection::SubPath))
393 break;
394 int end = fullpath.indexOf(QLatin1Char('/'), start);
395 end = (end == -1 ? length : end);
396 QStringView pathComponent = QStringView{fullpath}.mid(start, end - start);
397
398 QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator it =
399 std::lower_bound(node->children.constBegin(), node->children.constEnd(), pathComponent);
400 if (it != node->children.constEnd() && it->name == pathComponent)
401 // match
402 node = it;
403 else
404 node = nullptr;
405
406 start = end + 1;
407 }
408
409 // found our object
410 usedLength = (start > length ? length : start);
411 if (node) {
412 if (node->obj || !node->children.isEmpty())
413 result = *node;
414 else
415 // there really is no object here
416 // we're just looking at an unused space in the QList
417 node = nullptr;
418 }
419 return node;
420}
421
422static QObject *findChildObject(const QDBusConnectionPrivate::ObjectTreeNode *root,
423 const QString &fullpath, int start)
424{
425 int length = fullpath.length();
426
427 // any object in the tree can tell us to switch to its own object tree:
428 const QDBusConnectionPrivate::ObjectTreeNode *node = root;
429 if (node && node->flags & QDBusConnection::ExportChildObjects) {
430 QObject *obj = node->obj;
431
432 while (obj) {
433 if (start >= length)
434 // we're at the correct level
435 return obj;
436
437 int pos = fullpath.indexOf(QLatin1Char('/'), start);
438 pos = (pos == -1 ? length : pos);
439 auto pathComponent = QStringView{fullpath}.mid(start, pos - start);
440
441 const QObjectList children = obj->children();
442
443 // find a child with the proper name
444 QObject *next = nullptr;
445 QObjectList::ConstIterator it = children.constBegin();
446 QObjectList::ConstIterator end = children.constEnd();
447 for ( ; it != end; ++it)
448 if ((*it)->objectName() == pathComponent) {
449 next = *it;
450 break;
451 }
452
453 if (!next)
454 break;
455
456 obj = next;
457 start = pos + 1;
458 }
459 }
460
461 // object not found
462 return nullptr;
463}
464
465static QDBusConnectionPrivate::ArgMatchRules matchArgsForService(const QString &service, QDBusServiceWatcher::WatchMode mode)
466{
467 QDBusConnectionPrivate::ArgMatchRules matchArgs;
468 if (service.endsWith(QLatin1Char('*'))) {
469 matchArgs.arg0namespace = service.chopped(1);
470 matchArgs.args << QString();
471 }
472 else
473 matchArgs.args << service;
474
475 switch (mode) {
476 case QDBusServiceWatcher::WatchForOwnerChange:
477 break;
478
479 case QDBusServiceWatcher::WatchForRegistration:
480 matchArgs.args << QString::fromLatin1("", 0);
481 break;
482
483 case QDBusServiceWatcher::WatchForUnregistration:
484 matchArgs.args << QString() << QString::fromLatin1("", 0);
485 break;
486 }
487 return matchArgs;
488}
489
490
491extern Q_DBUS_EXPORT void qDBusAddSpyHook(QDBusSpyCallEvent::Hook);
492void qDBusAddSpyHook(QDBusSpyCallEvent::Hook hook)
493{
494 qDBusSpyHookList()->append(hook);
495}
496
497QDBusSpyCallEvent::~QDBusSpyCallEvent()
498{
499 // Reinsert the message into the processing queue for the connection.
500 // This is done in the destructor so the message is reinserted even if
501 // QCoreApplication is destroyed.
502 QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(const_cast<QObject *>(sender()));
503 qDBusDebug() << d << "message spies done for" << msg;
504 emit d->spyHooksFinished(msg);
505}
506
507void QDBusSpyCallEvent::placeMetaCall(QObject *)
508{
509 invokeSpyHooks(msg, hooks, hookCount);
510}
511
512inline void QDBusSpyCallEvent::invokeSpyHooks(const QDBusMessage &msg, const Hook *hooks, int hookCount)
513{
514 // call the spy hook list
515 for (int i = 0; i < hookCount; ++i)
516 hooks[i](msg);
517}
518
519extern "C" {
520static DBusHandlerResult
521qDBusSignalFilter(DBusConnection *connection, DBusMessage *message, void *data)
522{
523 Q_ASSERT(data);
524 Q_UNUSED(connection);
525 QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
526 if (d->mode == QDBusConnectionPrivate::InvalidMode)
527 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
528
529 QDBusMessage amsg = QDBusMessagePrivate::fromDBusMessage(message, d->connectionCapabilities());
530 qDBusDebug() << d << "got message (signal):" << amsg;
531
532 return d->handleMessage(amsg) ?
533 DBUS_HANDLER_RESULT_HANDLED :
534 DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
535}
536}
537
538bool QDBusConnectionPrivate::handleMessage(const QDBusMessage &amsg)
539{
540 if (!ref.loadRelaxed())
541 return false;
542
543 // local message are always delivered, regardless of filtering
544 // or whether the dispatcher is enabled
545 bool isLocal = QDBusMessagePrivate::isLocal(amsg);
546
547 if (!dispatchEnabled && !isLocal) {
548 // queue messages only, we'll handle them later
549 qDBusDebug() << this << "delivery is suspended";
550 pendingMessages << amsg;
551 return amsg.type() == QDBusMessage::MethodCallMessage;
552 }
553
554 switch (amsg.type()) {
555 case QDBusMessage::SignalMessage:
556 handleSignal(amsg);
557 // if there are any other filters in this DBusConnection,
558 // let them see the signal too
559 return false;
560 case QDBusMessage::MethodCallMessage:
561 // run it through the spy filters (if any) before the regular processing:
562 // a) if it's a local message, we're in the caller's thread, so invoke the filter directly
563 // b) if it's an external message, post to the main thread
564 if (Q_UNLIKELY(qDBusSpyHookList.exists()) && qApp) {
565 const QDBusSpyHookList &list = *qDBusSpyHookList;
566 if (isLocal) {
567 Q_ASSERT(QThread::currentThread() != thread());
568 qDBusDebug() << this << "invoking message spies directly";
569 QDBusSpyCallEvent::invokeSpyHooks(amsg, list.constData(), list.size());
570 } else {
571 qDBusDebug() << this << "invoking message spies via event";
572 QCoreApplication::postEvent(qApp, new QDBusSpyCallEvent(this, QDBusConnection(this),
573 amsg, list.constData(), list.size()));
574
575 // we'll be called back, so return
576 return true;
577 }
578 }
579
580 handleObjectCall(amsg);
581 return true;
582 case QDBusMessage::ReplyMessage:
583 case QDBusMessage::ErrorMessage:
584 case QDBusMessage::InvalidMessage:
585 return false; // we don't handle those here
586 }
587
588 return false;
589}
590
591static void huntAndDestroy(QObject *needle, QDBusConnectionPrivate::ObjectTreeNode &haystack)
592{
593 for (auto &node : haystack.children)
594 huntAndDestroy(needle, node);
595
596 auto isInactive = [](QDBusConnectionPrivate::ObjectTreeNode &node) { return !node.isActive(); };
597
598 haystack.children.erase(std::remove_if(haystack.children.begin(), haystack.children.end(),
599 isInactive),
600 haystack.children.end());
601
602 if (needle == haystack.obj) {
603 haystack.obj = nullptr;
604 haystack.flags = 0;
605 }
606}
607
608static void huntAndUnregister(const QList<QStringView> &pathComponents, int i,
609 QDBusConnection::UnregisterMode mode,
610 QDBusConnectionPrivate::ObjectTreeNode *node)
611{
612 if (pathComponents.count() == i) {
613 // found it
614 node->obj = nullptr;
615 node->flags = 0;
616
617 if (mode == QDBusConnection::UnregisterTree) {
618 // clear the sub-tree as well
619 node->children.clear(); // can't disconnect the objects because we really don't know if they can
620 // be found somewhere else in the path too
621 }
622 } else {
623 // keep going
624 QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator end = node->children.end();
625 QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator it =
626 std::lower_bound(node->children.begin(), end, pathComponents.at(i));
627 if (it == end || it->name != pathComponents.at(i))
628 return; // node not found
629
630 huntAndUnregister(pathComponents, i + 1, mode, it);
631 if (!it->isActive())
632 node->children.erase(it);
633 }
634}
635
636static void huntAndEmit(DBusConnection *connection, DBusMessage *msg,
637 QObject *needle, const QDBusConnectionPrivate::ObjectTreeNode &haystack,
638 bool isScriptable, bool isAdaptor, const QString &path = QString())
639{
640 QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator it = haystack.children.constBegin();
641 QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator end = haystack.children.constEnd();
642 for ( ; it != end; ++it) {
643 if (it->isActive())
644 huntAndEmit(connection, msg, needle, *it, isScriptable, isAdaptor, path + QLatin1Char('/') + it->name);
645 }
646
647 if (needle == haystack.obj) {
648 // is this a signal we should relay?
649 if (isAdaptor && (haystack.flags & QDBusConnection::ExportAdaptors) == 0)
650 return; // no: it comes from an adaptor and we're not exporting adaptors
651 else if (!isAdaptor) {
652 int mask = isScriptable
653 ? QDBusConnection::ExportScriptableSignals
654 : QDBusConnection::ExportNonScriptableSignals;
655 if ((haystack.flags & mask) == 0)
656 return; // signal was not exported
657 }
658
659 QByteArray p = path.toLatin1();
660 if (p.isEmpty())
661 p = "/";
662 qDBusDebug() << QThread::currentThread() << "emitting signal at" << p;
663 DBusMessage *msg2 = q_dbus_message_copy(msg);
664 q_dbus_message_set_path(msg2, p);
665 q_dbus_connection_send(connection, msg2, nullptr);
666 q_dbus_message_unref(msg2);
667 }
668}
669
670static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags,
671 const QString &signature_, QList<QMetaType> &metaTypes)
672{
673 QByteArray msgSignature = signature_.toLatin1();
674
675 for (int idx = mo->methodCount() - 1 ; idx >= QObject::staticMetaObject.methodCount(); --idx) {
676 QMetaMethod mm = mo->method(idx);
677
678 // check access:
679 if (mm.access() != QMetaMethod::Public)
680 continue;
681
682 // check type:
683 if (mm.methodType() != QMetaMethod::Slot && mm.methodType() != QMetaMethod::Method)
684 continue;
685
686 // check name:
687 if (mm.name() != name)
688 continue;
689
690 QMetaType returnType = mm.returnMetaType();
691 bool isAsync = qDBusCheckAsyncTag(mm.tag());
692 bool isScriptable = mm.attributes() & QMetaMethod::Scriptable;
693
694 // consistency check:
695 if (isAsync && returnType.id() != QMetaType::Void)
696 continue;
697
698 QString errorMsg;
699 int inputCount = qDBusParametersForMethod(mm, metaTypes, errorMsg);
700 if (inputCount == -1)
701 continue; // problem parsing
702
703 metaTypes[0] = returnType;
704 bool hasMessage = false;
705 if (inputCount > 0 &&
706 metaTypes.at(inputCount) == QDBusMetaTypeId::message()) {
707 // "no input parameters" is allowed as long as the message meta type is there
708 hasMessage = true;
709 --inputCount;
710 }
711
712 // try to match the parameters
713 int i;
714 QByteArray reconstructedSignature;
715 for (i = 1; i <= inputCount; ++i) {
716 const char *typeSignature = QDBusMetaType::typeToSignature( metaTypes.at(i) );
717 if (!typeSignature)
718 break; // invalid
719
720 reconstructedSignature += typeSignature;
721 if (!msgSignature.startsWith(reconstructedSignature))
722 break;
723 }
724
725 if (reconstructedSignature != msgSignature)
726 continue; // we didn't match them all
727
728 if (hasMessage)
729 ++i;
730
731 // make sure that the output parameters have signatures too
732 if (returnType.isValid() && returnType.id() != QMetaType::Void && QDBusMetaType::typeToSignature(returnType) == nullptr)
733 continue;
734
735 bool ok = true;
736 for (int j = i; ok && j < metaTypes.count(); ++j)
737 if (QDBusMetaType::typeToSignature(metaTypes.at(i)) == nullptr)
738 ok = false;
739 if (!ok)
740 continue;
741
742 // consistency check:
743 if (isAsync && metaTypes.count() > i + 1)
744 continue;
745
746 if (mm.methodType() == QMetaMethod::Slot) {
747 if (isScriptable && (flags & QDBusConnection::ExportScriptableSlots) == 0)
748 continue; // scriptable slots not exported
749 if (!isScriptable && (flags & QDBusConnection::ExportNonScriptableSlots) == 0)
750 continue; // non-scriptable slots not exported
751 } else {
752 if (isScriptable && (flags & QDBusConnection::ExportScriptableInvokables) == 0)
753 continue; // scriptable invokables not exported
754 if (!isScriptable && (flags & QDBusConnection::ExportNonScriptableInvokables) == 0)
755 continue; // non-scriptable invokables not exported
756 }
757
758 // if we got here, this slot matched
759 return idx;
760 }
761
762 // no slot matched
763 return -1;
764}
765
766/*!
767 \internal
768 Enables or disables the delivery of incoming method calls and signals. If
769 \a enable is true, this will also cause any queued, pending messages to be
770 delivered.
771 */
772void QDBusConnectionPrivate::setDispatchEnabled(bool enable)
773{
774 checkThread();
775 dispatchEnabled = enable;
776 if (enable)
777 emit dispatchStatusChanged();
778}
779
780static QDBusCallDeliveryEvent * const DIRECT_DELIVERY = (QDBusCallDeliveryEvent *)1;
781
782QDBusCallDeliveryEvent *QDBusConnectionPrivate::prepareReply(QDBusConnectionPrivate *target,
783 QObject *object, int idx,
784 const QList<QMetaType> &metaTypes,
785 const QDBusMessage &msg)
786{
787 Q_ASSERT(object);
788 Q_UNUSED(object);
789
790 int n = metaTypes.count() - 1;
791 if (metaTypes[n] == QDBusMetaTypeId::message())
792 --n;
793
794 if (msg.arguments().count() < n)
795 return nullptr; // too few arguments
796
797 // check that types match
798 for (int i = 0; i < n; ++i)
799 if (metaTypes.at(i + 1) != msg.arguments().at(i).metaType() &&
800 msg.arguments().at(i).metaType() != QMetaType::fromType<QDBusArgument>())
801 return nullptr; // no match
802
803 // we can deliver
804 // prepare for the call
805 if (target == object)
806 return DIRECT_DELIVERY;
807 return new QDBusCallDeliveryEvent(QDBusConnection(target), idx, target, msg, metaTypes);
808}
809
810void QDBusConnectionPrivate::activateSignal(const QDBusConnectionPrivate::SignalHook& hook,
811 const QDBusMessage &msg)
812{
813 // This is called by QDBusConnectionPrivate::handleSignal to deliver a signal
814 // that was received from D-Bus
815 //
816 // Signals are delivered to slots if the parameters match
817 // Slots can have less parameters than there are on the message
818 // Slots can optionally have one final parameter that is a QDBusMessage
819 // Slots receive read-only copies of the message (i.e., pass by value or by const-ref)
820 QDBusCallDeliveryEvent *call = prepareReply(this, hook.obj, hook.midx, hook.params, msg);
821 if (call == DIRECT_DELIVERY) {
822 // short-circuit delivery
823 Q_ASSERT(this == hook.obj);
824 deliverCall(this, 0, msg, hook.params, hook.midx);
825 return;
826 }
827 if (call)
828 postEventToThread(ActivateSignalAction, hook.obj, call);
829}
830
831bool QDBusConnectionPrivate::activateCall(QObject* object, int flags, const QDBusMessage &msg)
832{
833 // This is called by QDBusConnectionPrivate::handleObjectCall to place a call
834 // to a slot on the object.
835 //
836 // The call is delivered to the first slot that matches the following conditions:
837 // - has the same name as the message's target member
838 // - ALL of the message's types are found in slot's parameter list
839 // - optionally has one more parameter of type QDBusMessage
840 // If none match, then the slot of the same name as the message target and with
841 // the first type of QDBusMessage is delivered.
842 //
843 // The D-Bus specification requires that all MethodCall messages be replied to, unless the
844 // caller specifically waived this requirement. This means that we inspect if the user slot
845 // generated a reply and, if it didn't, we will. Obviously, if the user slot doesn't take a
846 // QDBusMessage parameter, it cannot generate a reply.
847 //
848 // When a return message is generated, the slot's return type, if any, will be placed
849 // in the message's first position. If there are non-const reference parameters to the
850 // slot, they must appear at the end and will be placed in the subsequent message
851 // positions.
852
853 static const char cachePropertyName[] = "_qdbus_slotCache";
854
855 if (!object)
856 return false;
857
858 Q_ASSERT_X(QThread::currentThread() == object->thread(),
859 "QDBusConnection: internal threading error",
860 "function called for an object that is in another thread!!");
861
862 QDBusSlotCache slotCache =
863 qvariant_cast<QDBusSlotCache>(object->property(cachePropertyName));
864 QString cacheKey = msg.member(), signature = msg.signature();
865 if (!signature.isEmpty()) {
866 cacheKey.reserve(cacheKey.length() + 1 + signature.length());
867 cacheKey += QLatin1Char('.');
868 cacheKey += signature;
869 }
870
871 QDBusSlotCache::Hash::ConstIterator cacheIt = slotCache.hash.constFind(cacheKey);
872 while (cacheIt != slotCache.hash.constEnd() && cacheIt->flags != flags &&
873 cacheIt.key() == cacheKey)
874 ++cacheIt;
875 if (cacheIt == slotCache.hash.constEnd() || cacheIt.key() != cacheKey)
876 {
877 // not cached, analyze the meta object
878 const QMetaObject *mo = object->metaObject();
879 QByteArray memberName = msg.member().toUtf8();
880
881 // find a slot that matches according to the rules above
882 QDBusSlotCache::Data slotData;
883 slotData.flags = flags;
884 slotData.slotIdx = ::findSlot(mo, memberName, flags, msg.signature(), slotData.metaTypes);
885 if (slotData.slotIdx == -1) {
886 // ### this is where we want to add the connection as an arg too
887 // try with no parameters, but with a QDBusMessage
888 slotData.slotIdx = ::findSlot(mo, memberName, flags, QString(), slotData.metaTypes);
889 if (slotData.metaTypes.count() != 2 ||
890 slotData.metaTypes.at(1) != QDBusMetaTypeId::message()) {
891 // not found
892 // save the negative lookup
893 slotData.slotIdx = -1;
894 slotData.metaTypes.clear();
895 slotCache.hash.insert(cacheKey, slotData);
896 object->setProperty(cachePropertyName, QVariant::fromValue(slotCache));
897 return false;
898 }
899 }
900
901 // save to the cache
902 slotCache.hash.insert(cacheKey, slotData);
903 object->setProperty(cachePropertyName, QVariant::fromValue(slotCache));
904
905 // found the slot to be called
906 deliverCall(object, flags, msg, slotData.metaTypes, slotData.slotIdx);
907 return true;
908 } else if (cacheIt->slotIdx == -1) {
909 // negative cache
910 return false;
911 } else {
912 // use the cache
913 deliverCall(object, flags, msg, cacheIt->metaTypes, cacheIt->slotIdx);
914 return true;
915 }
916 return false;
917}
918
919void QDBusConnectionPrivate::deliverCall(QObject *object, int /*flags*/, const QDBusMessage &msg,
920 const QList<QMetaType> &metaTypes, int slotIdx)
921{
922 Q_ASSERT_X(!object || QThread::currentThread() == object->thread(),
923 "QDBusConnection: internal threading error",
924 "function called for an object that is in another thread!!");
925
926 QVarLengthArray<void *, 10> params;
927 params.reserve(metaTypes.count());
928
929 QVariantList auxParameters;
930 // let's create the parameter list
931
932 // first one is the return type -- add it below
933 params.append(nullptr);
934
935 // add the input parameters
936 int i;
937 int pCount = qMin(msg.arguments().count(), metaTypes.count() - 1);
938 for (i = 1; i <= pCount; ++i) {
939 auto id = metaTypes[i];
940 if (id == QDBusMetaTypeId::message())
941 break;
942
943 const QVariant &arg = msg.arguments().at(i - 1);
944 if (arg.metaType() == id)
945 // no conversion needed
946 params.append(const_cast<void *>(arg.constData()));
947 else if (arg.metaType() == QMetaType::fromType<QDBusArgument>()) {
948 // convert to what the function expects
949 auxParameters.append(QVariant(QMetaType(id)));
950
951 const QDBusArgument &in =
952 *reinterpret_cast<const QDBusArgument *>(arg.constData());
953 QVariant &out = auxParameters[auxParameters.count() - 1];
954
955 if (Q_UNLIKELY(!QDBusMetaType::demarshall(in, out.metaType(), out.data())))
956 qFatal("Internal error: demarshalling function for type '%s' (%d) failed!",
957 out.typeName(), out.metaType().id());
958
959 params.append(const_cast<void *>(out.constData()));
960 } else {
961 qFatal("Internal error: got invalid meta type %d (%s) "
962 "when trying to convert to meta type %d (%s)",
963 arg.metaType().id(), arg.metaType().name(),
964 id.id(), id.name());
965 }
966 }
967
968 if (metaTypes.count() > i && metaTypes[i] == QDBusMetaTypeId::message()) {
969 params.append(const_cast<void*>(static_cast<const void*>(&msg)));
970 ++i;
971 }
972
973 // output arguments
974 const int numMetaTypes = metaTypes.count();
975 QVariantList outputArgs;
976 if (metaTypes[0].id() != QMetaType::Void && metaTypes[0].isValid()) {
977 outputArgs.reserve(numMetaTypes - i + 1);
978 QVariant arg{QMetaType(metaTypes[0])};
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{QMetaType(metaTypes[i])};
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
1026extern bool qDBusInitThreads();
1027
1028QDBusConnectionPrivate::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
1072QDBusConnectionPrivate::~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
1102void 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
1116void 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
1161void QDBusConnectionPrivate::handleDBusDisconnection()
1162{
1163 while (!pendingCalls.isEmpty())
1164 processFinishedCall(pendingCalls.first());
1165}
1166
1167void QDBusConnectionPrivate::checkThread()
1168{
1169 Q_ASSERT(thread() == QDBusConnectionManager::instance());
1170 Q_ASSERT(QThread::currentThread() == thread());
1171}
1172
1173bool 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
1184void QDBusConnectionPrivate::timerEvent(QTimerEvent *e)
1185{
1186 {
1187 DBusTimeout *timeout = timeouts.value(e->timerId(), nullptr);
1188 if (timeout)
1189 q_dbus_timeout_handle(timeout);
1190 }
1191
1192 doDispatch();
1193}
1194
1195void 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
1212void 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
1229void 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
1245void 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
1261void 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
1301void 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
1317int QDBusConnectionPrivate::findSlot(QObject *obj, const QByteArray &normalizedName,
1318 QList<QMetaType> &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
1332bool 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
1378void 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
1401bool 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
1447void 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
1534void 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
1599QDBusActivateObjectEvent::~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
1611void 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
1624void 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
1674void 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
1699void 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(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
1712void 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
1751void 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
1782static 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
1806void QDBusConnectionPrivate::handleAuthentication()
1807{
1808 capabilities.storeRelaxed(connectionCapabilies(connection));
1809 isAuthenticated = true;
1810}
1811
1812void 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(QMetaType::Void) << QMetaType(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(QMetaType::Void) << QMetaType(QMetaType::QString) << QMetaType(QMetaType::QString) << QMetaType(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
1870extern "C"{
1871static 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
1880void 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
1942bool 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.
1985class QDBusBlockingCallWatcher
1986{
1987public:
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
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
2056private:
2057 QDBusMessage m_message;
2058 int m_maxCallTimeoutMs;
2059 QElapsedTimer m_callTimer;
2060};
2061
2062
2063QDBusMessage 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
2094QDBusMessage 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
2127QDBusPendingCallPrivate *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
2181void 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
2221bool 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
2231bool 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
2248bool 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
2308bool 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
2318bool 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
2338bool 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
2362QDBusConnectionPrivate::SignalHookHash::Iterator
2363QDBusConnectionPrivate::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
2408void 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
2430void QDBusConnectionPrivate::unregisterObject(const QString &path, QDBusConnection::UnregisterMode mode)
2431{
2432 QDBusConnectionPrivate::ObjectTreeNode *node = &rootNode;
2433 QList<QStringView> pathComponents;
2434 int i;
2435 if (path == QLatin1String("/")) {
2436 i = 0;
2437 } else {
2438 pathComponents = QStringView{path}.split(QLatin1Char('/'));
2439 i = 1;
2440 }
2441
2442 huntAndUnregister(pathComponents, i, mode, node);
2443}
2444
2445void 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
2466void 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
2487bool 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*/
2510void 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*/
2525void 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
2532QString 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
2551QString 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
2576QDBusMetaObject *
2577QDBusConnectionPrivate::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, nullptr);
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, nullptr);
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
2624void QDBusConnectionPrivate::registerService(const QString &serviceName)
2625{
2626 QDBusWriteLocker locker(RegisterServiceAction, this);
2627 registerServiceNoLock(serviceName);
2628}
2629
2630void QDBusConnectionPrivate::registerServiceNoLock(const QString &serviceName)
2631{
2632 serviceNames.append(serviceName);
2633}
2634
2635void QDBusConnectionPrivate::unregisterService(const QString &serviceName)
2636{
2637 QDBusWriteLocker locker(UnregisterServiceAction, this);
2638 unregisterServiceNoLock(serviceName);
2639}
2640
2641void QDBusConnectionPrivate::unregisterServiceNoLock(const QString &serviceName)
2642{
2643 serviceNames.removeAll(serviceName);
2644}
2645
2646bool 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
2657void 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
2664QT_END_NAMESPACE
2665
2666#endif // QT_NO_DBUS
2667