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(interval: 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(id: 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(sender: watcher.read, signal: &QSocketNotifier::activated, receiver: d, slot: &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(sender: watcher.write, signal: &QSocketNotifier::activated, receiver: d, slot: &QDBusConnectionPrivate::socketWrite);
224 }
225 d->watchers.insert(akey: fd, avalue: 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(akey: 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(it: 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(akey: 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); Q_UNUSED(server);
293 Q_ASSERT(connection);
294 Q_ASSERT(data);
295
296 if (!QDBusConnectionManager::instance())
297 return;
298
299 // keep the connection alive
300 q_dbus_connection_ref(connection);
301 QDBusConnectionPrivate *serverConnection = static_cast<QDBusConnectionPrivate *>(data);
302
303 // allow anonymous authentication
304 if (serverConnection->anonymousAuthenticationAllowed)
305 q_dbus_connection_set_allow_anonymous(connection, value: true);
306
307 QDBusConnectionPrivate *newConnection = new QDBusConnectionPrivate(serverConnection->parent());
308 const auto locker = qt_scoped_lock(mutex&: QDBusConnectionManager::instance()->mutex);
309 QDBusConnectionManager::instance()->setConnection(name: QLatin1String("QDBusServer-") + QString::number(reinterpret_cast<qulonglong>(newConnection), base: 16), c: newConnection);
310 serverConnection->serverConnectionNames << newConnection->name;
311
312 // setPeer does the error handling for us
313 QDBusErrorInternal error;
314 newConnection->setPeer(connection, error);
315 newConnection->setDispatchEnabled(false);
316
317 // this is a queued connection and will resume in the QDBusServer's thread
318 emit serverConnection->newServerConnection(newConnection);
319
320 // we've disabled dispatching of events, so now we post an event to the
321 // QDBusServer's thread in order to enable it after the
322 // QDBusServer::newConnection() signal has been received by the
323 // application's code
324 newConnection->ref.ref();
325 QReadLocker serverLock(&serverConnection->lock);
326 QDBusConnectionDispatchEnabler *o = new QDBusConnectionDispatchEnabler(newConnection);
327 QTimer::singleShot(msec: 0, receiver: o, SLOT(execute()));
328 if (serverConnection->serverObject)
329 o->moveToThread(thread: serverConnection->serverObject->thread());
330}
331
332void QDBusConnectionPrivate::_q_newConnection(QDBusConnectionPrivate *newConnection)
333{
334 Q_ASSERT(mode == ServerMode);
335 emit serverObject->newConnection(connection: QDBusConnectionPrivate::q(connection: newConnection));
336}
337
338} // extern "C"
339
340static QByteArray buildMatchRule(const QString &service,
341 const QString &objectPath, const QString &interface,
342 const QString &member, const QDBusConnectionPrivate::ArgMatchRules &argMatch, const QString & /*signature*/)
343{
344 QString result;
345 result += QLatin1String("type='signal',");
346 const auto keyValue = QLatin1String("%1='%2',");
347
348 if (!service.isEmpty())
349 result += keyValue.arg(args: QLatin1String("sender"), args: service);
350 if (!objectPath.isEmpty())
351 result += keyValue.arg(args: QLatin1String("path"), args: objectPath);
352 if (!interface.isEmpty())
353 result += keyValue.arg(args: QLatin1String("interface"), args: interface);
354 if (!member.isEmpty())
355 result += keyValue.arg(args: QLatin1String("member"), args: member);
356
357 // add the argument string-matching now
358 if (!argMatch.args.isEmpty()) {
359 const QString keyValue = QLatin1String("arg%1='%2',");
360 for (int i = 0; i < argMatch.args.count(); ++i)
361 if (!argMatch.args.at(i).isNull())
362 result += keyValue.arg(a: i).arg(a: argMatch.args.at(i));
363 }
364 if (!argMatch.arg0namespace.isEmpty()) {
365 result += QLatin1String("arg0namespace='%1',").arg(args: argMatch.arg0namespace);
366 }
367
368 result.chop(n: 1); // remove ending comma
369 return result.toLatin1();
370}
371
372static bool findObject(const QDBusConnectionPrivate::ObjectTreeNode *root,
373 const QString &fullpath, int &usedLength,
374 QDBusConnectionPrivate::ObjectTreeNode &result)
375{
376 if (!fullpath.compare(other: QLatin1String("/")) && root->obj) {
377 usedLength = 1;
378 result = *root;
379 return root;
380 }
381 int start = 0;
382 int length = fullpath.length();
383 if (fullpath.at(i: 0) == QLatin1Char('/'))
384 start = 1;
385
386 // walk the object tree
387 QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator node = root;
388 while (start < length && node) {
389 if (node->flags & QDBusConnection::ExportChildObjects)
390 break;
391 if ((node->flags & QDBusConnectionPrivate::VirtualObject) && (node->flags & QDBusConnection::SubPath))
392 break;
393 int end = fullpath.indexOf(c: QLatin1Char('/'), from: start);
394 end = (end == -1 ? length : end);
395 QStringRef pathComponent(&fullpath, start, end - start);
396
397 QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator it =
398 std::lower_bound(first: node->children.constBegin(), last: node->children.constEnd(), val: pathComponent);
399 if (it != node->children.constEnd() && it->name == pathComponent)
400 // match
401 node = it;
402 else
403 node = nullptr;
404
405 start = end + 1;
406 }
407
408 // found our object
409 usedLength = (start > length ? length : start);
410 if (node) {
411 if (node->obj || !node->children.isEmpty())
412 result = *node;
413 else
414 // there really is no object here
415 // we're just looking at an unused space in the QVector
416 node = nullptr;
417 }
418 return node;
419}
420
421static QObject *findChildObject(const QDBusConnectionPrivate::ObjectTreeNode *root,
422 const QString &fullpath, int start)
423{
424 int length = fullpath.length();
425
426 // any object in the tree can tell us to switch to its own object tree:
427 const QDBusConnectionPrivate::ObjectTreeNode *node = root;
428 if (node && node->flags & QDBusConnection::ExportChildObjects) {
429 QObject *obj = node->obj;
430
431 while (obj) {
432 if (start >= length)
433 // we're at the correct level
434 return obj;
435
436 int pos = fullpath.indexOf(c: QLatin1Char('/'), from: start);
437 pos = (pos == -1 ? length : pos);
438 QStringRef pathComponent(&fullpath, start, pos - start);
439
440 const QObjectList children = obj->children();
441
442 // find a child with the proper name
443 QObject *next = nullptr;
444 QObjectList::ConstIterator it = children.constBegin();
445 QObjectList::ConstIterator end = children.constEnd();
446 for ( ; it != end; ++it)
447 if ((*it)->objectName() == pathComponent) {
448 next = *it;
449 break;
450 }
451
452 if (!next)
453 break;
454
455 obj = next;
456 start = pos + 1;
457 }
458 }
459
460 // object not found
461 return nullptr;
462}
463
464static QDBusConnectionPrivate::ArgMatchRules matchArgsForService(const QString &service, QDBusServiceWatcher::WatchMode mode)
465{
466 QDBusConnectionPrivate::ArgMatchRules matchArgs;
467 if (service.endsWith(c: QLatin1Char('*'))) {
468 matchArgs.arg0namespace = service.chopped(n: 1);
469 matchArgs.args << QString();
470 }
471 else
472 matchArgs.args << service;
473
474 switch (mode) {
475 case QDBusServiceWatcher::WatchForOwnerChange:
476 break;
477
478 case QDBusServiceWatcher::WatchForRegistration:
479 matchArgs.args << QString::fromLatin1(str: "", size: 0);
480 break;
481
482 case QDBusServiceWatcher::WatchForUnregistration:
483 matchArgs.args << QString() << QString::fromLatin1(str: "", size: 0);
484 break;
485 }
486 return matchArgs;
487}
488
489
490extern Q_DBUS_EXPORT void qDBusAddSpyHook(QDBusSpyCallEvent::Hook);
491void qDBusAddSpyHook(QDBusSpyCallEvent::Hook hook)
492{
493 qDBusSpyHookList()->append(t: hook);
494}
495
496QDBusSpyCallEvent::~QDBusSpyCallEvent()
497{
498 // Reinsert the message into the processing queue for the connection.
499 // This is done in the destructor so the message is reinserted even if
500 // QCoreApplication is destroyed.
501 QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(const_cast<QObject *>(sender()));
502 qDBusDebug() << d << "message spies done for" << msg;
503 emit d->spyHooksFinished(msg);
504}
505
506void QDBusSpyCallEvent::placeMetaCall(QObject *)
507{
508 invokeSpyHooks(msg, hooks, hookCount);
509}
510
511inline void QDBusSpyCallEvent::invokeSpyHooks(const QDBusMessage &msg, const Hook *hooks, int hookCount)
512{
513 // call the spy hook list
514 for (int i = 0; i < hookCount; ++i)
515 hooks[i](msg);
516}
517
518extern "C" {
519static DBusHandlerResult
520qDBusSignalFilter(DBusConnection *connection, DBusMessage *message, void *data)
521{
522 Q_ASSERT(data);
523 Q_UNUSED(connection);
524 QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
525 if (d->mode == QDBusConnectionPrivate::InvalidMode)
526 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
527
528 QDBusMessage amsg = QDBusMessagePrivate::fromDBusMessage(dmsg: message, capabilities: d->connectionCapabilities());
529 qDBusDebug() << d << "got message (signal):" << amsg;
530
531 return d->handleMessage(msg: amsg) ?
532 DBUS_HANDLER_RESULT_HANDLED :
533 DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
534}
535}
536
537bool QDBusConnectionPrivate::handleMessage(const QDBusMessage &amsg)
538{
539 if (!ref.loadRelaxed())
540 return false;
541
542 // local message are always delivered, regardless of filtering
543 // or whether the dispatcher is enabled
544 bool isLocal = QDBusMessagePrivate::isLocal(msg: amsg);
545
546 if (!dispatchEnabled && !isLocal) {
547 // queue messages only, we'll handle them later
548 qDBusDebug() << this << "delivery is suspended";
549 pendingMessages << amsg;
550 return amsg.type() == QDBusMessage::MethodCallMessage;
551 }
552
553 switch (amsg.type()) {
554 case QDBusMessage::SignalMessage:
555 handleSignal(msg: amsg);
556 // if there are any other filters in this DBusConnection,
557 // let them see the signal too
558 return false;
559 case QDBusMessage::MethodCallMessage:
560 // run it through the spy filters (if any) before the regular processing:
561 // a) if it's a local message, we're in the caller's thread, so invoke the filter directly
562 // b) if it's an external message, post to the main thread
563 if (Q_UNLIKELY(qDBusSpyHookList.exists()) && qApp) {
564 const QDBusSpyHookList &list = *qDBusSpyHookList;
565 if (isLocal) {
566 Q_ASSERT(QThread::currentThread() != thread());
567 qDBusDebug() << this << "invoking message spies directly";
568 QDBusSpyCallEvent::invokeSpyHooks(msg: amsg, hooks: list.constData(), hookCount: list.size());
569 } else {
570 qDBusDebug() << this << "invoking message spies via event";
571 QCoreApplication::postEvent(qApp, event: new QDBusSpyCallEvent(this, QDBusConnection(this),
572 amsg, list.constData(), list.size()));
573
574 // we'll be called back, so return
575 return true;
576 }
577 }
578
579 handleObjectCall(message: amsg);
580 return true;
581 case QDBusMessage::ReplyMessage:
582 case QDBusMessage::ErrorMessage:
583 case QDBusMessage::InvalidMessage:
584 return false; // we don't handle those here
585 }
586
587 return false;
588}
589
590static void huntAndDestroy(QObject *needle, QDBusConnectionPrivate::ObjectTreeNode &haystack)
591{
592 for (auto &node : haystack.children)
593 huntAndDestroy(needle, haystack&: node);
594
595 auto isInactive = [](QDBusConnectionPrivate::ObjectTreeNode &node) { return !node.isActive(); };
596
597 haystack.children.erase(abegin: std::remove_if(first: haystack.children.begin(), last: haystack.children.end(),
598 pred: isInactive),
599 aend: haystack.children.end());
600
601 if (needle == haystack.obj) {
602 haystack.obj = nullptr;
603 haystack.flags = 0;
604 }
605}
606
607static void huntAndUnregister(const QVector<QStringRef> &pathComponents, int i, QDBusConnection::UnregisterMode mode,
608 QDBusConnectionPrivate::ObjectTreeNode *node)
609{
610 if (pathComponents.count() == i) {
611 // found it
612 node->obj = nullptr;
613 node->flags = 0;
614
615 if (mode == QDBusConnection::UnregisterTree) {
616 // clear the sub-tree as well
617 node->children.clear(); // can't disconnect the objects because we really don't know if they can
618 // be found somewhere else in the path too
619 }
620 } else {
621 // keep going
622 QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator end = node->children.end();
623 QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator it =
624 std::lower_bound(first: node->children.begin(), last: end, val: pathComponents.at(i));
625 if (it == end || it->name != pathComponents.at(i))
626 return; // node not found
627
628 huntAndUnregister(pathComponents, i: i + 1, mode, node: it);
629 if (!it->isActive())
630 node->children.erase(pos: it);
631 }
632}
633
634static void huntAndEmit(DBusConnection *connection, DBusMessage *msg,
635 QObject *needle, const QDBusConnectionPrivate::ObjectTreeNode &haystack,
636 bool isScriptable, bool isAdaptor, const QString &path = QString())
637{
638 QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator it = haystack.children.constBegin();
639 QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator end = haystack.children.constEnd();
640 for ( ; it != end; ++it) {
641 if (it->isActive())
642 huntAndEmit(connection, msg, needle, haystack: *it, isScriptable, isAdaptor, path: path + QLatin1Char('/') + it->name);
643 }
644
645 if (needle == haystack.obj) {
646 // is this a signal we should relay?
647 if (isAdaptor && (haystack.flags & QDBusConnection::ExportAdaptors) == 0)
648 return; // no: it comes from an adaptor and we're not exporting adaptors
649 else if (!isAdaptor) {
650 int mask = isScriptable
651 ? QDBusConnection::ExportScriptableSignals
652 : QDBusConnection::ExportNonScriptableSignals;
653 if ((haystack.flags & mask) == 0)
654 return; // signal was not exported
655 }
656
657 QByteArray p = path.toLatin1();
658 if (p.isEmpty())
659 p = "/";
660 qDBusDebug() << QThread::currentThread() << "emitting signal at" << p;
661 DBusMessage *msg2 = q_dbus_message_copy(message: msg);
662 q_dbus_message_set_path(message: msg2, object_path: p);
663 q_dbus_connection_send(connection, message: msg2, client_serial: nullptr);
664 q_dbus_message_unref(message: msg2);
665 }
666}
667
668static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags,
669 const QString &signature_, QVector<int> &metaTypes)
670{
671 QByteArray msgSignature = signature_.toLatin1();
672
673 for (int idx = mo->methodCount() - 1 ; idx >= QObject::staticMetaObject.methodCount(); --idx) {
674 QMetaMethod mm = mo->method(index: idx);
675
676 // check access:
677 if (mm.access() != QMetaMethod::Public)
678 continue;
679
680 // check type:
681 if (mm.methodType() != QMetaMethod::Slot && mm.methodType() != QMetaMethod::Method)
682 continue;
683
684 // check name:
685 if (mm.name() != name)
686 continue;
687
688 int returnType = mm.returnType();
689 bool isAsync = qDBusCheckAsyncTag(tag: mm.tag());
690 bool isScriptable = mm.attributes() & QMetaMethod::Scriptable;
691
692 // consistency check:
693 if (isAsync && returnType != QMetaType::Void)
694 continue;
695
696 QString errorMsg;
697 int inputCount = qDBusParametersForMethod(mm, metaTypes, errorMsg);
698 if (inputCount == -1)
699 continue; // problem parsing
700
701 metaTypes[0] = returnType;
702 bool hasMessage = false;
703 if (inputCount > 0 &&
704 metaTypes.at(i: inputCount) == QDBusMetaTypeId::message()) {
705 // "no input parameters" is allowed as long as the message meta type is there
706 hasMessage = true;
707 --inputCount;
708 }
709
710 // try to match the parameters
711 int i;
712 QByteArray reconstructedSignature;
713 for (i = 1; i <= inputCount; ++i) {
714 const char *typeSignature = QDBusMetaType::typeToSignature( type: metaTypes.at(i) );
715 if (!typeSignature)
716 break; // invalid
717
718 reconstructedSignature += typeSignature;
719 if (!msgSignature.startsWith(a: reconstructedSignature))
720 break;
721 }
722
723 if (reconstructedSignature != msgSignature)
724 continue; // we didn't match them all
725
726 if (hasMessage)
727 ++i;
728
729 // make sure that the output parameters have signatures too
730 if (returnType != QMetaType::UnknownType && returnType != QMetaType::Void && QDBusMetaType::typeToSignature(type: returnType) == nullptr)
731 continue;
732
733 bool ok = true;
734 for (int j = i; ok && j < metaTypes.count(); ++j)
735 if (QDBusMetaType::typeToSignature(type: metaTypes.at(i)) == nullptr)
736 ok = false;
737 if (!ok)
738 continue;
739
740 // consistency check:
741 if (isAsync && metaTypes.count() > i + 1)
742 continue;
743
744 if (mm.methodType() == QMetaMethod::Slot) {
745 if (isScriptable && (flags & QDBusConnection::ExportScriptableSlots) == 0)
746 continue; // scriptable slots not exported
747 if (!isScriptable && (flags & QDBusConnection::ExportNonScriptableSlots) == 0)
748 continue; // non-scriptable slots not exported
749 } else {
750 if (isScriptable && (flags & QDBusConnection::ExportScriptableInvokables) == 0)
751 continue; // scriptable invokables not exported
752 if (!isScriptable && (flags & QDBusConnection::ExportNonScriptableInvokables) == 0)
753 continue; // non-scriptable invokables not exported
754 }
755
756 // if we got here, this slot matched
757 return idx;
758 }
759
760 // no slot matched
761 return -1;
762}
763
764/*!
765 \internal
766 Enables or disables the delivery of incoming method calls and signals. If
767 \a enable is true, this will also cause any queued, pending messages to be
768 delivered.
769 */
770void QDBusConnectionPrivate::setDispatchEnabled(bool enable)
771{
772 checkThread();
773 dispatchEnabled = enable;
774 if (enable)
775 emit dispatchStatusChanged();
776}
777
778static QDBusCallDeliveryEvent * const DIRECT_DELIVERY = (QDBusCallDeliveryEvent *)1;
779
780QDBusCallDeliveryEvent* QDBusConnectionPrivate::prepareReply(QDBusConnectionPrivate *target,
781 QObject *object, int idx,
782 const QVector<int> &metaTypes,
783 const QDBusMessage &msg)
784{
785 Q_ASSERT(object);
786 Q_UNUSED(object);
787
788 int n = metaTypes.count() - 1;
789 if (metaTypes[n] == QDBusMetaTypeId::message())
790 --n;
791
792 if (msg.arguments().count() < n)
793 return nullptr; // too few arguments
794
795 // check that types match
796 for (int i = 0; i < n; ++i)
797 if (metaTypes.at(i: i + 1) != msg.arguments().at(i).userType() &&
798 msg.arguments().at(i).userType() != qMetaTypeId<QDBusArgument>())
799 return nullptr; // no match
800
801 // we can deliver
802 // prepare for the call
803 if (target == object)
804 return DIRECT_DELIVERY;
805 return new QDBusCallDeliveryEvent(QDBusConnection(target), idx, target, msg, metaTypes);
806}
807
808void QDBusConnectionPrivate::activateSignal(const QDBusConnectionPrivate::SignalHook& hook,
809 const QDBusMessage &msg)
810{
811 // This is called by QDBusConnectionPrivate::handleSignal to deliver a signal
812 // that was received from D-Bus
813 //
814 // Signals are delivered to slots if the parameters match
815 // Slots can have less parameters than there are on the message
816 // Slots can optionally have one final parameter that is a QDBusMessage
817 // Slots receive read-only copies of the message (i.e., pass by value or by const-ref)
818 QDBusCallDeliveryEvent *call = prepareReply(target: this, object: hook.obj, idx: hook.midx, metaTypes: hook.params, msg);
819 if (call == DIRECT_DELIVERY) {
820 // short-circuit delivery
821 Q_ASSERT(this == hook.obj);
822 deliverCall(object: this, flags: 0, msg, metaTypes: hook.params, slotIdx: hook.midx);
823 return;
824 }
825 if (call)
826 postEventToThread(action: ActivateSignalAction, target: hook.obj, event: call);
827}
828
829bool QDBusConnectionPrivate::activateCall(QObject* object, int flags, const QDBusMessage &msg)
830{
831 // This is called by QDBusConnectionPrivate::handleObjectCall to place a call
832 // to a slot on the object.
833 //
834 // The call is delivered to the first slot that matches the following conditions:
835 // - has the same name as the message's target member
836 // - ALL of the message's types are found in slot's parameter list
837 // - optionally has one more parameter of type QDBusMessage
838 // If none match, then the slot of the same name as the message target and with
839 // the first type of QDBusMessage is delivered.
840 //
841 // The D-Bus specification requires that all MethodCall messages be replied to, unless the
842 // caller specifically waived this requirement. This means that we inspect if the user slot
843 // generated a reply and, if it didn't, we will. Obviously, if the user slot doesn't take a
844 // QDBusMessage parameter, it cannot generate a reply.
845 //
846 // When a return message is generated, the slot's return type, if any, will be placed
847 // in the message's first position. If there are non-const reference parameters to the
848 // slot, they must appear at the end and will be placed in the subsequent message
849 // positions.
850
851 static const char cachePropertyName[] = "_qdbus_slotCache";
852
853 if (!object)
854 return false;
855
856 Q_ASSERT_X(QThread::currentThread() == object->thread(),
857 "QDBusConnection: internal threading error",
858 "function called for an object that is in another thread!!");
859
860 QDBusSlotCache slotCache =
861 qvariant_cast<QDBusSlotCache>(v: object->property(name: cachePropertyName));
862 QString cacheKey = msg.member(), signature = msg.signature();
863 if (!signature.isEmpty()) {
864 cacheKey.reserve(asize: cacheKey.length() + 1 + signature.length());
865 cacheKey += QLatin1Char('.');
866 cacheKey += signature;
867 }
868
869 QDBusSlotCache::Hash::ConstIterator cacheIt = slotCache.hash.constFind(akey: cacheKey);
870 while (cacheIt != slotCache.hash.constEnd() && cacheIt->flags != flags &&
871 cacheIt.key() == cacheKey)
872 ++cacheIt;
873 if (cacheIt == slotCache.hash.constEnd() || cacheIt.key() != cacheKey)
874 {
875 // not cached, analyze the meta object
876 const QMetaObject *mo = object->metaObject();
877 QByteArray memberName = msg.member().toUtf8();
878
879 // find a slot that matches according to the rules above
880 QDBusSlotCache::Data slotData;
881 slotData.flags = flags;
882 slotData.slotIdx = ::findSlot(mo, name: memberName, flags, signature_: msg.signature(), metaTypes&: slotData.metaTypes);
883 if (slotData.slotIdx == -1) {
884 // ### this is where we want to add the connection as an arg too
885 // try with no parameters, but with a QDBusMessage
886 slotData.slotIdx = ::findSlot(mo, name: memberName, flags, signature_: QString(), metaTypes&: slotData.metaTypes);
887 if (slotData.metaTypes.count() != 2 ||
888 slotData.metaTypes.at(i: 1) != QDBusMetaTypeId::message()) {
889 // not found
890 // save the negative lookup
891 slotData.slotIdx = -1;
892 slotData.metaTypes.clear();
893 slotCache.hash.insert(akey: cacheKey, avalue: slotData);
894 object->setProperty(name: cachePropertyName, value: QVariant::fromValue(value: slotCache));
895 return false;
896 }
897 }
898
899 // save to the cache
900 slotCache.hash.insert(akey: cacheKey, avalue: slotData);
901 object->setProperty(name: cachePropertyName, value: QVariant::fromValue(value: slotCache));
902
903 // found the slot to be called
904 deliverCall(object, flags, msg, metaTypes: slotData.metaTypes, slotIdx: slotData.slotIdx);
905 return true;
906 } else if (cacheIt->slotIdx == -1) {
907 // negative cache
908 return false;
909 } else {
910 // use the cache
911 deliverCall(object, flags, msg, metaTypes: cacheIt->metaTypes, slotIdx: cacheIt->slotIdx);
912 return true;
913 }
914 return false;
915}
916
917void QDBusConnectionPrivate::deliverCall(QObject *object, int /*flags*/, const QDBusMessage &msg,
918 const QVector<int> &metaTypes, int slotIdx)
919{
920 Q_ASSERT_X(!object || QThread::currentThread() == object->thread(),
921 "QDBusConnection: internal threading error",
922 "function called for an object that is in another thread!!");
923
924 QVarLengthArray<void *, 10> params;
925 params.reserve(asize: metaTypes.count());
926
927 QVariantList auxParameters;
928 // let's create the parameter list
929
930 // first one is the return type -- add it below
931 params.append(t: 0);
932
933 // add the input parameters
934 int i;
935 int pCount = qMin(a: msg.arguments().count(), b: metaTypes.count() - 1);
936 for (i = 1; i <= pCount; ++i) {
937 int id = metaTypes[i];
938 if (id == QDBusMetaTypeId::message())
939 break;
940
941 const QVariant &arg = msg.arguments().at(i: i - 1);
942 if (arg.userType() == id)
943 // no conversion needed
944 params.append(t: const_cast<void *>(arg.constData()));
945 else if (arg.userType() == qMetaTypeId<QDBusArgument>()) {
946 // convert to what the function expects
947 void *null = nullptr;
948 auxParameters.append(t: QVariant(id, null));
949
950 const QDBusArgument &in =
951 *reinterpret_cast<const QDBusArgument *>(arg.constData());
952 QVariant &out = auxParameters[auxParameters.count() - 1];
953
954 if (Q_UNLIKELY(!QDBusMetaType::demarshall(in, out.userType(), out.data())))
955 qFatal(msg: "Internal error: demarshalling function for type '%s' (%d) failed!",
956 out.typeName(), out.userType());
957
958 params.append(t: const_cast<void *>(out.constData()));
959 } else {
960 qFatal(msg: "Internal error: got invalid meta type %d (%s) "
961 "when trying to convert to meta type %d (%s)",
962 arg.userType(), QMetaType::typeName(type: arg.userType()),
963 id, QMetaType::typeName(type: id));
964 }
965 }
966
967 if (metaTypes.count() > i && metaTypes[i] == QDBusMetaTypeId::message()) {
968 params.append(t: const_cast<void*>(static_cast<const void*>(&msg)));
969 ++i;
970 }
971
972 // output arguments
973 const int numMetaTypes = metaTypes.count();
974 QVariantList outputArgs;
975 void *null = nullptr;
976 if (metaTypes[0] != QMetaType::Void && metaTypes[0] != QMetaType::UnknownType) {
977 outputArgs.reserve(alloc: numMetaTypes - i + 1);
978 QVariant arg(metaTypes[0], null);
979 outputArgs.append( t: arg );
980 params[0] = const_cast<void*>(outputArgs.at( i: outputArgs.count() - 1 ).constData());
981 } else {
982 outputArgs.reserve(alloc: numMetaTypes - i);
983 }
984
985 for ( ; i < numMetaTypes; ++i) {
986 QVariant arg(metaTypes[i], null);
987 outputArgs.append( t: arg );
988 params.append(t: const_cast<void*>(outputArgs.at( i: 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(obj: object, newContext: &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(obj: object, newContext: 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(message: msg.createReply(arguments: outputArgs));
1015 } else {
1016 // generate internal error
1017 qWarning(msg: "Internal error: Failed to deliver message");
1018 send(message: msg.createErrorReply(type: QDBusError::InternalError,
1019 msg: 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(varName: "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(sender: this, signal: &QDBusConnectionPrivate::dispatchStatusChanged,
1051 receiver: this, slot: &QDBusConnectionPrivate::doDispatch, type: Qt::QueuedConnection);
1052 connect(sender: this, signal: &QDBusConnectionPrivate::spyHooksFinished,
1053 receiver: this, slot: &QDBusConnectionPrivate::handleObjectCall, type: Qt::QueuedConnection);
1054 connect(sender: this, signal: &QDBusConnectionPrivate::messageNeedsSending,
1055 receiver: this, slot: &QDBusConnectionPrivate::sendInternal);
1056 connect(sender: this, signal: &QDBusConnectionPrivate::signalNeedsConnecting,
1057 receiver: this, slot: &QDBusConnectionPrivate::addSignalHook, type: Qt::BlockingQueuedConnection);
1058 connect(sender: this, signal: &QDBusConnectionPrivate::signalNeedsDisconnecting,
1059 receiver: this, slot: &QDBusConnectionPrivate::removeSignalHook, type: 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(akey: QDBusUtil::dbusService(), avalue: WatchedServiceData(QDBusUtil::dbusService(), 1));
1066
1067 // prepopulate matchRefCounts:
1068 // we know that org.freedesktop.DBus will never change owners
1069 matchRefCounts.insert(akey: "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='org.freedesktop.DBus'", avalue: 1);
1070}
1071
1072QDBusConnectionPrivate::~QDBusConnectionPrivate()
1073{
1074 if (thread() && thread() != QThread::currentThread())
1075 qWarning(msg: "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(c: 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(sender: obj, signal: nullptr, receiver: this, member: nullptr);
1090 delete obj;
1091 }
1092 if (connection)
1093 q_dbus_connection_unref(connection: connection);
1094 connection = nullptr;
1095 } else if (lastMode == ServerMode) {
1096 if (server)
1097 q_dbus_server_unref(server: 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(haystack&: *it, set);
1109 it++;
1110 }
1111
1112 if (haystack.obj)
1113 set.insert(value: 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: server);
1126 q_dbus_server_free_data_slot(slot_p: &server_slot);
1127 }
1128
1129 if (oldMode == ClientMode || oldMode == PeerMode) {
1130 if (connection) {
1131 q_dbus_connection_close(connection: connection);
1132 // send the "close" message
1133 while (q_dbus_connection_dispatch(connection: connection) == DBUS_DISPATCH_DATA_REMAINS)
1134 ;
1135 }
1136 }
1137
1138 qDeleteAll(c: 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(haystack&: rootNode, set&: allObjects);
1147 SignalHookHash::const_iterator sit = signalHooks.constBegin();
1148 while (sit != signalHooks.constEnd()) {
1149 allObjects.insert(value: 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(receiver: this);
1157 ++oit;
1158 }
1159}
1160
1161void QDBusConnectionPrivate::handleDBusDisconnection()
1162{
1163 while (!pendingCalls.isEmpty())
1164 processFinishedCall(call: 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(akey: e->timerId(), adefaultValue: 0);
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: 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(amsg: std::move(*it));
1206 }
1207 pendingMessages.clear();
1208 }
1209 }
1210}
1211
1212void QDBusConnectionPrivate::socketRead(qintptr fd)
1213{
1214 WatcherHash::ConstIterator it = watchers.constFind(akey: fd);
1215 while (it != watchers.constEnd() && it.key() == fd) {
1216 if (it->watch && it->read && it->read->isEnabled()) {
1217 if (!q_dbus_watch_handle(watch: it.value().watch, flags: DBUS_WATCH_READABLE))
1218 qDebug(msg: "OUT OF MEM");
1219 break;
1220 }
1221 ++it;
1222 }
1223 if ((mode == ClientMode || mode == PeerMode) && !isAuthenticated
1224 && q_dbus_connection_get_is_authenticated(connection: connection))
1225 handleAuthentication();
1226 doDispatch();
1227}
1228
1229void QDBusConnectionPrivate::socketWrite(qintptr fd)
1230{
1231 WatcherHash::ConstIterator it = watchers.constFind(akey: fd);
1232 while (it != watchers.constEnd() && it.key() == fd) {
1233 if (it->watch && it->write && it->write->isEnabled()) {
1234 if (!q_dbus_watch_handle(watch: it.value().watch, flags: DBUS_WATCH_WRITABLE))
1235 qDebug(msg: "OUT OF MEM");
1236 break;
1237 }
1238 ++it;
1239 }
1240 if ((mode == ClientMode || mode == PeerMode) && !isAuthenticated
1241 && q_dbus_connection_get_is_authenticated(connection: connection))
1242 handleAuthentication();
1243}
1244
1245void QDBusConnectionPrivate::objectDestroyed(QObject *obj)
1246{
1247 QDBusWriteLocker locker(ObjectDestroyedAction, this);
1248 huntAndDestroy(needle: obj, haystack&: 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(it: sit);
1254 else
1255 ++sit;
1256 }
1257
1258 obj->disconnect(receiver: 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(index: 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(path: QLatin1String("/"), interface,
1281 name: QLatin1String(memberName));
1282 QDBusMessagePrivate::setParametersValidated(msg&: message, enable: true);
1283 message.setArguments(args);
1284 QDBusError error;
1285 DBusMessage *msg =
1286 QDBusMessagePrivate::toDBusMessage(message, capabilities: connectionCapabilities(), error: &error);
1287 if (!msg) {
1288 qWarning(msg: "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(message: msg, no_reply: true); // the reply would not be delivered to anything
1297 huntAndEmit(connection: connection, msg, needle: obj, haystack: rootNode, isScriptable, isAdaptor);
1298 q_dbus_message_unref(message: 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(akey: name);
1307 if (it == watchedServices.end())
1308 return;
1309 if (oldOwner != it->owner)
1310 qWarning(msg: "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 QVector<int> &params)
1319{
1320 int midx = obj->metaObject()->indexOfMethod(method: normalizedName);
1321 if (midx == -1)
1322 return -1;
1323
1324 QString errorMsg;
1325 int inputCount = qDBusParametersForMethod(mm: obj->metaObject()->method(index: midx), metaTypes&: 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(obj: receiver, normalizedName: signal + 1, params&: hook.params);
1341 if (hook.midx == -1) {
1342 normalizedName = QMetaObject::normalizedSignature(method: signal + 1);
1343 hook.midx = findSlot(obj: receiver, normalizedName, params&: 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(pos: normalizedName.indexOf(c: '('));
1360 mname = QString::fromUtf8(str: normalizedName);
1361 }
1362 key = mname;
1363 key.reserve(asize: 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( type: hook.params.at(i) ) );
1372 }
1373
1374 hook.matchRule = buildMatchRule(service, objectPath: path, interface, member: 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(args: msg.interface());
1386
1387 send(message: msg.createErrorReply(type: code,
1388 msg: QLatin1String("No such method '%1' in %2 at object path '%3' "
1389 "(signature '%4')")
1390 .arg(args: msg.member(), args&: interfaceMsg, args: msg.path(), args: msg.signature())));
1391 } else if (code == QDBusError::UnknownInterface) {
1392 send(message: msg.createErrorReply(type: QDBusError::UnknownInterface,
1393 msg: QLatin1String("No such interface '%1' at object path '%2'")
1394 .arg(args: msg.interface(), args: msg.path())));
1395 } else if (code == QDBusError::UnknownObject) {
1396 send(message: msg.createErrorReply(type: QDBusError::UnknownObject,
1397 msg: QLatin1String("No such object path '%1'").arg(args: 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(argument: qDBusIntrospectObject(node, path: msg.path()));
1411 send(message: reply);
1412 return true;
1413 }
1414
1415 if (!interface.isEmpty()) {
1416 sendError(msg, code: 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(message: reply);
1427 return true;
1428 } else if (msg.member() == QLatin1String("Set") && msg.signature() == QLatin1String("ssv")) {
1429 QDBusMessage reply = qDBusPropertySet(node, msg);
1430 send(message: reply);
1431 return true;
1432 } else if (msg.member() == QLatin1String("GetAll") && msg.signature() == QLatin1String("s")) {
1433 QDBusMessage reply = qDBusPropertyGetAll(node, msg);
1434 send(message: reply);
1435 return true;
1436 }
1437
1438 if (!interface.isEmpty()) {
1439 sendError(msg, code: 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(message: msg, connection: q(connection: 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(root: &node, fullpath: msg.path(), start: pathStartPos);
1469 if (!node.obj) {
1470 sendError(msg, code: QDBusError::UnknownObject);
1471 return;
1472 }
1473 }
1474
1475 QDBusAdaptorConnector *connector;
1476 if (node.flags & QDBusConnection::ExportAdaptors &&
1477 (connector = qDBusFindAdaptorConnector(object: 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(object: it->adaptor, flags: 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(first: connector->adaptors.constBegin(), last: connector->adaptors.constEnd(),
1495 val: msg.interface());
1496 if (it != connector->adaptors.constEnd() && msg.interface() == QLatin1String(it->interface)) {
1497 if (!activateCall(object: it->adaptor, flags: newflags, msg))
1498 sendError(msg, code: 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(obj: node.obj, interface_name: msg.interface());
1518 }
1519
1520 if (interfaceFound) {
1521 if (!activateCall(object: node.obj, flags: node.flags, msg))
1522 sendError(msg, code: QDBusError::UnknownMethod);
1523 return;
1524 }
1525 }
1526
1527 // nothing matched, send an error code
1528 if (msg.interface().isEmpty())
1529 sendError(msg, code: QDBusError::UnknownMethod);
1530 else
1531 sendError(msg, code: 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(root: &rootNode, fullpath: msg.path(), usedLength, result)) {
1552 // qDebug("Call failed: no object found at %s", qPrintable(msg.path()));
1553 sendError(msg, code: 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(node&: result, msg, pathStartPos: usedLength);
1561 return;
1562 }
1563
1564 objThread = result.obj->thread();
1565 if (!objThread) {
1566 send(message: msg.createErrorReply(type: QDBusError::InternalError,
1567 msg: QLatin1String("Object '%1' (at path '%2')"
1568 " has no thread. Cannot deliver message.")
1569 .arg(args: result.obj->objectName(), args: msg.path())));
1570 return;
1571 }
1572
1573 if (!QDBusMessagePrivate::isLocal(msg)) {
1574 // external incoming message
1575 // post it and forget
1576 postEventToThread(action: HandleObjectCallPostEventAction, target: result.obj,
1577 event: 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(action: HandleObjectCallPostEventAction, target: result.obj,
1584 event: 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(node&: result, msg, pathStartPos: 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(q: connection);
1605 that->sendError(msg: message, code: QDBusError::UnknownObject);
1606 }
1607
1608 // semaphore releasing happens in ~QMetaCallEvent
1609}
1610
1611void QDBusActivateObjectEvent::placeMetaCall(QObject *)
1612{
1613 QDBusConnectionPrivate *that = QDBusConnectionPrivate::d(q: connection);
1614
1615 QDBusLockerBase::reportThreadAction(action: HandleObjectCallPostEventAction,
1616 condition: QDBusLockerBase::BeforeDeliver, ptr: that);
1617 that->activateObject(node, msg: message, pathStartPos);
1618 QDBusLockerBase::reportThreadAction(action: HandleObjectCallPostEventAction,
1619 condition: QDBusLockerBase::AfterDeliver, ptr: that);
1620
1621 handled = true;
1622}
1623
1624void QDBusConnectionPrivate::handleSignal(const QString &key, const QDBusMessage& msg)
1625{
1626 SignalHookHash::const_iterator it = signalHooks.constFind(akey: 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(akey: hook.service, adefaultValue: 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(i: 0).toString();
1666 if (param != hook.argumentMatch.arg0namespace
1667 && !param.startsWith(s: 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(asize: 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(pos: 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::Void;
1707 hook.midx = staticMetaObject.indexOfSlot(slot: "handleDBusDisconnection()");
1708 Q_ASSERT(hook.midx != -1);
1709 signalHooks.insert(akey: QLatin1String("Disconnected:" DBUS_INTERFACE_LOCAL), avalue: 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(slot_p: &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: server,
1729 add_function: qDBusAddWatch,
1730 remove_function: qDBusRemoveWatch,
1731 toggled_function: qDBusToggleWatch,
1732 data: this, free_data_function: 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: server,
1737 add_function: qDBusAddTimeout,
1738 remove_function: qDBusRemoveTimeout,
1739 toggled_function: qDBusToggleTimeout,
1740 data: this, free_data_function: 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: server, function: qDBusNewConnection, data: this, free_data_function: nullptr);
1745
1746 dbus_bool_t data_set = q_dbus_server_set_data(server: server, slot: server_slot, data: this, free_data_func: 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: connection, exit_on_disconnect: false);
1762 q_dbus_connection_set_watch_functions(connection: connection,
1763 add_function: qDBusAddWatch,
1764 remove_function: qDBusRemoveWatch,
1765 toggled_function: qDBusToggleWatch,
1766 data: this, free_data_function: nullptr);
1767 q_dbus_connection_set_timeout_functions(connection: connection,
1768 add_function: qDBusAddTimeout,
1769 remove_function: qDBusRemoveTimeout,
1770 toggled_function: qDBusToggleTimeout,
1771 data: this, free_data_function: nullptr);
1772 q_dbus_connection_set_dispatch_status_function(connection: connection, function: qDBusUpdateDispatchStatus, data: this, free_data_function: nullptr);
1773 q_dbus_connection_add_filter(connection: connection,
1774 function: qDBusSignalFilter,
1775 user_data: this, free_data_function: nullptr);
1776
1777 watchForDBusDisconnection();
1778
1779 QMetaObject::invokeMethod(obj: this, member: "doDispatch", type: 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(newValue: connectionCapabilies(connection: 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: connection);
1823 Q_ASSERT(service);
1824 baseService = QString::fromUtf8(str: 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: connection, exit_on_disconnect: false);
1829 q_dbus_connection_set_watch_functions(connection: connection, add_function: qDBusAddWatch, remove_function: qDBusRemoveWatch,
1830 toggled_function: qDBusToggleWatch, data: this, free_data_function: nullptr);
1831 q_dbus_connection_set_timeout_functions(connection: connection, add_function: qDBusAddTimeout, remove_function: qDBusRemoveTimeout,
1832 toggled_function: qDBusToggleTimeout, data: this, free_data_function: nullptr);
1833 q_dbus_connection_set_dispatch_status_function(connection: connection, function: qDBusUpdateDispatchStatus, data: this, free_data_function: nullptr);
1834 q_dbus_connection_add_filter(connection: connection, function: qDBusSignalFilter, user_data: this, free_data_function: nullptr);
1835
1836 // Initialize the hooks for the NameAcquired and NameLost signals
1837 // we don't use connectSignal here because we don't need the rules to be sent to the bus
1838 // the bus will always send us these two signals
1839 SignalHook hook;
1840 hook.service = QDBusUtil::dbusService();
1841 hook.path.clear(); // no matching
1842 hook.obj = this;
1843 hook.params << QMetaType::Void << QMetaType::QString; // both functions take a QString as parameter and return void
1844
1845 hook.midx = staticMetaObject.indexOfSlot(slot: "registerServiceNoLock(QString)");
1846 Q_ASSERT(hook.midx != -1);
1847 signalHooks.insert(akey: QLatin1String("NameAcquired:" DBUS_INTERFACE_DBUS), avalue: hook);
1848
1849 hook.midx = staticMetaObject.indexOfSlot(slot: "unregisterServiceNoLock(QString)");
1850 Q_ASSERT(hook.midx != -1);
1851 signalHooks.insert(akey: QLatin1String("NameLost:" DBUS_INTERFACE_DBUS), avalue: 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(asize: 4);
1857 hook.params << QMetaType::Void << QMetaType::QString << QMetaType::QString << QMetaType::QString;
1858 hook.midx = staticMetaObject.indexOfSlot(slot: "serviceOwnerChangedNoLock(QString,QString,QString)");
1859 Q_ASSERT(hook.midx != -1);
1860 signalHooks.insert(akey: QLatin1String("NameOwnerChanged:" DBUS_INTERFACE_DBUS), avalue: hook);
1861
1862 watchForDBusDisconnection();
1863
1864 qDBusDebug() << this << ": connected successfully";
1865
1866 // schedule a dispatch:
1867 QMetaObject::invokeMethod(obj: this, member: "doDispatch", type: 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(mutex&: call->mutex);
1885
1886 connection->pendingCalls.removeOne(t: 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(pending: call->pending)) {
1893 // decode the message
1894 DBusMessage *reply = q_dbus_pending_call_steal_reply(pending: call->pending);
1895 msg = QDBusMessagePrivate::fromDBusMessage(dmsg: reply, capabilities: connection->connectionCapabilities());
1896 q_dbus_message_unref(message: reply);
1897 } else {
1898 msg = QDBusMessage::createError(type: QDBusError::Disconnected, msg: 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(target: connection, object: call->receiver, idx: call->methodIdx,
1916 metaTypes: call->metaTypes, msg);
1917 if (e)
1918 connection->postEventToThread(action: MessageResultReceivedAction, target: call->receiver, event: e);
1919 else
1920 qDBusDebug(msg: "Deliver failed!");
1921 }
1922
1923 if (call->pending) {
1924 q_dbus_pending_call_unref(pending: call->pending);
1925 call->pending = nullptr;
1926 }
1927
1928 // Are there any watchers?
1929 if (call->watcherHelper)
1930 call->watcherHelper->emitSignals(replyMessage: msg, sentMessage: call->sentMessage);
1931
1932 call->waitForFinishedCondition.wakeAll();
1933 locker.unlock();
1934
1935 if (msg.type() == QDBusMessage::ErrorMessage)
1936 emit connection->callWithCallbackFailed(error: QDBusError(msg), message: call->sentMessage);
1937
1938 if (!call->ref.deref())
1939 delete call;
1940}
1941
1942bool QDBusConnectionPrivate::send(const QDBusMessage& message)
1943{
1944 if (QDBusMessagePrivate::isLocal(msg: 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, capabilities: connectionCapabilities(), error: &error);
1951 if (!msg) {
1952 if (message.type() == QDBusMessage::MethodCallMessage)
1953 qWarning(msg: "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(msg: "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(msg: "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(message: msg, no_reply: true); // the reply would not be delivered to anything
1974 qDBusDebug() << this << "sending message (no reply):" << message;
1975 emit messageNeedsSending(pcall: 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(mutex&: initializeMutex);
2003
2004 if (!initializedAmounts) {
2005 int tmp = 0;
2006 QByteArray env;
2007 bool ok = true;
2008
2009 env = qgetenv(varName: "Q_DBUS_BLOCKING_CALL_MAIN_THREAD_WARNING_MS");
2010 if (!env.isEmpty()) {
2011 tmp = env.toInt(ok: &ok);
2012 if (ok)
2013 mainThreadWarningAmount = tmp;
2014 else
2015 qWarning(msg: "QDBusBlockingCallWatcher: Q_DBUS_BLOCKING_CALL_MAIN_THREAD_WARNING_MS must be an integer; value ignored");
2016 }
2017
2018 env = qgetenv(varName: "Q_DBUS_BLOCKING_CALL_OTHER_THREAD_WARNING_MS");
2019 if (!env.isEmpty()) {
2020 tmp = env.toInt(ok: &ok);
2021 if (ok)
2022 otherThreadWarningAmount = tmp;
2023 else
2024 qWarning(msg: "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(msg: "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, receiver: nullptr, returnMethod: nullptr, errorMethod: 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(sender: pcall->watcherHelper, signal: &QDBusPendingCallWatcherHelper::reply, receiver: &loop, slot: &QEventLoop::quit);
2077 loop.connect(sender: pcall->watcherHelper, signal: &QDBusPendingCallWatcherHelper::error, receiver: &loop, slot: &QEventLoop::quit);
2078
2079 // enter the event loop and wait for a reply
2080 loop.exec(flags: 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(conn: *this, asSent: message);
2099 bool handled = handleMessage(amsg: localCallMsg);
2100
2101 if (!handled) {
2102 QString interface = message.interface();
2103 if (interface.isEmpty())
2104 interface = QLatin1String("<no-interface>");
2105 return QDBusMessage::createError(type: QDBusError::InternalError,
2106 msg: QLatin1String("Internal error trying to call %1.%2 at %3 (signature '%4'")
2107 .arg(args&: interface, args: message.member(),
2108 args: message.path(), args: message.signature()));
2109 }
2110
2111 // if the message was handled, there might be a reply
2112 QDBusMessage localReplyMsg = QDBusMessagePrivate::makeLocalReply(conn: *this, asSent: localCallMsg);
2113 if (localReplyMsg.type() == QDBusMessage::InvalidMessage) {
2114 qWarning(msg: "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 err: 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(serviceName: message.service()))) {
2134 // special case for local calls
2135 pcall->replyMessage = sendWithReplyLocal(message);
2136 }
2137
2138 if (receiver && returnMethod)
2139 pcall->setReplyCallback(target: receiver, member: returnMethod);
2140
2141 if (errorMethod) {
2142 pcall->watcherHelper = new QDBusPendingCallWatcherHelper;
2143 connect(sender: pcall->watcherHelper, SIGNAL(error(QDBusError,QDBusMessage)), receiver, member: errorMethod,
2144 Qt::QueuedConnection);
2145 pcall->watcherHelper->moveToThread(thread: thread());
2146 }
2147
2148 if ((receiver && returnMethod) || errorMethod) {
2149 // no one waiting, will delete pcall in processFinishedCall()
2150 pcall->ref.storeRelaxed(newValue: 1);
2151 } else {
2152 // set double ref to prevent race between processFinishedCall() and ref counting
2153 // by QDBusPendingCall::QExplicitlySharedDataPointer<QDBusPendingCallPrivate>
2154 pcall->ref.storeRelaxed(newValue: 2);
2155 }
2156
2157 if (isLoopback) {
2158 // a loopback call
2159 processFinishedCall(call: pcall);
2160 return pcall;
2161 }
2162
2163 QDBusError error;
2164 DBusMessage *msg =
2165 QDBusMessagePrivate::toDBusMessage(message, capabilities: connectionCapabilities(), error: &error);
2166 if (!msg) {
2167 qWarning(msg: "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(err: error);
2172 lastError = error;
2173 processFinishedCall(call: 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: connection, message: msg, client_serial: nullptr)) {
2192 // success
2193 } else if (!isNoReply && q_dbus_connection_send_with_reply(connection: connection, message: msg, pending_return: &pending, timeout_milliseconds: timeout)) {
2194 if (pending) {
2195 q_dbus_message_unref(message: msg);
2196
2197 pcall->pending = pending;
2198 q_dbus_pending_call_set_notify(pending, function: qDBusResultReceived, user_data: pcall, free_user_data: 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(t: 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(message: msg);
2214 if (pcall) {
2215 pcall->replyMessage = QDBusMessage::createError(err: error);
2216 processFinishedCall(call: 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, argumentMatch: 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, argMatch: argumentMatch, receiver, signal: slot, minMIdx: 0, buildSignature: 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(akey: 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(akey: key, avalue: hook);
2269 connect(sender: hook.obj, signal: &QObject::destroyed, receiver: this, slot: &QDBusConnectionPrivate::objectDestroyed,
2270 type: Qt::ConnectionType(Qt::BlockingQueuedConnection | Qt::UniqueConnection));
2271
2272 MatchRefCountHash::iterator mit = matchRefCounts.find(akey: 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(akey: hook.matchRule, avalue: 1);
2280
2281 if (connection) {
2282 if (mode != QDBusConnectionPrivate::PeerMode) {
2283 qDBusDebug() << this << "Adding rule:" << hook.matchRule;
2284 q_dbus_bus_add_match(connection: connection, rule: hook.matchRule, error: nullptr);
2285
2286 // Successfully connected the signal
2287 // Do we need to watch for this name?
2288 if (shouldWatchService(service: 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: connection,
2295 rule: buildMatchRule(service: QDBusUtil::dbusService(), objectPath: QString(), interface: QDBusUtil::dbusInterface(),
2296 member: QDBusUtil::nameOwnerChanged(), argMatch: rules, QString()),
2297 error: nullptr);
2298 data.owner = getNameOwnerNoCache(service: 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, argumentMatch: 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, argMatch: argumentMatch, receiver, signal: slot, minMIdx: 0, buildSignature: 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(akey: 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(akey: hook.matchRule);
2369 if (i == matchRefCounts.end()) {
2370 qWarning(msg: "QDBusConnectionPrivate::disconnectSignal: MatchRule not found in matchRefCounts!!");
2371 } else {
2372 if (i.value() == 1) {
2373 erase = true;
2374 matchRefCounts.erase(it: 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: connection, rule: hook.matchRule, error: nullptr);
2386
2387 // Successfully disconnected the signal
2388 // Were we watching for this name?
2389 WatchedServicesHash::Iterator sit = watchedServices.find(akey: hook.service);
2390 if (sit != watchedServices.end()) {
2391 if (--sit.value().refcount == 0) {
2392 watchedServices.erase(it: sit);
2393 ArgMatchRules rules;
2394 rules.args << hook.service;
2395 q_dbus_bus_remove_match(connection: connection,
2396 rule: buildMatchRule(service: QDBusUtil::dbusService(), objectPath: QString(), interface: QDBusUtil::dbusInterface(),
2397 member: QDBusUtil::nameOwnerChanged(), argMatch: rules, QString()),
2398 error: nullptr);
2399 }
2400 }
2401 }
2402
2403 }
2404
2405 return signalHooks.erase(it);
2406}
2407
2408void QDBusConnectionPrivate::registerObject(const ObjectTreeNode *node)
2409{
2410 connect(sender: node->obj, signal: &QObject::destroyed, receiver: this, slot: &QDBusConnectionPrivate::objectDestroyed,
2411 type: Qt::ConnectionType(Qt::BlockingQueuedConnection | Qt::UniqueConnection));
2412
2413 if (node->flags & (QDBusConnection::ExportAdaptors
2414 | QDBusConnection::ExportScriptableSignals
2415 | QDBusConnection::ExportNonScriptableSignals)) {
2416 QDBusAdaptorConnector *connector = qDBusCreateAdaptorConnector(object: node->obj);
2417
2418 if (node->flags & (QDBusConnection::ExportScriptableSignals
2419 | QDBusConnection::ExportNonScriptableSignals)) {
2420 connector->disconnectAllSignals(object: node->obj);
2421 connector->connectAllSignals(object: node->obj);
2422 }
2423
2424 connect(sender: connector, SIGNAL(relaySignal(QObject*,const QMetaObject*,int,QVariantList)),
2425 receiver: 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 QVector<QStringRef> pathComponents;
2434 int i;
2435 if (path == QLatin1String("/")) {
2436 i = 0;
2437 } else {
2438 pathComponents = path.splitRef(sep: 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(a: signal.methodSignature());
2458 if (!prepareHook(hook, key, service, path, interface, name: QString(), argMatch: ArgMatchRules(), receiver, signal: sig,
2459 minMIdx: QDBusAbstractInterface::staticMetaObject.methodCount(), buildSignature: 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(a: signal.methodSignature());
2479 if (!prepareHook(hook, key, service, path, interface, name: QString(), argMatch: ArgMatchRules(), receiver, signal: sig,
2480 minMIdx: QDBusAbstractInterface::staticMetaObject.methodCount(), buildSignature: 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(service: QDBusUtil::dbusService(), path: QString(), interface: QDBusUtil::dbusInterface(), name: QDBusUtil::nameOwnerChanged(),
2514 argumentMatch: matchArgs, signature: QString(), receiver: obj, slot: 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(service: QDBusUtil::dbusService(), path: QString(), interface: QDBusUtil::dbusInterface(), name: QDBusUtil::nameOwnerChanged(),
2529 argumentMatch: matchArgs, signature: QString(), receiver: obj, slot: member);
2530}
2531
2532QString QDBusConnectionPrivate::getNameOwner(const QString& serviceName)
2533{
2534 if (QDBusUtil::isValidUniqueConnectionName(busName: 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(akey: serviceName);
2543 if (it != watchedServices.constEnd())
2544 return it->owner;
2545 }
2546
2547 // not cached
2548 return getNameOwnerNoCache(service: serviceName);
2549}
2550
2551QString QDBusConnectionPrivate::getNameOwnerNoCache(const QString &serviceName)
2552{
2553 QDBusMessage msg = QDBusMessage::createMethodCall(destination: QDBusUtil::dbusService(),
2554 path: QDBusUtil::dbusPath(), interface: QDBusUtil::dbusInterface(),
2555 QStringLiteral("GetNameOwner"));
2556 QDBusMessagePrivate::setParametersValidated(msg, enable: true);
2557 msg << serviceName;
2558
2559 QDBusPendingCallPrivate *pcall = sendWithReplyAsync(message: msg, receiver: nullptr, returnMethod: nullptr, errorMethod: 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(pending: 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(i: 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(akey: interface, adefaultValue: 0);
2584 if (mo)
2585 return mo;
2586 }
2587
2588 // introspect the target object
2589 QDBusMessage msg = QDBusMessage::createMethodCall(destination: service, path,
2590 interface: QDBusUtil::dbusInterfaceIntrospectable(),
2591 QStringLiteral("Introspect"));
2592 QDBusMessagePrivate::setParametersValidated(msg, enable: true);
2593
2594 QDBusMessage reply = sendWithReply(message: msg, sendMode: 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(akey: interface, adefaultValue: 0);
2601 if (mo)
2602 // maybe it got created when we switched from read to write lock
2603 return mo;
2604
2605 QString xml;
2606 if (reply.type() == QDBusMessage::ReplyMessage) {
2607 if (reply.signature() == QLatin1String("s"))
2608 // fetch the XML description
2609 xml = reply.arguments().at(i: 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 map&: 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(t: 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(t: 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(str: serviceName);
2655}
2656
2657void QDBusConnectionPrivate::postEventToThread(int action, QObject *object, QEvent *ev)
2658{
2659 QDBusLockerBase::reportThreadAction(action, condition: QDBusLockerBase::BeforePost, ptr: this);
2660 QCoreApplication::postEvent(receiver: object, event: ev);
2661 QDBusLockerBase::reportThreadAction(action, condition: QDBusLockerBase::AfterPost, ptr: this);
2662}
2663
2664QT_END_NAMESPACE
2665
2666#endif // QT_NO_DBUS
2667

source code of qtbase/src/dbus/qdbusintegrator.cpp