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

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