* dbus/dbus-sysdeps.c: Make tcp socket connection error somewhat more
[platform/upstream/dbus.git] / qt / src / qdbusmarshall.cpp
1 /* qdbusmarshall.cpp
2  *
3  * Copyright (C) 2005 Harald Fernengel <harry@kdevelop.org>
4  * Copyright (C) 2006 Trolltech AS. All rights reserved.
5  *    Author: Thiago Macieira <thiago.macieira@trolltech.com>
6  *
7  * Licensed under the Academic Free License version 2.1
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation
21  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22  *
23  */
24
25 #include "qdbusmarshall_p.h"
26 #include "qdbustype_p.h"
27 #include "qdbustypehelper_p.h"
28
29 #include <qdebug.h>
30 #include <qvariant.h>
31 #include <qlist.h>
32 #include <qmap.h>
33 #include <qstringlist.h>
34 #include <qvarlengtharray.h>
35 #include <qvector.h>
36
37 #include <dbus/dbus.h>
38
39 static QVariant qFetchParameter(DBusMessageIter *it);
40
41 template <typename T>
42 inline T qIterGet(DBusMessageIter *it)
43 {
44     T t;
45     dbus_message_iter_get_basic(it, &t);
46     return t;
47 }
48
49 template<>
50 inline QVariant qIterGet(DBusMessageIter *it)
51 {
52     DBusMessageIter sub;
53     dbus_message_iter_recurse(it, &sub);
54     return QDBusTypeHelper<QVariant>::toVariant(qFetchParameter(&sub));
55 }    
56
57 template <typename DBusType, typename QtType>
58 inline QVariant qFetchList(DBusMessageIter *arrayIt)
59 {
60     QList<QtType> list;
61
62     DBusMessageIter it;
63     dbus_message_iter_recurse(arrayIt, &it);
64     if (dbus_message_iter_get_array_len(&it) == 0)
65         return QDBusTypeHelper<QList<QtType> >::toVariant(list);
66
67     do {
68         list.append( static_cast<QtType>( qIterGet<DBusType>(&it) ) );
69     } while (dbus_message_iter_next(&it));
70
71     return QDBusTypeHelper<QList<QtType> >::toVariant(list);
72 }
73
74 static QStringList qFetchStringList(DBusMessageIter *arrayIt)
75 {
76     QStringList list;
77
78     DBusMessageIter it;
79     dbus_message_iter_recurse(arrayIt, &it);
80     if (dbus_message_iter_get_array_len(&it) == 0)
81         return list;
82
83     do {
84         list.append(QString::fromUtf8(qIterGet<char *>(&it)));
85     } while (dbus_message_iter_next(&it));
86
87     return list;
88 }
89
90 static QVariant qFetchParameter(DBusMessageIter *it)
91 {
92     switch (dbus_message_iter_get_arg_type(it)) {
93     case DBUS_TYPE_BYTE:
94         return qVariantFromValue(qIterGet<unsigned char>(it));
95     case DBUS_TYPE_INT16:
96         return qVariantFromValue(qIterGet<dbus_int16_t>(it));
97     case DBUS_TYPE_UINT16:
98         return qVariantFromValue(qIterGet<dbus_uint16_t>(it));
99     case DBUS_TYPE_INT32:
100         return qIterGet<dbus_int32_t>(it);
101     case DBUS_TYPE_UINT32:
102         return qIterGet<dbus_uint32_t>(it);
103     case DBUS_TYPE_DOUBLE:
104         return qIterGet<double>(it);
105     case DBUS_TYPE_BOOLEAN:
106         return bool(qIterGet<dbus_bool_t>(it));
107     case DBUS_TYPE_INT64:
108         return static_cast<qlonglong>(qIterGet<dbus_int64_t>(it));
109     case DBUS_TYPE_UINT64:
110         return static_cast<qulonglong>(qIterGet<dbus_uint64_t>(it));
111     case DBUS_TYPE_STRING:
112     case DBUS_TYPE_OBJECT_PATH:
113     case DBUS_TYPE_SIGNATURE:
114         return QString::fromUtf8(qIterGet<char *>(it));
115     case DBUS_TYPE_VARIANT:
116         return qIterGet<QVariant>(it);
117     case DBUS_TYPE_ARRAY: {
118         int arrayType = dbus_message_iter_get_element_type(it);
119         switch (arrayType)
120         {
121         case DBUS_TYPE_BYTE: {
122             // QByteArray
123             DBusMessageIter sub;
124             dbus_message_iter_recurse(it, &sub);
125             int len = dbus_message_iter_get_array_len(&sub);
126             char* data;
127             dbus_message_iter_get_fixed_array(&sub,&data,&len);
128             return QByteArray(data,len);
129         }
130         case DBUS_TYPE_INT16:
131             return qFetchList<dbus_int16_t, short>(it);
132         case DBUS_TYPE_UINT16:
133             return qFetchList<dbus_uint16_t, ushort>(it);
134         case DBUS_TYPE_INT32:
135             return qFetchList<dbus_int32_t, int>(it);
136         case DBUS_TYPE_UINT32:
137             return qFetchList<dbus_uint32_t, uint>(it);
138         case DBUS_TYPE_BOOLEAN:
139             return qFetchList<dbus_bool_t, bool>(it);
140         case DBUS_TYPE_DOUBLE:
141             return qFetchList<double, double>(it);
142         case DBUS_TYPE_INT64:
143             return qFetchList<dbus_int64_t, qlonglong>(it);
144         case DBUS_TYPE_UINT64:
145             return qFetchList<dbus_uint64_t, qulonglong>(it);
146         case DBUS_TYPE_STRING:
147         case DBUS_TYPE_OBJECT_PATH:
148         case DBUS_TYPE_SIGNATURE:
149             return qFetchStringList(it);
150         case DBUS_TYPE_VARIANT:
151             return qFetchList<QVariant, QVariant>(it);
152         case DBUS_TYPE_DICT_ENTRY: {
153             // ### support other types of maps?
154             QMap<QString, QVariant> map;
155             DBusMessageIter sub;
156             
157             dbus_message_iter_recurse(it, &sub);
158             if (dbus_message_iter_get_array_len(&sub) == 0)
159                 // empty map
160                 return map;
161             
162             do {
163                 DBusMessageIter itemIter;
164                 dbus_message_iter_recurse(&sub, &itemIter);
165                 Q_ASSERT(dbus_message_iter_has_next(&itemIter));
166                 QString key = qFetchParameter(&itemIter).toString();
167                 dbus_message_iter_next(&itemIter);
168                 map.insertMulti(key, qFetchParameter(&itemIter));
169             } while (dbus_message_iter_next(&sub));
170             return map;
171         }
172         }
173     }
174     // fall through
175     // common handling for structs and lists of lists (for now)
176     case DBUS_TYPE_STRUCT: {
177         QList<QVariant> list;
178         DBusMessageIter sub;
179         dbus_message_iter_recurse(it, &sub);
180         if (dbus_message_iter_get_array_len(&sub) == 0)
181             return list;
182         do {
183             list.append(qFetchParameter(&sub));
184         } while (dbus_message_iter_next(&sub));
185         return list;
186     }
187
188     default:
189         qWarning("Don't know how to handle type %d '%c'", dbus_message_iter_get_arg_type(it), dbus_message_iter_get_arg_type(it));
190         return QVariant();
191         break;
192     }
193 }
194
195 void QDBusMarshall::messageToList(QList<QVariant> &list, DBusMessage *message)
196 {
197     Q_ASSERT(message);
198
199     DBusMessageIter it;
200     if (!dbus_message_iter_init(message, &it))
201         return;
202
203     do {
204         list.append(qFetchParameter(&it));
205     } while (dbus_message_iter_next(&it));
206 }
207
208 // convert the variant to the given type and return true if it worked.
209 // if the type is not known, guess it from the variant and set.
210 // return false if conversion failed.
211 static bool checkType(QVariant &var, QDBusType &type)
212 {
213     if (!type.isValid()) {
214         // guess it from the variant
215         type = QDBusType::guessFromVariant(var);
216         return true;
217     }
218
219     int id = var.userType(); 
220     
221     if (type.dbusType() == DBUS_TYPE_VARIANT) {
222         // this is a non symmetrical operation:
223         // nest a QVariant if we want variant and it isn't so
224         if (id != QDBusTypeHelper<QVariant>::id()) {
225             QVariant tmp = var;
226             var = QDBusTypeHelper<QVariant>::toVariant(tmp);
227         }
228         return true;
229     }
230
231     switch (id) {
232     case QVariant::Bool:
233     case QMetaType::Short: 
234     case QMetaType::UShort:
235     case QMetaType::UChar:
236     case QVariant::Int:
237     case QVariant::UInt:
238     case QVariant::LongLong:
239     case QVariant::ULongLong:
240     case QVariant::Double:
241     case QVariant::String:
242         if (type.isBasic())
243             // QVariant can handle this on its own
244             return true;
245
246         // cannot handle this
247         qWarning("Invalid conversion from %s to '%s'", var.typeName(),
248                  type.dbusSignature().constData());
249         var.clear();
250         return false;
251
252     case QVariant::ByteArray:
253         // make sure it's an "ARRAY of BYTE"
254         if (type.qvariantType() != QVariant::ByteArray) {
255             qWarning("Invalid conversion from %s to '%s'", var.typeName(),
256                      type.dbusSignature().constData());
257             var.clear();
258             return false;
259         }
260         return true;
261
262     case QVariant::StringList:
263         // make sure it's "ARRAY of STRING"
264         if (type.qvariantType() != QVariant::StringList) {
265             qWarning("Invalid conversion from %s to '%s'", var.typeName(),
266                      type.dbusSignature().constData());
267             var.clear();
268             return false;
269         }
270         return true;
271
272     case QVariant::List:
273         // could be either struct or array
274         if (type.dbusType() != DBUS_TYPE_ARRAY && type.dbusType() != DBUS_TYPE_STRUCT) {
275             qWarning("Invalid conversion from %s to '%s'", var.typeName(),
276                      type.dbusSignature().constData());
277             var.clear();
278             return false;
279         }
280         
281         return true;
282
283     case QVariant::Map:
284         if (!type.isMap()) {
285             qWarning("Invalid conversion from %s to '%s'", var.typeName(),
286                      type.dbusSignature().constData());
287             var.clear();
288             return false;
289         }
290
291         return true;
292
293     case QVariant::Invalid: {
294         // create an empty variant
295         void *null = 0;
296         var = QVariant(type.qvariantType(), null);
297         break;
298     }
299
300     default:
301         if (id == QDBusTypeHelper<QVariant>::id()) {
302             // if we got here, it means the match above for DBUS_TYPE_VARIANT didn't work
303             qWarning("Invalid conversion from nested variant to '%s'",
304                      type.dbusSignature().constData());
305             return false;
306         } else if (type.dbusType() == DBUS_TYPE_ARRAY) {
307             int subType = type.arrayElement().dbusType();
308             if ((id == QDBusTypeHelper<bool>::listId() && subType == DBUS_TYPE_BOOLEAN) ||
309                 (id == QDBusTypeHelper<short>::listId() && subType == DBUS_TYPE_INT16) ||
310                 (id == QDBusTypeHelper<ushort>::listId() && subType == DBUS_TYPE_UINT16) ||
311                 (id == QDBusTypeHelper<int>::listId() && subType == DBUS_TYPE_INT32) ||
312                 (id == QDBusTypeHelper<uint>::listId() && subType == DBUS_TYPE_UINT32) ||
313                 (id == QDBusTypeHelper<qlonglong>::listId() && subType == DBUS_TYPE_INT64) ||
314                 (id == QDBusTypeHelper<qulonglong>::listId() && subType == DBUS_TYPE_UINT64) ||
315                 (id == QDBusTypeHelper<double>::listId() && subType == DBUS_TYPE_DOUBLE))
316                 return true;
317         }
318
319         qWarning("Invalid conversion from %s to '%s'", var.typeName(),
320                  type.dbusSignature().constData());
321         return false;
322     }
323
324     qWarning("Found unknown QVariant type %d (%s) when converting to DBus", (int)var.type(),
325              var.typeName());
326     var.clear();
327     return false;
328 }
329
330 static void qVariantToIteratorInternal(DBusMessageIter *it, const QVariant &var,
331                                        const QDBusType &type);
332
333 static void qListToIterator(DBusMessageIter *it, const QList<QVariant> &list,
334                             const QDBusTypeList &typelist);
335
336 template<typename T>
337 static void qIterAppend(DBusMessageIter *it, const QDBusType &type, T arg)
338 {
339     dbus_message_iter_append_basic(it, type.dbusType(), &arg);
340 }
341
342 template<typename DBusType, typename QtType>
343 static void qAppendListToMessage(DBusMessageIter *it, const QDBusType &subType,
344                                  const QVariant &var)
345 {
346     QList<QtType> list = QDBusTypeHelper<QList<QtType> >::fromVariant(var);
347     foreach (const QtType &item, list)
348         qIterAppend(it, subType, static_cast<DBusType>(item));
349 }
350
351 static void qAppendArrayToMessage(DBusMessageIter *it, const QDBusType &subType,
352                                   const QVariant &var)
353 {
354     DBusMessageIter sub;
355     dbus_message_iter_open_container(it, DBUS_TYPE_ARRAY, subType.dbusSignature(), &sub);
356
357     switch (var.type())
358     {
359     case QVariant::StringList: {
360         const QStringList list = var.toStringList();
361         foreach (QString str, list)
362             qIterAppend(&sub, subType, str.toUtf8().constData());
363         break;
364     }
365
366     case QVariant::ByteArray: {
367         const QByteArray array = var.toByteArray();
368         const char* cdata = array.constData();
369         dbus_message_iter_append_fixed_array(&sub, DBUS_TYPE_BYTE, &cdata, array.length());
370         break;
371     }
372
373     case QVariant::Map: {
374         const QVariantMap map = var.toMap();
375         const QDBusTypeList& subTypes = subType.subTypes();
376         for (QMap<QString, QVariant>::const_iterator mit = map.constBegin();
377              mit != map.constEnd(); ++mit) {
378             DBusMessageIter itemIterator;
379             dbus_message_iter_open_container(&sub, DBUS_TYPE_DICT_ENTRY, 0, &itemIterator);
380             
381             // let the string be converted to QVariant
382             qVariantToIteratorInternal(&itemIterator, mit.key(), subTypes[0]);
383             qVariantToIteratorInternal(&itemIterator, mit.value(), subTypes[1]);
384             
385             dbus_message_iter_close_container(&sub, &itemIterator);
386         }
387         break;
388     }
389
390     case QVariant::List: {
391         const QVariantList list = var.toList();
392         foreach (QVariant v, list)
393             qVariantToIteratorInternal(&sub, v, subType);
394         break;        
395     }
396
397     default: {
398         int id = var.userType();
399         if (id == QDBusTypeHelper<bool>::listId())
400             qAppendListToMessage<dbus_bool_t,bool>(&sub, subType, var);
401         else if (id == QDBusTypeHelper<short>::listId())
402             qAppendListToMessage<dbus_int16_t,short>(&sub, subType, var);
403         else if (id == QDBusTypeHelper<ushort>::listId())
404             qAppendListToMessage<dbus_uint16_t,ushort>(&sub, subType, var);
405         else if (id == QDBusTypeHelper<int>::listId())
406             qAppendListToMessage<dbus_int32_t,int>(&sub, subType, var);
407         else if (id == QDBusTypeHelper<uint>::listId())
408             qAppendListToMessage<dbus_uint32_t,uint>(&sub, subType, var);
409         else if (id == QDBusTypeHelper<qlonglong>::listId())
410             qAppendListToMessage<dbus_int64_t,qlonglong>(&sub, subType, var);
411         else if (id == QDBusTypeHelper<qulonglong>::listId())
412             qAppendListToMessage<dbus_uint64_t,qulonglong>(&sub, subType, var);
413         else if (id == QDBusTypeHelper<double>::listId())
414             qAppendListToMessage<double,double>(&sub, subType, var);
415 #if 0   // never reached, since QVariant::List mached
416         else if (id == QDBusTypeHelper<QVariant>::listId())
417             qAppendListToMessage<QVariant,QVariant>(&sub, subType, var);
418 #endif
419         else
420             qFatal("qAppendArrayToMessage got unknown type!");
421         break;
422     }
423     }
424     
425     dbus_message_iter_close_container(it, &sub);
426 }
427
428 static void qAppendStructToMessage(DBusMessageIter *it, const QDBusTypeList &typeList,
429                                    const QVariantList &list)
430 {
431     DBusMessageIter sub;
432     dbus_message_iter_open_container(it, DBUS_TYPE_STRUCT, NULL, &sub);
433     qListToIterator(&sub, list, typeList);
434     dbus_message_iter_close_container(it, &sub);
435 }
436
437 static void qAppendVariantToMessage(DBusMessageIter *it, const QDBusType &type,
438                                     const QVariant &var)
439 {
440     Q_UNUSED(type);             // type is 'v'
441
442     QVariant arg = var;
443     if (var.userType() == QDBusTypeHelper<QVariant>::id())
444         arg = QDBusTypeHelper<QVariant>::fromVariant(var); // extract the inner variant
445     
446     QDBusType t = QDBusType::guessFromVariant(arg);
447     
448     // now add this variant
449     DBusMessageIter sub;
450     dbus_message_iter_open_container(it, DBUS_TYPE_VARIANT, t.dbusSignature(), &sub);
451     qVariantToIteratorInternal(&sub, arg, t);
452     dbus_message_iter_close_container(it, &sub);
453 }
454
455 static void qVariantToIterator(DBusMessageIter *it, QVariant var, QDBusType type)
456 {
457     if (var.isNull() && !type.isValid())
458         return;                 // cannot add a null like this
459     if (!checkType(var, type))
460         return;                 // type checking failed
461
462     qVariantToIteratorInternal(it, var, type);
463 }
464
465 static void qVariantToIteratorInternal(DBusMessageIter *it, const QVariant &var,
466                                        const QDBusType &type)
467 {
468     switch (type.dbusType()) {
469     case DBUS_TYPE_BYTE:
470         qIterAppend( it, type, QDBusTypeHelper<uchar>::fromVariant(var) );
471         break;
472     case DBUS_TYPE_BOOLEAN:
473         qIterAppend( it, type, static_cast<dbus_bool_t>(var.toBool()) );
474         break;
475     case DBUS_TYPE_INT16:
476         qIterAppend( it, type, QDBusTypeHelper<short>::fromVariant(var) );
477         break;
478     case DBUS_TYPE_UINT16:
479         qIterAppend( it, type, QDBusTypeHelper<ushort>::fromVariant(var) );
480         break;
481     case DBUS_TYPE_INT32:
482         qIterAppend( it, type, static_cast<dbus_int32_t>(var.toInt()) );
483         break;
484     case DBUS_TYPE_UINT32:
485         qIterAppend( it, type, static_cast<dbus_uint32_t>(var.toUInt()) );
486         break;
487     case DBUS_TYPE_INT64:
488         qIterAppend( it, type, static_cast<dbus_int64_t>(var.toLongLong()) );
489         break;
490     case DBUS_TYPE_UINT64:
491         qIterAppend( it, type, static_cast<dbus_uint64_t>(var.toULongLong()) );
492         break;
493     case DBUS_TYPE_DOUBLE:
494         qIterAppend( it, type, var.toDouble() );
495         break;
496     case DBUS_TYPE_STRING:
497     case DBUS_TYPE_OBJECT_PATH:
498     case DBUS_TYPE_SIGNATURE:
499         qIterAppend( it, type, var.toString().toUtf8().constData() );
500         break;
501
502     // compound types:
503     case DBUS_TYPE_ARRAY:
504         // could be many things
505         qAppendArrayToMessage( it, type.arrayElement(), var );
506         break;
507
508     case DBUS_TYPE_VARIANT:
509         qAppendVariantToMessage( it, type, var );
510         break;
511
512     case DBUS_TYPE_STRUCT:
513         qAppendStructToMessage( it, type.subTypes(), var.toList() );
514         break;
515
516     case DBUS_TYPE_DICT_ENTRY:
517         qFatal("qVariantToIterator got a DICT_ENTRY!");
518         break;
519
520     default:
521         qWarning("Found unknown DBus type '%s'", type.dbusSignature().constData());
522         break;
523     }
524 }
525
526 void qListToIterator(DBusMessageIter *it, const QList<QVariant> &list)
527 {
528     for (int i = 0; i < list.count(); ++i)
529         qVariantToIterator(it, list.at(i), QDBusType());
530 }
531
532 void qListToIterator(DBusMessageIter *it, const QList<QVariant> &list, const QDBusTypeList &types)
533 {
534     int min = qMin(list.count(), types.count());
535     for (int i = 0; i < min; ++i)
536         qVariantToIterator(it, list.at(i), types.at(i));
537
538     for (int i = min; i < types.count(); ++i)
539         // we're missing a few arguments, so add default parameters
540         qVariantToIterator(it, QVariant(), types.at(i));
541 }
542     
543 void QDBusMarshall::listToMessage(const QList<QVariant> &list, DBusMessage *msg,
544                                   const QString &signature)
545 {
546     Q_ASSERT(msg);
547     DBusMessageIter it;
548     dbus_message_iter_init_append(msg, &it);
549
550     if (signature.isEmpty())
551         qListToIterator(&it, list);
552     else
553         qListToIterator(&it, list, QDBusTypeList(signature.toUtf8()));
554 }