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 "qdbusargument_p.h"
41 #include "qdbusconnection.h"
42 #include "qdbusmetatype_p.h"
43 #include "qdbusutil_p.h"
44 
45 #ifndef QT_NO_DBUS
46 
47 QT_BEGIN_NAMESPACE
48 
qIterAppend(DBusMessageIter * it,QByteArray * ba,int type,const void * arg)49 static void qIterAppend(DBusMessageIter *it, QByteArray *ba, int type, const void *arg)
50 {
51     if (ba)
52         *ba += char(type);
53     else
54         q_dbus_message_iter_append_basic(it, type, arg);
55 }
56 
~QDBusMarshaller()57 QDBusMarshaller::~QDBusMarshaller()
58 {
59     close();
60 }
61 
currentSignature()62 inline QString QDBusMarshaller::currentSignature()
63 {
64     if (message)
65         return QString::fromUtf8(q_dbus_message_get_signature(message));
66     return QString();
67 }
68 
append(uchar arg)69 inline void QDBusMarshaller::append(uchar arg)
70 {
71     if (!skipSignature)
72         qIterAppend(&iterator, ba, DBUS_TYPE_BYTE, &arg);
73 }
74 
append(bool arg)75 inline void QDBusMarshaller::append(bool arg)
76 {
77     dbus_bool_t cast = arg;
78     if (!skipSignature)
79         qIterAppend(&iterator, ba, DBUS_TYPE_BOOLEAN, &cast);
80 }
81 
append(short arg)82 inline void QDBusMarshaller::append(short arg)
83 {
84     if (!skipSignature)
85         qIterAppend(&iterator, ba, DBUS_TYPE_INT16, &arg);
86 }
87 
append(ushort arg)88 inline void QDBusMarshaller::append(ushort arg)
89 {
90     if (!skipSignature)
91         qIterAppend(&iterator, ba, DBUS_TYPE_UINT16, &arg);
92 }
93 
append(int arg)94 inline void QDBusMarshaller::append(int arg)
95 {
96     if (!skipSignature)
97         qIterAppend(&iterator, ba, DBUS_TYPE_INT32, &arg);
98 }
99 
append(uint arg)100 inline void QDBusMarshaller::append(uint arg)
101 {
102     if (!skipSignature)
103         qIterAppend(&iterator, ba, DBUS_TYPE_UINT32, &arg);
104 }
105 
append(qlonglong arg)106 inline void QDBusMarshaller::append(qlonglong arg)
107 {
108     if (!skipSignature)
109         qIterAppend(&iterator, ba, DBUS_TYPE_INT64, &arg);
110 }
111 
append(qulonglong arg)112 inline void QDBusMarshaller::append(qulonglong arg)
113 {
114     if (!skipSignature)
115         qIterAppend(&iterator, ba, DBUS_TYPE_UINT64, &arg);
116 }
117 
append(double arg)118 inline void QDBusMarshaller::append(double arg)
119 {
120     if (!skipSignature)
121         qIterAppend(&iterator, ba, DBUS_TYPE_DOUBLE, &arg);
122 }
123 
append(const QString & arg)124 void QDBusMarshaller::append(const QString &arg)
125 {
126     QByteArray data = arg.toUtf8();
127     const char *cdata = data.constData();
128     if (!skipSignature)
129         qIterAppend(&iterator, ba, DBUS_TYPE_STRING, &cdata);
130 }
131 
append(const QDBusObjectPath & arg)132 inline void QDBusMarshaller::append(const QDBusObjectPath &arg)
133 {
134     QByteArray data = arg.path().toUtf8();
135     if (!ba && data.isEmpty()) {
136         error(QLatin1String("Invalid object path passed in arguments"));
137     } else {
138         const char *cdata = data.constData();
139         if (!skipSignature)
140             qIterAppend(&iterator, ba, DBUS_TYPE_OBJECT_PATH, &cdata);
141     }
142 }
143 
append(const QDBusSignature & arg)144 inline void QDBusMarshaller::append(const QDBusSignature &arg)
145 {
146     QByteArray data = arg.signature().toUtf8();
147     if (!ba && data.isEmpty()) {
148         error(QLatin1String("Invalid signature passed in arguments"));
149     } else {
150         const char *cdata = data.constData();
151         if (!skipSignature)
152             qIterAppend(&iterator, ba, DBUS_TYPE_SIGNATURE, &cdata);
153     }
154 }
155 
append(const QDBusUnixFileDescriptor & arg)156 inline void QDBusMarshaller::append(const QDBusUnixFileDescriptor &arg)
157 {
158     int fd = arg.fileDescriptor();
159     if (!ba && fd == -1) {
160         error(QLatin1String("Invalid file descriptor passed in arguments"));
161     } else {
162         if (!skipSignature)
163             qIterAppend(&iterator, ba, DBUS_TYPE_UNIX_FD, &fd);
164     }
165 }
166 
append(const QByteArray & arg)167 inline void QDBusMarshaller::append(const QByteArray &arg)
168 {
169     if (ba) {
170         if (!skipSignature)
171             *ba += DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING;
172         return;
173     }
174 
175     const char* cdata = arg.constData();
176     DBusMessageIter subiterator;
177     q_dbus_message_iter_open_container(&iterator, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING,
178                                      &subiterator);
179     q_dbus_message_iter_append_fixed_array(&subiterator, DBUS_TYPE_BYTE, &cdata, arg.length());
180     q_dbus_message_iter_close_container(&iterator, &subiterator);
181 }
182 
append(const QDBusVariant & arg)183 inline bool QDBusMarshaller::append(const QDBusVariant &arg)
184 {
185     if (ba) {
186         if (!skipSignature)
187             *ba += DBUS_TYPE_VARIANT_AS_STRING;
188         return true;
189     }
190 
191     const QVariant &value = arg.variant();
192     int id = value.userType();
193     if (id == QMetaType::UnknownType) {
194         qWarning("QDBusMarshaller: cannot add a null QDBusVariant");
195         error(QLatin1String("Variant containing QVariant::Invalid passed in arguments"));
196         return false;
197     }
198 
199     QByteArray tmpSignature;
200     const char *signature = nullptr;
201     if (id == QDBusMetaTypeId::argument()) {
202         // take the signature from the QDBusArgument object we're marshalling
203         tmpSignature =
204             qvariant_cast<QDBusArgument>(value).currentSignature().toLatin1();
205         signature = tmpSignature.constData();
206     } else {
207         // take the signatuer from the metatype we're marshalling
208         signature = QDBusMetaType::typeToSignature(id);
209     }
210     if (!signature) {
211         qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. "
212                  "Use qDBusRegisterMetaType to register it",
213                  QMetaType::typeName(id), id);
214         error(QLatin1String("Unregistered type %1 passed in arguments")
215               .arg(QLatin1String(QMetaType::typeName(id))));
216         return false;
217     }
218 
219     QDBusMarshaller sub(capabilities);
220     open(sub, DBUS_TYPE_VARIANT, signature);
221     bool isOk = sub.appendVariantInternal(value);
222     // don't call sub.close(): it auto-closes
223 
224     return isOk;
225 }
226 
append(const QStringList & arg)227 inline void QDBusMarshaller::append(const QStringList &arg)
228 {
229     if (ba) {
230         if (!skipSignature)
231             *ba += DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING;
232         return;
233     }
234 
235     QDBusMarshaller sub(capabilities);
236     open(sub, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING);
237     QStringList::ConstIterator it = arg.constBegin();
238     QStringList::ConstIterator end = arg.constEnd();
239     for ( ; it != end; ++it)
240         sub.append(*it);
241     // don't call sub.close(): it auto-closes
242 }
243 
beginStructure()244 inline QDBusMarshaller *QDBusMarshaller::beginStructure()
245 {
246     return beginCommon(DBUS_TYPE_STRUCT, nullptr);
247 }
248 
beginArray(int id)249 inline QDBusMarshaller *QDBusMarshaller::beginArray(int id)
250 {
251     const char *signature = QDBusMetaType::typeToSignature( QVariant::Type(id) );
252     if (!signature) {
253         qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. "
254                  "Use qDBusRegisterMetaType to register it",
255                  QMetaType::typeName(id), id);
256         error(QLatin1String("Unregistered type %1 passed in arguments")
257               .arg(QLatin1String(QMetaType::typeName(id))));
258         return this;
259     }
260 
261     return beginCommon(DBUS_TYPE_ARRAY, signature);
262 }
263 
beginMap(int kid,int vid)264 inline QDBusMarshaller *QDBusMarshaller::beginMap(int kid, int vid)
265 {
266     const char *ksignature = QDBusMetaType::typeToSignature( QVariant::Type(kid) );
267     if (!ksignature) {
268         qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. "
269                  "Use qDBusRegisterMetaType to register it",
270                  QMetaType::typeName(kid), kid);
271         error(QLatin1String("Unregistered type %1 passed in arguments")
272               .arg(QLatin1String(QMetaType::typeName(kid))));
273         return this;
274     }
275     if (ksignature[1] != 0 || !QDBusUtil::isValidBasicType(*ksignature)) {
276         qWarning("QDBusMarshaller: type '%s' (%d) cannot be used as the key type in a D-BUS map.",
277                  QMetaType::typeName(kid), kid);
278         error(QLatin1String("Type %1 passed in arguments cannot be used as a key in a map")
279               .arg(QLatin1String(QMetaType::typeName(kid))));
280         return this;
281     }
282 
283     const char *vsignature = QDBusMetaType::typeToSignature( QVariant::Type(vid) );
284     if (!vsignature) {
285         const char *typeName = QMetaType::typeName(vid);
286         qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. "
287                  "Use qDBusRegisterMetaType to register it",
288                  typeName, vid);
289         error(QLatin1String("Unregistered type %1 passed in arguments")
290               .arg(QLatin1String(typeName)));
291         return this;
292     }
293 
294     QByteArray signature;
295     signature = DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING;
296     signature += ksignature;
297     signature += vsignature;
298     signature += DBUS_DICT_ENTRY_END_CHAR_AS_STRING;
299     return beginCommon(DBUS_TYPE_ARRAY, signature);
300 }
301 
beginMapEntry()302 inline QDBusMarshaller *QDBusMarshaller::beginMapEntry()
303 {
304     return beginCommon(DBUS_TYPE_DICT_ENTRY, nullptr);
305 }
306 
open(QDBusMarshaller & sub,int code,const char * signature)307 void QDBusMarshaller::open(QDBusMarshaller &sub, int code, const char *signature)
308 {
309     sub.parent = this;
310     sub.ba = ba;
311     sub.ok = true;
312     sub.capabilities = capabilities;
313     sub.skipSignature = skipSignature;
314 
315     if (ba) {
316         if (!skipSignature) {
317             switch (code) {
318             case DBUS_TYPE_ARRAY:
319                 *ba += char(code);
320                 *ba += signature;
321                 Q_FALLTHROUGH();
322 
323             case DBUS_TYPE_DICT_ENTRY:
324                 sub.closeCode = 0;
325                 sub.skipSignature = true;
326                 break;
327 
328             case DBUS_TYPE_STRUCT:
329                 *ba += DBUS_STRUCT_BEGIN_CHAR;
330                 sub.closeCode = DBUS_STRUCT_END_CHAR;
331                 break;
332             }
333         }
334     } else {
335         q_dbus_message_iter_open_container(&iterator, code, signature, &sub.iterator);
336     }
337 }
338 
beginCommon(int code,const char * signature)339 QDBusMarshaller *QDBusMarshaller::beginCommon(int code, const char *signature)
340 {
341     QDBusMarshaller *d = new QDBusMarshaller(capabilities);
342     open(*d, code, signature);
343     return d;
344 }
345 
endStructure()346 inline QDBusMarshaller *QDBusMarshaller::endStructure()
347 { return endCommon(); }
348 
endArray()349 inline QDBusMarshaller *QDBusMarshaller::endArray()
350 { return endCommon(); }
351 
endMap()352 inline QDBusMarshaller *QDBusMarshaller::endMap()
353 { return endCommon(); }
354 
endMapEntry()355 inline QDBusMarshaller *QDBusMarshaller::endMapEntry()
356 { return endCommon(); }
357 
endCommon()358 QDBusMarshaller *QDBusMarshaller::endCommon()
359 {
360     QDBusMarshaller *retval = parent;
361     delete this;
362     return retval;
363 }
364 
close()365 void QDBusMarshaller::close()
366 {
367     if (ba) {
368         if (!skipSignature && closeCode)
369             *ba += closeCode;
370     } else if (parent) {
371         q_dbus_message_iter_close_container(&parent->iterator, &iterator);
372     }
373 }
374 
error(const QString & msg)375 void QDBusMarshaller::error(const QString &msg)
376 {
377     ok = false;
378     if (parent)
379         parent->error(msg);
380     else
381         errorString = msg;
382 }
383 
appendVariantInternal(const QVariant & arg)384 bool QDBusMarshaller::appendVariantInternal(const QVariant &arg)
385 {
386     int id = arg.userType();
387     if (id == QMetaType::UnknownType) {
388         qWarning("QDBusMarshaller: cannot add an invalid QVariant");
389         error(QLatin1String("Variant containing QVariant::Invalid passed in arguments"));
390         return false;
391     }
392 
393     // intercept QDBusArgument parameters here
394     if (id == QDBusMetaTypeId::argument()) {
395         QDBusArgument dbusargument = qvariant_cast<QDBusArgument>(arg);
396         QDBusArgumentPrivate *d = QDBusArgumentPrivate::d(dbusargument);
397         if (!d->message)
398             return false;       // can't append this one...
399 
400         QDBusDemarshaller demarshaller(capabilities);
401         demarshaller.message = q_dbus_message_ref(d->message);
402 
403         if (d->direction == Demarshalling) {
404             // it's demarshalling; just copy
405             demarshaller.iterator = static_cast<QDBusDemarshaller *>(d)->iterator;
406         } else {
407             // it's marshalling; start over
408             if (!q_dbus_message_iter_init(demarshaller.message, &demarshaller.iterator))
409                 return false;   // error!
410         }
411 
412         return appendCrossMarshalling(&demarshaller);
413     }
414 
415     const char *signature = QDBusMetaType::typeToSignature( QVariant::Type(id) );
416     if (!signature) {
417         qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. "
418                  "Use qDBusRegisterMetaType to register it",
419                  QMetaType::typeName(id), id);
420         error(QLatin1String("Unregistered type %1 passed in arguments")
421               .arg(QLatin1String(QMetaType::typeName(id))));
422         return false;
423     }
424 
425     switch (*signature) {
426 #ifdef __OPTIMIZE__
427     case DBUS_TYPE_BYTE:
428     case DBUS_TYPE_INT16:
429     case DBUS_TYPE_UINT16:
430     case DBUS_TYPE_INT32:
431     case DBUS_TYPE_UINT32:
432     case DBUS_TYPE_INT64:
433     case DBUS_TYPE_UINT64:
434     case DBUS_TYPE_DOUBLE:
435         qIterAppend(&iterator, ba, *signature, arg.constData());
436         return true;
437     case DBUS_TYPE_BOOLEAN:
438         append( arg.toBool() );
439         return true;
440 #else
441     case DBUS_TYPE_BYTE:
442         append( qvariant_cast<uchar>(arg) );
443         return true;
444     case DBUS_TYPE_BOOLEAN:
445         append( arg.toBool() );
446         return true;
447     case DBUS_TYPE_INT16:
448         append( qvariant_cast<short>(arg) );
449         return true;
450     case DBUS_TYPE_UINT16:
451         append( qvariant_cast<ushort>(arg) );
452         return true;
453     case DBUS_TYPE_INT32:
454         append( static_cast<dbus_int32_t>(arg.toInt()) );
455         return true;
456     case DBUS_TYPE_UINT32:
457         append( static_cast<dbus_uint32_t>(arg.toUInt()) );
458         return true;
459     case DBUS_TYPE_INT64:
460         append( arg.toLongLong() );
461         return true;
462     case DBUS_TYPE_UINT64:
463         append( arg.toULongLong() );
464         return true;
465     case DBUS_TYPE_DOUBLE:
466         append( arg.toDouble() );
467         return true;
468 #endif
469 
470     case DBUS_TYPE_STRING:
471         append( arg.toString() );
472         return true;
473     case DBUS_TYPE_OBJECT_PATH:
474         append( qvariant_cast<QDBusObjectPath>(arg) );
475         return true;
476     case DBUS_TYPE_SIGNATURE:
477         append( qvariant_cast<QDBusSignature>(arg) );
478         return true;
479 
480     // compound types:
481     case DBUS_TYPE_VARIANT:
482         // nested QVariant
483         return append( qvariant_cast<QDBusVariant>(arg) );
484 
485     case DBUS_TYPE_ARRAY:
486         // could be many things
487         // find out what kind of array it is
488         switch (arg.userType()) {
489         case QMetaType::QStringList:
490             append( arg.toStringList() );
491             return true;
492 
493         case QMetaType::QByteArray:
494             append( arg.toByteArray() );
495             return true;
496 
497         default:
498             ;
499         }
500         Q_FALLTHROUGH();
501 
502     case DBUS_TYPE_STRUCT:
503     case DBUS_STRUCT_BEGIN_CHAR:
504         return appendRegisteredType( arg );
505 
506     case DBUS_TYPE_DICT_ENTRY:
507     case DBUS_DICT_ENTRY_BEGIN_CHAR:
508         qFatal("QDBusMarshaller::appendVariantInternal got a DICT_ENTRY!");
509         return false;
510 
511     case DBUS_TYPE_UNIX_FD:
512         if (capabilities & QDBusConnection::UnixFileDescriptorPassing || ba) {
513             append(qvariant_cast<QDBusUnixFileDescriptor>(arg));
514             return true;
515         }
516         Q_FALLTHROUGH();
517 
518     default:
519         qWarning("QDBusMarshaller::appendVariantInternal: Found unknown D-BUS type '%s'",
520                  signature);
521         return false;
522     }
523 
524     return true;
525 }
526 
appendRegisteredType(const QVariant & arg)527 bool QDBusMarshaller::appendRegisteredType(const QVariant &arg)
528 {
529     ref.ref();                  // reference up
530     QDBusArgument self(QDBusArgumentPrivate::create(this));
531     return QDBusMetaType::marshall(self, arg.userType(), arg.constData());
532 }
533 
appendCrossMarshalling(QDBusDemarshaller * demarshaller)534 bool QDBusMarshaller::appendCrossMarshalling(QDBusDemarshaller *demarshaller)
535 {
536     int code = q_dbus_message_iter_get_arg_type(&demarshaller->iterator);
537     if (QDBusUtil::isValidBasicType(code)) {
538         // easy: just append
539         // do exactly like the D-BUS docs suggest
540         // (see apidocs for q_dbus_message_iter_get_basic)
541 
542         qlonglong value;
543         q_dbus_message_iter_get_basic(&demarshaller->iterator, &value);
544         q_dbus_message_iter_next(&demarshaller->iterator);
545         q_dbus_message_iter_append_basic(&iterator, code, &value);
546         return true;
547     }
548 
549     if (code == DBUS_TYPE_ARRAY) {
550         int element = q_dbus_message_iter_get_element_type(&demarshaller->iterator);
551         if (QDBusUtil::isValidFixedType(element) && element != DBUS_TYPE_UNIX_FD) {
552             // another optimization: fixed size arrays
553             // code is exactly like QDBusDemarshaller::toByteArray
554             DBusMessageIter sub;
555             q_dbus_message_iter_recurse(&demarshaller->iterator, &sub);
556             q_dbus_message_iter_next(&demarshaller->iterator);
557             int len;
558             void* data;
559             q_dbus_message_iter_get_fixed_array(&sub,&data,&len);
560 
561             char signature[2] = { char(element), 0 };
562             q_dbus_message_iter_open_container(&iterator, DBUS_TYPE_ARRAY, signature, &sub);
563             q_dbus_message_iter_append_fixed_array(&sub, element, &data, len);
564             q_dbus_message_iter_close_container(&iterator, &sub);
565 
566             return true;
567         }
568     }
569 
570     // We have to recurse
571     QDBusDemarshaller *drecursed = demarshaller->beginCommon();
572 
573     QDBusMarshaller mrecursed(capabilities);  // create on the stack makes it autoclose
574     QByteArray subSignature;
575     const char *sig = nullptr;
576     if (code == DBUS_TYPE_VARIANT || code == DBUS_TYPE_ARRAY) {
577         subSignature = drecursed->currentSignature().toLatin1();
578         if (!subSignature.isEmpty())
579             sig = subSignature.constData();
580     }
581     open(mrecursed, code, sig);
582 
583     while (!drecursed->atEnd()) {
584         if (!mrecursed.appendCrossMarshalling(drecursed)) {
585             delete drecursed;
586             return false;
587         }
588     }
589 
590     delete drecursed;
591     return true;
592 }
593 
594 QT_END_NAMESPACE
595 
596 #endif // QT_NO_DBUS
597