Coverage Report

Created: 2026-03-12 07:14

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