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