/* qdbusmarshall.cpp * * Copyright (C) 2005 Harald Fernengel * Copyright (C) 2006 Trolltech AS. All rights reserved. * Author: Thiago Macieira * * Licensed under the Academic Free License version 2.1 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "qdbusmarshall_p.h" #include "qdbustype_p.h" #include "qdbustypehelper_p.h" #include #include #include #include #include #include #include #include static QVariant qFetchParameter(DBusMessageIter *it); template inline T qIterGet(DBusMessageIter *it) { T t; dbus_message_iter_get_basic(it, &t); return t; } template<> inline QVariant qIterGet(DBusMessageIter *it) { DBusMessageIter sub; dbus_message_iter_recurse(it, &sub); return QDBusTypeHelper::toVariant(qFetchParameter(&sub)); } template inline QVariant qFetchList(DBusMessageIter *arrayIt) { QList list; DBusMessageIter it; dbus_message_iter_recurse(arrayIt, &it); if (dbus_message_iter_get_array_len(&it) == 0) return QDBusTypeHelper >::toVariant(list); do { list.append( static_cast( qIterGet(&it) ) ); } while (dbus_message_iter_next(&it)); return QDBusTypeHelper >::toVariant(list); } static QStringList qFetchStringList(DBusMessageIter *arrayIt) { QStringList list; DBusMessageIter it; dbus_message_iter_recurse(arrayIt, &it); if (dbus_message_iter_get_array_len(&it) == 0) return list; do { list.append(QString::fromUtf8(qIterGet(&it))); } while (dbus_message_iter_next(&it)); return list; } static QVariant qFetchParameter(DBusMessageIter *it) { switch (dbus_message_iter_get_arg_type(it)) { case DBUS_TYPE_BYTE: return qVariantFromValue(qIterGet(it)); case DBUS_TYPE_INT16: return qVariantFromValue(qIterGet(it)); case DBUS_TYPE_UINT16: return qVariantFromValue(qIterGet(it)); case DBUS_TYPE_INT32: return qIterGet(it); case DBUS_TYPE_UINT32: return qIterGet(it); case DBUS_TYPE_DOUBLE: return qIterGet(it); case DBUS_TYPE_BOOLEAN: return bool(qIterGet(it)); case DBUS_TYPE_INT64: return static_cast(qIterGet(it)); case DBUS_TYPE_UINT64: return static_cast(qIterGet(it)); case DBUS_TYPE_STRING: case DBUS_TYPE_OBJECT_PATH: case DBUS_TYPE_SIGNATURE: return QString::fromUtf8(qIterGet(it)); case DBUS_TYPE_VARIANT: return qIterGet(it); case DBUS_TYPE_ARRAY: { int arrayType = dbus_message_iter_get_element_type(it); switch (arrayType) { case DBUS_TYPE_BYTE: { // QByteArray DBusMessageIter sub; dbus_message_iter_recurse(it, &sub); int len = dbus_message_iter_get_array_len(&sub); char* data; dbus_message_iter_get_fixed_array(&sub,&data,&len); return QByteArray(data,len); } case DBUS_TYPE_INT16: return qFetchList(it); case DBUS_TYPE_UINT16: return qFetchList(it); case DBUS_TYPE_INT32: return qFetchList(it); case DBUS_TYPE_UINT32: return qFetchList(it); case DBUS_TYPE_BOOLEAN: return qFetchList(it); case DBUS_TYPE_DOUBLE: return qFetchList(it); case DBUS_TYPE_INT64: return qFetchList(it); case DBUS_TYPE_UINT64: return qFetchList(it); case DBUS_TYPE_STRING: case DBUS_TYPE_OBJECT_PATH: case DBUS_TYPE_SIGNATURE: return qFetchStringList(it); case DBUS_TYPE_VARIANT: return qFetchList(it); case DBUS_TYPE_DICT_ENTRY: { // ### support other types of maps? QMap map; DBusMessageIter sub; dbus_message_iter_recurse(it, &sub); if (dbus_message_iter_get_array_len(&sub) == 0) // empty map return map; do { DBusMessageIter itemIter; dbus_message_iter_recurse(&sub, &itemIter); Q_ASSERT(dbus_message_iter_has_next(&itemIter)); QString key = qFetchParameter(&itemIter).toString(); dbus_message_iter_next(&itemIter); map.insertMulti(key, qFetchParameter(&itemIter)); } while (dbus_message_iter_next(&sub)); return map; } } } // fall through // common handling for structs and lists of lists (for now) case DBUS_TYPE_STRUCT: { QList list; DBusMessageIter sub; dbus_message_iter_recurse(it, &sub); if (dbus_message_iter_get_array_len(&sub) == 0) return list; do { list.append(qFetchParameter(&sub)); } while (dbus_message_iter_next(&sub)); return list; } default: qWarning("Don't know how to handle type %d '%c'", dbus_message_iter_get_arg_type(it), dbus_message_iter_get_arg_type(it)); return QVariant(); break; } } void QDBusMarshall::messageToList(QList &list, DBusMessage *message) { Q_ASSERT(message); DBusMessageIter it; if (!dbus_message_iter_init(message, &it)) return; do { list.append(qFetchParameter(&it)); } while (dbus_message_iter_next(&it)); } // convert the variant to the given type and return true if it worked. // if the type is not known, guess it from the variant and set. // return false if conversion failed. static bool checkType(QVariant &var, QDBusType &type) { if (!type.isValid()) { // guess it from the variant type = QDBusType::guessFromVariant(var); return type.isValid(); } int id = var.userType(); if (type.dbusType() == DBUS_TYPE_VARIANT) { // this is a non symmetrical operation: // nest a QVariant if we want variant and it isn't so if (id != QDBusTypeHelper::id()) { QVariant tmp = var; var = QDBusTypeHelper::toVariant(tmp); } return true; } switch (id) { case QVariant::Bool: case QMetaType::Short: case QMetaType::UShort: case QMetaType::UChar: case QVariant::Int: case QVariant::UInt: case QVariant::LongLong: case QVariant::ULongLong: case QVariant::Double: case QVariant::String: if (type.isBasic()) // QVariant can handle this on its own return true; // cannot handle this qWarning("Invalid conversion from %s to '%s'", var.typeName(), type.dbusSignature().constData()); var.clear(); return false; case QVariant::ByteArray: // make sure it's an "ARRAY of BYTE" if (type.qvariantType() != QVariant::ByteArray) { qWarning("Invalid conversion from %s to '%s'", var.typeName(), type.dbusSignature().constData()); var.clear(); return false; } return true; case QVariant::StringList: // make sure it's "ARRAY of STRING" if (type.qvariantType() != QVariant::StringList) { qWarning("Invalid conversion from %s to '%s'", var.typeName(), type.dbusSignature().constData()); var.clear(); return false; } return true; case QVariant::List: // could be either struct or array if (type.dbusType() != DBUS_TYPE_ARRAY && type.dbusType() != DBUS_TYPE_STRUCT) { qWarning("Invalid conversion from %s to '%s'", var.typeName(), type.dbusSignature().constData()); var.clear(); return false; } return true; case QVariant::Map: if (!type.isMap()) { qWarning("Invalid conversion from %s to '%s'", var.typeName(), type.dbusSignature().constData()); var.clear(); return false; } return true; case QVariant::Invalid: { // create an empty variant void *null = 0; var = QVariant(type.qvariantType(), null); break; } default: if (id == QDBusTypeHelper::id()) { // if we got here, it means the match above for DBUS_TYPE_VARIANT didn't work qWarning("Invalid conversion from nested variant to '%s'", type.dbusSignature().constData()); return false; } else if (type.dbusType() == DBUS_TYPE_ARRAY) { int subType = type.arrayElement().dbusType(); if ((id == QDBusTypeHelper::listId() && subType == DBUS_TYPE_BOOLEAN) || (id == QDBusTypeHelper::listId() && subType == DBUS_TYPE_INT16) || (id == QDBusTypeHelper::listId() && subType == DBUS_TYPE_UINT16) || (id == QDBusTypeHelper::listId() && subType == DBUS_TYPE_INT32) || (id == QDBusTypeHelper::listId() && subType == DBUS_TYPE_UINT32) || (id == QDBusTypeHelper::listId() && subType == DBUS_TYPE_INT64) || (id == QDBusTypeHelper::listId() && subType == DBUS_TYPE_UINT64) || (id == QDBusTypeHelper::listId() && subType == DBUS_TYPE_DOUBLE)) return true; } qWarning("Invalid conversion from %s to '%s'", var.typeName(), type.dbusSignature().constData()); return false; } qWarning("Found unknown QVariant type %d (%s) when converting to DBus", (int)var.type(), var.typeName()); var.clear(); return false; } static bool qVariantToIteratorInternal(DBusMessageIter *it, const QVariant &var, const QDBusType &type); static bool qListToIterator(DBusMessageIter *it, const QList &list, const QDBusTypeList &typelist); template static void qIterAppend(DBusMessageIter *it, const QDBusType &type, T arg) { dbus_message_iter_append_basic(it, type.dbusType(), &arg); } template static void qAppendListToMessage(DBusMessageIter *it, const QDBusType &subType, const QVariant &var) { QList list = QDBusTypeHelper >::fromVariant(var); foreach (const QtType &item, list) qIterAppend(it, subType, static_cast(item)); } static bool qAppendArrayToMessage(DBusMessageIter *it, const QDBusType &subType, const QVariant &var) { bool ok = false; DBusMessageIter sub; dbus_message_iter_open_container(it, DBUS_TYPE_ARRAY, subType.dbusSignature(), &sub); switch (var.type()) { case QVariant::StringList: { const QStringList list = var.toStringList(); foreach (QString str, list) qIterAppend(&sub, subType, str.toUtf8().constData()); ok = true; break; } case QVariant::ByteArray: { const QByteArray array = var.toByteArray(); const char* cdata = array.constData(); dbus_message_iter_append_fixed_array(&sub, DBUS_TYPE_BYTE, &cdata, array.length()); ok = true; break; } case QVariant::Map: { const QVariantMap map = var.toMap(); const QDBusTypeList& subTypes = subType.subTypes(); ok = true; for (QMap::const_iterator mit = map.constBegin(); ok && mit != map.constEnd(); ++mit) { DBusMessageIter itemIterator; dbus_message_iter_open_container(&sub, DBUS_TYPE_DICT_ENTRY, 0, &itemIterator); // let the string be converted to QVariant if (!qVariantToIteratorInternal(&itemIterator, mit.key(), subTypes[0])) ok = false; else if (!qVariantToIteratorInternal(&itemIterator, mit.value(), subTypes[1])) ok = false; dbus_message_iter_close_container(&sub, &itemIterator); } break; } case QVariant::List: { const QVariantList list = var.toList(); ok = true; foreach (QVariant v, list) if (!qVariantToIteratorInternal(&sub, v, subType)) { ok = false; break; } break; } default: { int id = var.userType(); ok = true; if (id == QDBusTypeHelper::listId()) qAppendListToMessage(&sub, subType, var); else if (id == QDBusTypeHelper::listId()) qAppendListToMessage(&sub, subType, var); else if (id == QDBusTypeHelper::listId()) qAppendListToMessage(&sub, subType, var); else if (id == QDBusTypeHelper::listId()) qAppendListToMessage(&sub, subType, var); else if (id == QDBusTypeHelper::listId()) qAppendListToMessage(&sub, subType, var); else if (id == QDBusTypeHelper::listId()) qAppendListToMessage(&sub, subType, var); else if (id == QDBusTypeHelper::listId()) qAppendListToMessage(&sub, subType, var); else if (id == QDBusTypeHelper::listId()) qAppendListToMessage(&sub, subType, var); #if 0 // never reached, since QVariant::List mached else if (id == QDBusTypeHelper::listId()) qAppendListToMessage(&sub, subType, var); #endif else { qFatal("qAppendArrayToMessage got unknown type!"); ok = false; } break; } } dbus_message_iter_close_container(it, &sub); return ok; } static bool qAppendStructToMessage(DBusMessageIter *it, const QDBusTypeList &typeList, const QVariantList &list) { DBusMessageIter sub; dbus_message_iter_open_container(it, DBUS_TYPE_STRUCT, NULL, &sub); bool ok = qListToIterator(&sub, list, typeList); dbus_message_iter_close_container(it, &sub); return ok; } static bool qAppendVariantToMessage(DBusMessageIter *it, const QDBusType &type, const QVariant &var) { Q_UNUSED(type); // type is 'v' QVariant arg = var; if (var.userType() == QDBusTypeHelper::id()) arg = QDBusTypeHelper::fromVariant(var); // extract the inner variant QDBusType t = QDBusType::guessFromVariant(arg); if (!t.isValid()) return false; // now add this variant DBusMessageIter sub; dbus_message_iter_open_container(it, DBUS_TYPE_VARIANT, t.dbusSignature(), &sub); qVariantToIteratorInternal(&sub, arg, t); dbus_message_iter_close_container(it, &sub); return true; // success } static bool qVariantToIterator(DBusMessageIter *it, QVariant var, QDBusType type) { if (!var.isValid()) { qWarning("QDBusMarshall: invalid QVariant entry found"); return false; } if (!checkType(var, type)) // warning has been printed return false; // type checking failed return qVariantToIteratorInternal(it, var, type); } static bool qVariantToIteratorInternal(DBusMessageIter *it, const QVariant &var, const QDBusType &type) { switch (type.dbusType()) { case DBUS_TYPE_BYTE: qIterAppend( it, type, QDBusTypeHelper::fromVariant(var) ); break; case DBUS_TYPE_BOOLEAN: qIterAppend( it, type, static_cast(var.toBool()) ); break; case DBUS_TYPE_INT16: qIterAppend( it, type, QDBusTypeHelper::fromVariant(var) ); break; case DBUS_TYPE_UINT16: qIterAppend( it, type, QDBusTypeHelper::fromVariant(var) ); break; case DBUS_TYPE_INT32: qIterAppend( it, type, static_cast(var.toInt()) ); break; case DBUS_TYPE_UINT32: qIterAppend( it, type, static_cast(var.toUInt()) ); break; case DBUS_TYPE_INT64: qIterAppend( it, type, static_cast(var.toLongLong()) ); break; case DBUS_TYPE_UINT64: qIterAppend( it, type, static_cast(var.toULongLong()) ); break; case DBUS_TYPE_DOUBLE: qIterAppend( it, type, var.toDouble() ); break; case DBUS_TYPE_STRING: case DBUS_TYPE_OBJECT_PATH: case DBUS_TYPE_SIGNATURE: qIterAppend( it, type, var.toString().toUtf8().constData() ); break; // compound types: case DBUS_TYPE_ARRAY: // could be many things return qAppendArrayToMessage( it, type.arrayElement(), var ); case DBUS_TYPE_VARIANT: return qAppendVariantToMessage( it, type, var ); case DBUS_TYPE_STRUCT: return qAppendStructToMessage( it, type.subTypes(), var.toList() ); case DBUS_TYPE_DICT_ENTRY: qFatal("qVariantToIterator got a DICT_ENTRY!"); return false; default: qWarning("Found unknown DBus type '%s'", type.dbusSignature().constData()); return false; } return true; } bool qListToIterator(DBusMessageIter *it, const QList &list) { for (int i = 0; i < list.count(); ++i) if (!qVariantToIterator(it, list.at(i), QDBusType())) return false; return true; } bool qListToIterator(DBusMessageIter *it, const QList &list, const QDBusTypeList &types) { if (list.count() < types.count()) { qWarning("QDBusMarshall: too few parameters"); return false; } for (int i = 0; i < types.count(); ++i) if (!qVariantToIterator(it, list.at(i), types.at(i))) return false; return true; } void QDBusMarshall::listToMessage(const QList &list, DBusMessage *msg, const QString &signature) { Q_ASSERT(msg); DBusMessageIter it; dbus_message_iter_init_append(msg, &it); if (signature.isEmpty()) (void) qListToIterator(&it, list); else (void) qListToIterator(&it, list, QDBusTypeList(signature.toUtf8())); }