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
47QT_BEGIN_NAMESPACE
48
49static 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
57QDBusMarshaller::~QDBusMarshaller()
58{
59 close();
60}
61
62inline QString QDBusMarshaller::currentSignature()
63{
64 if (message)
65 return QString::fromUtf8(q_dbus_message_get_signature(message));
66 return QString();
67}
68
69inline void QDBusMarshaller::append(uchar arg)
70{
71 if (!skipSignature)
72 qIterAppend(&iterator, ba, DBUS_TYPE_BYTE, &arg);
73}
74
75inline 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
82inline void QDBusMarshaller::append(short arg)
83{
84 if (!skipSignature)
85 qIterAppend(&iterator, ba, DBUS_TYPE_INT16, &arg);
86}
87
88inline void QDBusMarshaller::append(ushort arg)
89{
90 if (!skipSignature)
91 qIterAppend(&iterator, ba, DBUS_TYPE_UINT16, &arg);
92}
93
94inline void QDBusMarshaller::append(int arg)
95{
96 if (!skipSignature)
97 qIterAppend(&iterator, ba, DBUS_TYPE_INT32, &arg);
98}
99
100inline void QDBusMarshaller::append(uint arg)
101{
102 if (!skipSignature)
103 qIterAppend(&iterator, ba, DBUS_TYPE_UINT32, &arg);
104}
105
106inline void QDBusMarshaller::append(qlonglong arg)
107{
108 if (!skipSignature)
109 qIterAppend(&iterator, ba, DBUS_TYPE_INT64, &arg);
110}
111
112inline void QDBusMarshaller::append(qulonglong arg)
113{
114 if (!skipSignature)
115 qIterAppend(&iterator, ba, DBUS_TYPE_UINT64, &arg);
116}
117
118inline void QDBusMarshaller::append(double arg)
119{
120 if (!skipSignature)
121 qIterAppend(&iterator, ba, DBUS_TYPE_DOUBLE, &arg);
122}
123
124void 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
132inline 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
144inline 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
156inline 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
167inline 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
183inline 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 QMetaType id = value.metaType();
193 if (!id.isValid()) {
194 qWarning("QDBusMarshaller: cannot add a null QDBusVariant");
195 error(QLatin1String("Invalid QVariant 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 id.name(), id.id());
214 error(QLatin1String("Unregistered type %1 passed in arguments")
215 .arg(QLatin1String(id.name())));
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
227inline 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
244inline QDBusMarshaller *QDBusMarshaller::beginStructure()
245{
246 return beginCommon(DBUS_TYPE_STRUCT, nullptr);
247}
248
249inline QDBusMarshaller *QDBusMarshaller::beginArray(QMetaType id)
250{
251 const char *signature = QDBusMetaType::typeToSignature(id);
252 if (!signature) {
253 qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. "
254 "Use qDBusRegisterMetaType to register it",
255 id.name(), id.id());
256 error(QLatin1String("Unregistered type %1 passed in arguments")
257 .arg(QLatin1String(id.name())));
258 return this;
259 }
260
261 return beginCommon(DBUS_TYPE_ARRAY, signature);
262}
263
264inline QDBusMarshaller *QDBusMarshaller::beginMap(QMetaType kid, QMetaType vid)
265{
266 const char *ksignature = QDBusMetaType::typeToSignature(kid);
267 if (!ksignature) {
268 qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. "
269 "Use qDBusRegisterMetaType to register it",
270 kid.name(), kid.id());
271 error(QLatin1String("Unregistered type %1 passed in arguments")
272 .arg(QLatin1String(kid.name())));
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 kid.name(), kid.id());
278 error(QLatin1String("Type %1 passed in arguments cannot be used as a key in a map")
279 .arg(QLatin1String(kid.name())));
280 return this;
281 }
282
283 const char *vsignature = QDBusMetaType::typeToSignature(vid);
284 if (!vsignature) {
285 const char *typeName = vid.name();
286 qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. "
287 "Use qDBusRegisterMetaType to register it",
288 typeName, vid.id());
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
302inline QDBusMarshaller *QDBusMarshaller::beginMapEntry()
303{
304 return beginCommon(DBUS_TYPE_DICT_ENTRY, nullptr);
305}
306
307void 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
339QDBusMarshaller *QDBusMarshaller::beginCommon(int code, const char *signature)
340{
341 QDBusMarshaller *d = new QDBusMarshaller(capabilities);
342 open(*d, code, signature);
343 return d;
344}
345
346inline QDBusMarshaller *QDBusMarshaller::endStructure()
347{ return endCommon(); }
348
349inline QDBusMarshaller *QDBusMarshaller::endArray()
350{ return endCommon(); }
351
352inline QDBusMarshaller *QDBusMarshaller::endMap()
353{ return endCommon(); }
354
355inline QDBusMarshaller *QDBusMarshaller::endMapEntry()
356{ return endCommon(); }
357
358QDBusMarshaller *QDBusMarshaller::endCommon()
359{
360 QDBusMarshaller *retval = parent;
361 delete this;
362 return retval;
363}
364
365void 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
375void QDBusMarshaller::error(const QString &msg)
376{
377 ok = false;
378 if (parent)
379 parent->error(msg);
380 else
381 errorString = msg;
382}
383
384bool QDBusMarshaller::appendVariantInternal(const QVariant &arg)
385{
386 QMetaType id = arg.metaType();
387 if (!id.isValid()) {
388 qWarning("QDBusMarshaller: cannot add an invalid QVariant");
389 error(QLatin1String("Invalid QVariant 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(id);
416 if (!signature) {
417 qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. "
418 "Use qDBusRegisterMetaType to register it",
419 id.name(), id.id());
420 error(QLatin1String("Unregistered type %1 passed in arguments")
421 .arg(QLatin1String(id.name())));
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.metaType().id()) {
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
527bool QDBusMarshaller::appendRegisteredType(const QVariant &arg)
528{
529 ref.ref(); // reference up
530 QDBusArgument self(QDBusArgumentPrivate::create(this));
531 return QDBusMetaType::marshall(self, arg.metaType(), arg.constData());
532}
533
534bool 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
594QT_END_NAMESPACE
595
596#endif // QT_NO_DBUS
597