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