Coverage Report

Created: 2026-09-26 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/qt/qtbase/src/dbus/qdbusabstractadaptor.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 "qdbusabstractadaptor.h"
7
#include "qdbusabstractadaptor_p.h"
8
9
#include <QtCore/qcoreapplication.h>
10
#include <QtCore/qmetaobject.h>
11
#include <QtCore/qset.h>
12
#include <QtCore/qthread.h>
13
14
#include "qdbusconnection.h"
15
16
#include "qdbusconnection_p.h"  // for qDBusParametersForMethod
17
#include "qdbusmetatype_p.h"
18
19
#include <algorithm>
20
21
#ifndef QT_NO_DBUS
22
23
QT_BEGIN_NAMESPACE
24
25
int QDBusAdaptorConnector::relaySlotMethodIndex()
26
0
{
27
0
    static const int cachedRelaySlotMethodIndex = staticMetaObject.indexOfMethod("relaySlot()");
28
0
    Q_ASSERT(cachedRelaySlotMethodIndex != 0); // 0 should be deleteLater() or destroyed()
29
0
    return cachedRelaySlotMethodIndex;
30
0
}
31
32
QDBusAdaptorConnector *qDBusFindAdaptorConnector(QObject *obj)
33
0
{
34
0
    if (!obj)
35
0
        return nullptr;
36
37
0
    for (QObject *child : std::as_const(obj->children())) {
38
0
        QDBusAdaptorConnector *connector = qobject_cast<QDBusAdaptorConnector *>(child);
39
0
        if (connector) {
40
0
            connector->polish();
41
0
            return connector;
42
0
        }
43
0
    }
44
0
    return nullptr;
45
0
}
46
47
QDBusAdaptorConnector *qDBusFindAdaptorConnector(QDBusAbstractAdaptor *adaptor)
48
0
{
49
0
    return qDBusFindAdaptorConnector(adaptor->parent());
50
0
}
51
52
QDBusAdaptorConnector *qDBusCreateAdaptorConnector(QObject *obj)
53
0
{
54
0
    QDBusAdaptorConnector *connector = qDBusFindAdaptorConnector(obj);
55
0
    if (connector)
56
0
        return connector;
57
0
    return new QDBusAdaptorConnector(obj);
58
0
}
59
60
QString QDBusAbstractAdaptorPrivate::retrieveIntrospectionXml(QDBusAbstractAdaptor *adaptor)
61
0
{
62
0
    return adaptor->d_func()->xml;
63
0
}
64
65
void QDBusAbstractAdaptorPrivate::saveIntrospectionXml(QDBusAbstractAdaptor *adaptor,
66
                                                       const QString &xml)
67
0
{
68
0
    adaptor->d_func()->xml = xml;
69
0
}
70
71
/*!
72
    \class QDBusAbstractAdaptor
73
    \inmodule QtDBus
74
    \since 4.2
75
76
    \brief The QDBusAbstractAdaptor class is the base class of D-Bus adaptor classes.
77
78
    The QDBusAbstractAdaptor class is the starting point for all objects intending to provide
79
    interfaces to the external world using D-Bus. This is accomplished by attaching a one or more
80
    classes derived from QDBusAbstractAdaptor to a normal QObject and then registering that QObject
81
    with QDBusConnection::registerObject. QDBusAbstractAdaptor objects are intended to be
82
    light-weight wrappers, mostly just relaying calls into the real object (its parent) and the
83
    signals from it.
84
85
    Each QDBusAbstractAdaptor-derived class should define the D-Bus interface it is implementing
86
    using the Q_CLASSINFO macro in the class definition. Note that only one interface can be
87
    exposed in this way.
88
89
    QDBusAbstractAdaptor uses the standard QObject mechanism of signals, slots and properties to
90
    determine what signals, methods and properties to export to the bus. Any signal emitted by
91
    QDBusAbstractAdaptor-derived classes will be automatically be relayed through any D-Bus
92
    connections the object is registered on.
93
94
    Classes derived from QDBusAbstractAdaptor must be created on the heap using the \a new operator
95
    and must not be deleted by the user (they will be deleted automatically when the object they are
96
    connected to is also deleted).
97
98
    \sa {usingadaptors.html}{Using adaptors}, QDBusConnection
99
*/
100
101
/*!
102
    Constructs a QDBusAbstractAdaptor with \a obj as the parent object.
103
*/
104
QDBusAbstractAdaptor::QDBusAbstractAdaptor(QObject* obj)
105
0
    : QObject(*new QDBusAbstractAdaptorPrivate, obj)
