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