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 | |
11 | QT_BEGIN_NAMESPACE |
12 | |
13 | using namespace Qt::StringLiterals; |
14 | |
15 | static 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 | |
23 | QDBusMarshaller::~QDBusMarshaller() |
24 | { |
25 | close(); |
26 | } |
27 | |
28 | void 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 | |
38 | inline QString QDBusMarshaller::currentSignature() |
39 | { |
40 | if (message) |
41 | return QString::fromUtf8(utf8: q_dbus_message_get_signature(message)); |
42 | return QString(); |
43 | } |
44 | |
45 | inline void QDBusMarshaller::append(uchar arg) |
46 | { |
47 | if (!skipSignature) |
48 | qIterAppend(it: &iterator, ba, DBUS_TYPE_BYTE, arg: &arg); |
49 | } |
50 | |
51 | inline 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 | |
58 | inline void QDBusMarshaller::append(short arg) |
59 | { |
60 | if (!skipSignature) |
61 | qIterAppend(it: &iterator, ba, DBUS_TYPE_INT16, arg: &arg); |
62 | } |
63 | |
64 | inline void QDBusMarshaller::append(ushort arg) |
65 | { |
66 | if (!skipSignature) |
67 | qIterAppend(it: &iterator, ba, DBUS_TYPE_UINT16, arg: &arg); |
68 | } |
69 | |
70 | inline void QDBusMarshaller::append(int arg) |
71 | { |
72 | if (!skipSignature) |
73 | qIterAppend(it: &iterator, ba, DBUS_TYPE_INT32, arg: &arg); |
74 | } |
75 | |
76 | inline void QDBusMarshaller::append(uint arg) |
77 | { |
78 | if (!skipSignature) |
79 | qIterAppend(it: &iterator, ba, DBUS_TYPE_UINT32, arg: &arg); |
80 | } |
81 | |
82 | inline void QDBusMarshaller::append(qlonglong arg) |
83 | { |
84 | if (!skipSignature) |
85 | qIterAppend(it: &iterator, ba, DBUS_TYPE_INT64, arg: &arg); |
86 | } |
87 | |
88 | inline void QDBusMarshaller::append(qulonglong arg) |
89 | { |
90 | if (!skipSignature) |
91 | qIterAppend(it: &iterator, ba, DBUS_TYPE_UINT64, arg: &arg); |
92 | } |
93 | |
94 | inline void QDBusMarshaller::append(double arg) |
95 | { |
96 | if (!skipSignature) |
97 | qIterAppend(it: &iterator, ba, DBUS_TYPE_DOUBLE, arg: &arg); |
98 | } |
99 | |
100 | void 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 | |
108 | inline 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 | |
120 | inline 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 | |
132 | inline 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 | |
143 | inline 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 | |
159 | inline 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 | |
199 | inline 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 | |
214 | inline QDBusMarshaller *QDBusMarshaller::beginStructure() |
215 | { |
216 | return beginCommon(DBUS_TYPE_STRUCT, signature: nullptr); |
217 | } |
218 | |
219 | inline 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 | |
230 | inline 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)) { |
238 | QT_WARNING_PUSH |
239 | QT_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()); |
242 | QT_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 | |
262 | inline QDBusMarshaller *QDBusMarshaller::beginMapEntry() |
263 | { |
264 | return beginCommon(DBUS_TYPE_DICT_ENTRY, signature: nullptr); |
265 | } |
266 | |
267 | void 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 | |
299 | QDBusMarshaller *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 | |
306 | inline QDBusMarshaller *QDBusMarshaller::endStructure() |
307 | { return endCommon(); } |
308 | |
309 | inline QDBusMarshaller *QDBusMarshaller::endArray() |
310 | { return endCommon(); } |
311 | |
312 | inline QDBusMarshaller *QDBusMarshaller::endMap() |
313 | { return endCommon(); } |
314 | |
315 | inline QDBusMarshaller *QDBusMarshaller::endMapEntry() |
316 | { return endCommon(); } |
317 | |
318 | QDBusMarshaller *QDBusMarshaller::endCommon() |
319 | { |
320 | QDBusMarshaller *retval = parent; |
321 | delete this; |
322 | return retval; |
323 | } |
324 | |
325 | void 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 | |
335 | void QDBusMarshaller::error(const QString &msg) |
336 | { |
337 | ok = false; |
338 | if (parent) |
339 | parent->error(msg); |
340 | else |
341 | errorString = msg; |
342 | } |
343 | |
344 | bool 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 | |
483 | bool 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 | |
490 | bool 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 | |
550 | QT_END_NAMESPACE |
551 | |
552 | #endif // QT_NO_DBUS |
553 | |