106
0
{
107
108
0
    Q_ASSERT_X(obj, Q_FUNC_INFO, "Expected non-null parent");
109
110
0
    QDBusAdaptorConnector *connector = qDBusCreateAdaptorConnector(obj);
111
112
0
    connector->waitingForPolish = true;
113
0
    QMetaObject::invokeMethod(connector, &QDBusAdaptorConnector::polish, Qt::QueuedConnection);
114
0
}
115
116
/*!
117
    Destroys the adaptor.
118
119
    \warning Adaptors are destroyed automatically when the real object they refer to is
120
             destroyed. Do not delete the adaptors yourself.
121
*/
122
QDBusAbstractAdaptor::~QDBusAbstractAdaptor()
123
0
{
124
0
}
125
126
/*!
127
    Toggles automatic signal relaying from the real object (see object()).
128
129
    Automatic signal relaying consists of signal-to-signal connection of the signals on the parent
130
    that have the exact same method signature in both classes.
131
132
    If \a enable is set to true, connect the signals; if set to false, disconnect all signals.
133
*/
134
void QDBusAbstractAdaptor::setAutoRelaySignals(bool enable)
135
0
{
136
0
    const QMetaObject *us = metaObject();
137
0
    const QMetaObject *them = parent()->metaObject();
138
0
    bool connected = false;
139
0
    for (int idx = staticMetaObject.methodCount(); idx < us->methodCount(); ++idx) {
140
0
        QMetaMethod mm = us->method(idx);
141
142
0
        if (mm.methodType() != QMetaMethod::Signal)
143
0
            continue;
144
145
        // try to connect/disconnect to a signal on the parent that has the same method signature
146
0
        QByteArray sig = QMetaObject::normalizedSignature(mm.methodSignature().constData());
147
0
        if (them->indexOfSignal(sig) == -1)
148
0
            continue;
149
0
        sig.prepend(QSIGNAL_CODE + '0');
150
0
        parent()->disconnect(sig, this, sig);
151
0
        if (enable)
152
0
            connected = connect(parent(), sig, sig) || connected;
153
0
    }
154
0
    d_func()->autoRelaySignals = connected;
155
0
}
156
157
/*!
158
    Returns \c true if automatic signal relaying from the real object (see object()) is enabled,
159
    otherwiser returns \c false.
160
161
    \sa setAutoRelaySignals()
162
*/
163
bool QDBusAbstractAdaptor::autoRelaySignals() const
164
0
{
165
0
    return d_func()->autoRelaySignals;
166
0
}
167
168
QDBusAdaptorConnector::QDBusAdaptorConnector(QObject *obj)
169
0
    : QObject(obj), waitingForPolish(false)
170
0
{
171
0
}
172
173
QDBusAdaptorConnector::~QDBusAdaptorConnector()
174
0
{
175
0
}
176
177
void QDBusAdaptorConnector::addAdaptor(QDBusAbstractAdaptor *adaptor)
178
0
{
179
    // find the interface name
180
0
    const QMetaObject *mo = adaptor->metaObject();
181
0
    int ciid = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTERFACE);
182
0
    if (ciid != -1) {
183
0
        QMetaClassInfo mci = mo->classInfo(ciid);
184
0
        if (*mci.value()) {
185
            // find out if this interface exists first
186
0
            const char *interface = mci.value();
187
0
            AdaptorMap::Iterator it = std::lower_bound(adaptors.begin(), adaptors.end(),
188
0
                                                       QByteArrayView(interface));
189
0
            if (it != adaptors.end() && qstrcmp(interface, it->interface) == 0) {
190
                // exists. Replace it (though it's probably the same)
191
0
                if (it->adaptor != adaptor) {
192
                    // reconnect the signals
193
0
                    disconnectAllSignals(it->adaptor);
194
0
                    connectAllSignals(adaptor);
195
0
                }
196
0
                it->adaptor = adaptor;
197
0
            } else {
198
                // create a new one
199
0
                AdaptorData entry;
200
0
                entry.interface = interface;
201
0
                entry.adaptor = adaptor;
202
0
                adaptors << entry;
203
204
                // connect the adaptor's signals to our relaySlot slot
205
0
                connectAllSignals(adaptor);
206
0
            }
207
0
        }
208
0
    }
