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