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

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