209
0
}
210
211
void QDBusAdaptorConnector::disconnectAllSignals(QObject *obj)
212
0
{
213
0
    QMetaObject::disconnect(obj, -1, this, relaySlotMethodIndex());
214
0
}
215
216
void QDBusAdaptorConnector::connectAllSignals(QObject *obj)
217
0
{
218
0
    QMetaObject::connect(obj, -1, this, relaySlotMethodIndex(), Qt::DirectConnection);
219
0
}
220
221
void QDBusAdaptorConnector::polish()
222
0
{
223
0
    if (!waitingForPolish)
224
0
        return;                 // avoid working multiple times if multiple adaptors were added
225
226
0
    waitingForPolish = false;
227
0
    for (QObject *child : std::as_const(parent()->children())) {
228
0
        QDBusAbstractAdaptor *adaptor = qobject_cast<QDBusAbstractAdaptor *>(child);
229
0
        if (adaptor)
230
0
            addAdaptor(adaptor);
231
0
    }
232
233
    // sort the adaptor list
234
0
    std::sort(adaptors.begin(), adaptors.end());
235
0
}
236
237
void QDBusAdaptorConnector::relaySlot(QMethodRawArguments argv)
238
0
{
239
0
    QObject *sndr = sender();
240
0
    if (Q_LIKELY(sndr)) {
241
0
        relay(sndr, senderSignalIndex(), argv.arguments);
242
0
    } else {
243
0
        qWarning("QtDBus: cannot relay signals from parent %s(%p \"%s\") unless they are emitted in the object's thread %s(%p \"%s\"). "
244
0
                 "Current thread is %s(%p \"%s\").",
245
0
                 parent()->metaObject()->className(), parent(), qPrintable(parent()->objectName()),
246
0
                 parent()->thread()->metaObject()->className(), parent()->thread(), qPrintable(parent()->thread()->objectName()),
247
0
                 QThread::currentThread()->metaObject()->className(), QThread::currentThread(), qPrintable(QThread::currentThread()->objectName()));
248
0
    }
249
0
}
250
251
void QDBusAdaptorConnector::relay(QObject *senderObj, int lastSignalIdx, void **argv)
252
0
{
253
0
    if (lastSignalIdx < QObject::staticMetaObject.methodCount())
254
        // QObject signal (destroyed(QObject *)) -- ignore
255
0
        return;
256
257
0
    QMetaMethod mm = senderObj->metaObject()->method(lastSignalIdx);
258
0
    const QMetaObject *senderMetaObject = mm.enclosingMetaObject();
259
260
0
    QObject *realObject = senderObj;
261
0
    if (qobject_cast<QDBusAbstractAdaptor *>(senderObj))
262
        // it's an adaptor, so the real object is in fact its parent
263
0
        realObject = realObject->parent();
264
265
    // break down the parameter list
266
0
    QList<QMetaType> types;
267
0
    QString errorMsg;
268
0
    int inputCount = qDBusParametersForMethod(mm, types, errorMsg);
269
0
    if (inputCount == -1) {
270
        // invalid signal signature
271
0
        qWarning("QDBusAbstractAdaptor: Cannot relay signal %s::%s: %s",
272
0
                 senderMetaObject->className(), mm.methodSignature().constData(),
273
0
                 qPrintable(errorMsg));
274
0
        return;
275
0
    }
276
0
    if (inputCount + 1 != types.size() ||
277
0
        types.at(inputCount) == QDBusMetaTypeId::message()) {
278
        // invalid signal signature
279
0
        qWarning("QDBusAbstractAdaptor: Cannot relay signal %s::%s",
280
0
                 senderMetaObject->className(), mm.methodSignature().constData());
281
0
        return;
282
0
    }
283
284
0
    QVariantList args;
285
0
    const int numTypes = types.size();
286
0
    args.reserve(numTypes - 1);
287
0
    for (int i = 1; i < numTypes; ++i)
288
0
        args << QVariant(QMetaType(types.at(i)), argv[i]);
289
290
    // now emit the signal with all the information
291
0
    emit relaySignal(realObject, senderMetaObject, lastSignalIdx, args);
292
0
}
293
294
QT_END_NAMESPACE
295
296
#include "moc_qdbusabstractadaptor_p.cpp"
297
#include "moc_qdbusabstractadaptor.cpp"
298
299
#endif // QT_NO_DBUS