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