1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:critical reason:data-parser
4
5#include "qdbusmetatype.h"
6#include "qdbusmetatype_p.h"
7
8#include <string.h>
9#include "qdbus_symbols_p.h"
10
11#include <qbytearray.h>
12#include <qglobal.h>
13#include <qlist.h>
14#include <qreadwritelock.h>
15#include <qdatetime.h>
16#include <qrect.h>
17#include <qsize.h>
18#include <qpoint.h>
19#include <qline.h>
20
21#include "qdbusargument_p.h"
22#include "qdbusutil_p.h"
23#include "qdbusunixfiledescriptor.h"
24#ifndef QT_BOOTSTRAPPED
25#include "qdbusmessage.h"
26#endif
27
28#ifndef QT_NO_DBUS
29
30#ifndef DBUS_TYPE_UNIX_FD
31# define DBUS_TYPE_UNIX_FD int('h')
32# define DBUS_TYPE_UNIX_FD_AS_STRING "h"
33#endif
34
35QT_BEGIN_NAMESPACE
36
37class QDBusCustomTypeInfo
38{
39public:
40 QDBusCustomTypeInfo() : signature(), marshall(nullptr), demarshall(nullptr)
41 { }
42
43 // Suggestion:
44 // change 'signature' to char* and make QDBusCustomTypeInfo a Movable type
45 QByteArray signature;
46 QDBusMetaType::MarshallFunction marshall;
47 QDBusMetaType::DemarshallFunction demarshall;
48};
49
50void QDBusMetaTypeId::init()
51{
52 Q_CONSTINIT static QBasicAtomicInt initialized = Q_BASIC_ATOMIC_INITIALIZER(false);
53
54 // reentrancy is not a problem since everything else is locked on their own
55 // set the guard variable at the end
56 if (!initialized.loadRelaxed()) {
57 // register our types with Qt Core
58 message().registerType();
59 argument().registerType();
60 variant().registerType();
61 objectpath().registerType();
62 signature().registerType();
63 error().registerType();
64 unixfd().registerType();
65
66#ifndef QDBUS_NO_SPECIALTYPES
67 // and register Qt Core's with us
68 qDBusRegisterMetaType<QDate>();
69 qDBusRegisterMetaType<QTime>();
70 qDBusRegisterMetaType<QDateTime>();
71 qDBusRegisterMetaType<QRect>();
72 qDBusRegisterMetaType<QRectF>();
73 qDBusRegisterMetaType<QSize>();
74 qDBusRegisterMetaType<QSizeF>();
75 qDBusRegisterMetaType<QPoint>();
76 qDBusRegisterMetaType<QPointF>();
77 qDBusRegisterMetaType<QLine>();
78 qDBusRegisterMetaType<QLineF>();
79 qDBusRegisterMetaType<QVariantList>();
80 qDBusRegisterMetaType<QVariantMap>();
81 qDBusRegisterMetaType<QVariantHash>();
82
83 qDBusRegisterMetaType<QList<bool> >();
84 qDBusRegisterMetaType<QList<short> >();
85 qDBusRegisterMetaType<QList<ushort> >();
86 qDBusRegisterMetaType<QList<int> >();
87 qDBusRegisterMetaType<QList<uint> >();
88 qDBusRegisterMetaType<QList<qlonglong> >();
89 qDBusRegisterMetaType<QList<qulonglong> >();
90 qDBusRegisterMetaType<QList<double> >();
91
92 // plus lists of our own types
93 qDBusRegisterMetaType<QList<QDBusVariant> >();
94 qDBusRegisterMetaType<QList<QDBusObjectPath> >();
95 qDBusRegisterMetaType<QList<QDBusSignature> >();
96 qDBusRegisterMetaType<QList<QDBusUnixFileDescriptor> >();
97#endif
98
99 initialized.storeRelaxed(newValue: true);
100 }
101}
102
103struct QDBusCustomTypes
104{
105 QReadWriteLock lock;
106 QHash<int, QDBusCustomTypeInfo> hash;
107};
108
109Q_GLOBAL_STATIC(QDBusCustomTypes, customTypes)
110
111/*!
112 \class QDBusMetaType
113 \inmodule QtDBus
114 \brief Meta-type registration system for the Qt D-Bus module.
115 \internal
116
117 The QDBusMetaType class allows you to register class types for
118 marshalling and demarshalling over D-Bus. D-Bus supports a very
119 limited set of primitive types, but allows one to extend the type
120 system by creating compound types, such as arrays (lists) and
121 structs. In order to use them with Qt D-Bus, those types must be
122 registered.
123
124 See \l {qdbustypesystem.html}{Qt D-Bus Type System} for more
125 information on the type system and how to register additional
126 types.
127
128 \sa {qdbustypesystem.html}{Qt D-Bus Type System},
129 qDBusRegisterMetaType(), QMetaType, QVariant, QDBusArgument
130*/
131
132/*!
133 \fn template<typename T> QMetaType qDBusRegisterMetaType()
134 \relates QDBusArgument
135 \threadsafe
136 \since 4.2
137
138 Registers \c{T} with the
139 \l {qdbustypesystem.html}{Qt D-Bus Type System} and the Qt \l
140 {QMetaType}{meta-type system}, if it's not already registered.
141
142 To register a type, it must be declared as a meta-type with the
143 Q_DECLARE_METATYPE() macro, and then registered as in the
144 following example:
145
146 \snippet code/src_qdbus_qdbusmetatype.cpp 0-0
147 \codeline
148 \snippet code/src_qdbus_qdbusmetatype.cpp 0-1
149
150 If \c{T} isn't one of
151 Qt's \l{container classes}, the \c{operator<<} and
152 \c{operator>>} streaming operators between \c{T} and QDBusArgument
153 must be already declared. See the \l {qdbustypesystem.html}{Qt D-Bus
154 Type System} page for more information on how to declare such
155 types.
156
157 This function returns the Qt meta type id for the type (the same
158 value that is returned from qRegisterMetaType()).
159
160 \note The feature that a \c{T} inheriting a streamable type (including
161 the containers QList, QHash or QMap) can be streamed without providing
162 custom \c{operator<<} and \c{operator>>} is deprecated as of Qt 5.7,
163 because it ignores everything in \c{T} except the base class. There is
164 no diagnostic. You should always provide these operators for all types
165 you wish to stream and not rely on Qt-provided stream operators for
166 base classes.
167
168 \sa {qdbustypesystem.html}{Qt D-Bus Type System}, qRegisterMetaType(), QMetaType
169*/
170
171/*!
172 \typedef QDBusMetaType::MarshallFunction
173 \internal
174*/
175
176/*!
177 \typedef QDBusMetaType::DemarshallFunction
178 \internal
179*/
180
181/*!
182 \internal
183 Registers the marshalling and demarshalling functions for meta
184 type \a metaType.
185*/
186void QDBusMetaType::registerMarshallOperators(QMetaType metaType, MarshallFunction mf,
187 DemarshallFunction df)
188{
189 int id = metaType.id();
190 if (id < 0 || !mf || !df)
191 return; // error!
192
193 auto *ct = customTypes();
194 if (!ct)
195 return;
196
197 QWriteLocker locker(&ct->lock);
198 QDBusCustomTypeInfo &info = ct->hash[id];
199 info.marshall = mf;
200 info.demarshall = df;
201}
202
203/*!
204 \internal
205 Executes the marshalling of type \a metaType (whose data is contained in
206 \a data) to the D-Bus marshalling argument \a arg. Returns \c true if
207 the marshalling succeeded, or false if an error occurred.
208*/
209bool QDBusMetaType::marshall(QDBusArgument &arg, QMetaType metaType, const void *data)
210{
211 auto *ct = customTypes();
212 if (!ct)
213 return false;
214
215 int id = metaType.id();
216 QDBusMetaTypeId::init();
217
218 MarshallFunction mf;
219 {
220 QReadLocker locker(&ct->lock);
221
222 auto it = ct->hash.constFind(key: id);
223 if (it == ct->hash.cend())
224 return false; // non-existent
225
226 const QDBusCustomTypeInfo &info = *it;
227 if (!info.marshall) {
228 mf = nullptr; // make gcc happy
229 return false;
230 } else
231 mf = info.marshall;
232 }
233
234 mf(arg, data);
235 return true;
236}
237
238/*!
239 \internal
240 Executes the demarshalling of type \a metaType (whose data will be placed in
241 \a data) from the D-Bus marshalling argument \a arg. Returns \c true if
242 the demarshalling succeeded, or false if an error occurred.
243*/
244bool QDBusMetaType::demarshall(const QDBusArgument &arg, QMetaType metaType, void *data)
245{
246 auto *ct = customTypes();
247 if (!ct)
248 return false;
249
250 int id = metaType.id();
251 QDBusMetaTypeId::init();
252
253 DemarshallFunction df;
254 {
255 QReadLocker locker(&ct->lock);
256
257 auto it = ct->hash.constFind(key: id);
258 if (it == ct->hash.cend())
259 return false; // non-existent
260
261 const QDBusCustomTypeInfo &info = *it;
262 if (!info.demarshall) {
263 df = nullptr; // make gcc happy
264 return false;
265 } else
266 df = info.demarshall;
267 }
268#ifndef QT_BOOTSTRAPPED
269 QDBusArgument copy = arg;
270 df(copy, data);
271#else
272 Q_UNUSED(arg);
273 Q_UNUSED(data);
274 Q_UNUSED(df);
275#endif
276 return true;
277}
278
279/*!
280 \fn QDBusMetaType::signatureToType(const char *signature)
281 \internal
282
283 Returns the Qt meta type id for the given D-Bus signature for exactly one full type, given
284 by \a signature.
285
286 Note: this function only handles the basic D-Bus types.
287
288 \sa QDBusUtil::isValidSingleSignature(), typeToSignature(),
289 QVariant::metaType()
290*/
291QMetaType QDBusMetaType::signatureToMetaType(const char *signature)
292{
293 if (!signature)
294 return QMetaType(QMetaType::UnknownType);
295
296 QDBusMetaTypeId::init();
297 switch (signature[0])
298 {
299 case DBUS_TYPE_BOOLEAN:
300 return QMetaType(QMetaType::Bool);
301
302 case DBUS_TYPE_BYTE:
303 return QMetaType(QMetaType::UChar);
304
305 case DBUS_TYPE_INT16:
306 return QMetaType(QMetaType::Short);
307
308 case DBUS_TYPE_UINT16:
309 return QMetaType(QMetaType::UShort);
310
311 case DBUS_TYPE_INT32:
312 return QMetaType(QMetaType::Int);
313
314 case DBUS_TYPE_UINT32:
315 return QMetaType(QMetaType::UInt);
316
317 case DBUS_TYPE_INT64:
318 return QMetaType(QMetaType::LongLong);
319
320 case DBUS_TYPE_UINT64:
321 return QMetaType(QMetaType::ULongLong);
322
323 case DBUS_TYPE_DOUBLE:
324 return QMetaType(QMetaType::Double);
325
326 case DBUS_TYPE_STRING:
327 return QMetaType(QMetaType::QString);
328
329 case DBUS_TYPE_OBJECT_PATH:
330 return QDBusMetaTypeId::objectpath();
331
332 case DBUS_TYPE_SIGNATURE:
333 return QDBusMetaTypeId::signature();
334
335 case DBUS_TYPE_UNIX_FD:
336 return QDBusMetaTypeId::unixfd();
337
338 case DBUS_TYPE_VARIANT:
339 return QDBusMetaTypeId::variant();
340
341 case DBUS_TYPE_ARRAY: // special case
342 switch (signature[1]) {
343 case DBUS_TYPE_BYTE:
344 return QMetaType(QMetaType::QByteArray);
345
346 case DBUS_TYPE_STRING:
347 return QMetaType(QMetaType::QStringList);
348
349 case DBUS_TYPE_VARIANT:
350 return QMetaType(QMetaType::QVariantList);
351
352 case DBUS_TYPE_OBJECT_PATH:
353 return QMetaType::fromType<QList<QDBusObjectPath> >();
354
355 case DBUS_TYPE_SIGNATURE:
356 return QMetaType::fromType<QList<QDBusSignature> >();
357
358 }
359 Q_FALLTHROUGH();
360 default:
361 return QMetaType(QMetaType::UnknownType);
362 }
363}
364
365/*!
366 \fn QDBusMetaType::registerCustomType(QMetaType type, const QByteArray &signature)
367 \internal
368
369 Registers the meta type \a type to be represeneted by the given D-Bus \a signature.
370
371 This is used in qdbuscpp2xml for custom types which aren't known to the C++ type system.
372*/
373void QDBusMetaType::registerCustomType(QMetaType type, const QByteArray &signature)
374{
375 auto *ct = customTypes();
376 if (!ct)
377 return;
378
379 QWriteLocker locker(&ct->lock);
380 auto &info = ct->hash[type.id()];
381 info.signature = signature;
382 // note how marshall/demarshall are not set, the type is never used at runtime
383}
384
385/*!
386 \fn QDBusMetaType::typeToSignature(int type)
387 \internal
388
389 Returns the D-Bus signature equivalent to the supplied meta type id \a type.
390
391 More types can be registered with the qDBusRegisterMetaType() function.
392
393 \sa QDBusUtil::isValidSingleSignature(), signatureToType(),
394 QVariant::metaType()
395*/
396const char *QDBusMetaType::typeToSignature(QMetaType type)
397{
398 // check if it's a static type
399 switch (type.id())
400 {
401 case QMetaType::UChar:
402 return DBUS_TYPE_BYTE_AS_STRING;
403
404 case QMetaType::Bool:
405 return DBUS_TYPE_BOOLEAN_AS_STRING;
406
407 case QMetaType::Short:
408 return DBUS_TYPE_INT16_AS_STRING;
409
410 case QMetaType::UShort:
411 return DBUS_TYPE_UINT16_AS_STRING;
412
413 case QMetaType::Int:
414 return DBUS_TYPE_INT32_AS_STRING;
415
416 case QMetaType::UInt:
417 return DBUS_TYPE_UINT32_AS_STRING;
418
419 case QMetaType::LongLong:
420 return DBUS_TYPE_INT64_AS_STRING;
421
422 case QMetaType::ULongLong:
423 return DBUS_TYPE_UINT64_AS_STRING;
424
425 case QMetaType::Double:
426 return DBUS_TYPE_DOUBLE_AS_STRING;
427
428 case QMetaType::QString:
429 return DBUS_TYPE_STRING_AS_STRING;
430
431 case QMetaType::QStringList:
432 return DBUS_TYPE_ARRAY_AS_STRING
433 DBUS_TYPE_STRING_AS_STRING; // as
434
435 case QMetaType::QByteArray:
436 return DBUS_TYPE_ARRAY_AS_STRING
437 DBUS_TYPE_BYTE_AS_STRING; // ay
438 }
439
440 QDBusMetaTypeId::init();
441 if (type == QDBusMetaTypeId::variant())
442 return DBUS_TYPE_VARIANT_AS_STRING;
443 else if (type == QDBusMetaTypeId::objectpath())
444 return DBUS_TYPE_OBJECT_PATH_AS_STRING;
445 else if (type == QDBusMetaTypeId::signature())
446 return DBUS_TYPE_SIGNATURE_AS_STRING;
447 else if (type == QDBusMetaTypeId::unixfd())
448 return DBUS_TYPE_UNIX_FD_AS_STRING;
449
450 // try the database
451 auto *ct = customTypes();
452 if (!ct)
453 return nullptr;
454
455 {
456 QReadLocker locker(&ct->lock);
457 auto it = ct->hash.constFind(key: type.id());
458 if (it == ct->hash.end())
459 return nullptr;
460
461 const QDBusCustomTypeInfo &info = *it;
462
463 if (!info.signature.isNull())
464 return info.signature;
465
466 if (!info.marshall)
467 return nullptr; // type not registered with us
468 }
469
470 // call to user code to construct the signature type
471 QDBusCustomTypeInfo *info;
472 {
473 // createSignature will never return a null QByteArray
474 // if there was an error, it'll return ""
475 QByteArray signature = QDBusArgumentPrivate::createSignature(type);
476
477 // re-acquire lock
478 QWriteLocker locker(&ct->lock);
479 info = &ct->hash[type.id()];
480 info->signature = signature;
481 }
482 return info->signature;
483}
484
485QT_END_NAMESPACE
486
487#endif // QT_NO_DBUS
488

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