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