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