| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtDBus module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qdbusargument_p.h" |
| #include "qdbusconnection.h" |
| #include "qdbusmetatype_p.h" |
| #include "qdbusutil_p.h" |
| |
| #ifndef QT_NO_DBUS |
| |
| QT_BEGIN_NAMESPACE |
| |
| static void qIterAppend(DBusMessageIter *it, QByteArray *ba, int type, const void *arg) |
| { |
| if (ba) |
| *ba += char(type); |
| else |
| q_dbus_message_iter_append_basic(it, type, arg); |
| } |
| |
| QDBusMarshaller::~QDBusMarshaller() |
| { |
| close(); |
| } |
| |
| inline QString QDBusMarshaller::currentSignature() |
| { |
| if (message) |
| return QString::fromUtf8(q_dbus_message_get_signature(message)); |
| return QString(); |
| } |
| |
| inline void QDBusMarshaller::append(uchar arg) |
| { |
| if (!skipSignature) |
| qIterAppend(&iterator, ba, DBUS_TYPE_BYTE, &arg); |
| } |
| |
| inline void QDBusMarshaller::append(bool arg) |
| { |
| dbus_bool_t cast = arg; |
| if (!skipSignature) |
| qIterAppend(&iterator, ba, DBUS_TYPE_BOOLEAN, &cast); |
| } |
| |
| inline void QDBusMarshaller::append(short arg) |
| { |
| if (!skipSignature) |
| qIterAppend(&iterator, ba, DBUS_TYPE_INT16, &arg); |
| } |
| |
| inline void QDBusMarshaller::append(ushort arg) |
| { |
| if (!skipSignature) |
| qIterAppend(&iterator, ba, DBUS_TYPE_UINT16, &arg); |
| } |
| |
| inline void QDBusMarshaller::append(int arg) |
| { |
| if (!skipSignature) |
| qIterAppend(&iterator, ba, DBUS_TYPE_INT32, &arg); |
| } |
| |
| inline void QDBusMarshaller::append(uint arg) |
| { |
| if (!skipSignature) |
| qIterAppend(&iterator, ba, DBUS_TYPE_UINT32, &arg); |
| } |
| |
| inline void QDBusMarshaller::append(qlonglong arg) |
| { |
| if (!skipSignature) |
| qIterAppend(&iterator, ba, DBUS_TYPE_INT64, &arg); |
| } |
| |
| inline void QDBusMarshaller::append(qulonglong arg) |
| { |
| if (!skipSignature) |
| qIterAppend(&iterator, ba, DBUS_TYPE_UINT64, &arg); |
| } |
| |
| inline void QDBusMarshaller::append(double arg) |
| { |
| if (!skipSignature) |
| qIterAppend(&iterator, ba, DBUS_TYPE_DOUBLE, &arg); |
| } |
| |
| void QDBusMarshaller::append(const QString &arg) |
| { |
| QByteArray data = arg.toUtf8(); |
| const char *cdata = data.constData(); |
| if (!skipSignature) |
| qIterAppend(&iterator, ba, DBUS_TYPE_STRING, &cdata); |
| } |
| |
| inline void QDBusMarshaller::append(const QDBusObjectPath &arg) |
| { |
| QByteArray data = arg.path().toUtf8(); |
| if (!ba && data.isEmpty()) { |
| error(QLatin1String("Invalid object path passed in arguments")); |
| } else { |
| const char *cdata = data.constData(); |
| if (!skipSignature) |
| qIterAppend(&iterator, ba, DBUS_TYPE_OBJECT_PATH, &cdata); |
| } |
| } |
| |
| inline void QDBusMarshaller::append(const QDBusSignature &arg) |
| { |
| QByteArray data = arg.signature().toUtf8(); |
| if (!ba && data.isEmpty()) { |
| error(QLatin1String("Invalid signature passed in arguments")); |
| } else { |
| const char *cdata = data.constData(); |
| if (!skipSignature) |
| qIterAppend(&iterator, ba, DBUS_TYPE_SIGNATURE, &cdata); |
| } |
| } |
| |
| inline void QDBusMarshaller::append(const QDBusUnixFileDescriptor &arg) |
| { |
| int fd = arg.fileDescriptor(); |
| if (!ba && fd == -1) { |
| error(QLatin1String("Invalid file descriptor passed in arguments")); |
| } else { |
| if (!skipSignature) |
| qIterAppend(&iterator, ba, DBUS_TYPE_UNIX_FD, &fd); |
| } |
| } |
| |
| inline void QDBusMarshaller::append(const QByteArray &arg) |
| { |
| if (ba) { |
| if (!skipSignature) |
| *ba += DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING; |
| return; |
| } |
| |
| const char* cdata = arg.constData(); |
| DBusMessageIter subiterator; |
| q_dbus_message_iter_open_container(&iterator, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, |
| &subiterator); |
| q_dbus_message_iter_append_fixed_array(&subiterator, DBUS_TYPE_BYTE, &cdata, arg.length()); |
| q_dbus_message_iter_close_container(&iterator, &subiterator); |
| } |
| |
| inline bool QDBusMarshaller::append(const QDBusVariant &arg) |
| { |
| if (ba) { |
| if (!skipSignature) |
| *ba += DBUS_TYPE_VARIANT_AS_STRING; |
| return true; |
| } |
| |
| const QVariant &value = arg.variant(); |
| int id = value.userType(); |
| if (id == QVariant::Invalid) { |
| qWarning("QDBusMarshaller: cannot add a null QDBusVariant"); |
| error(QLatin1String("Variant containing QVariant::Invalid passed in arguments")); |
| return false; |
| } |
| |
| QByteArray tmpSignature; |
| const char *signature = 0; |
| if (id == QDBusMetaTypeId::argument()) { |
| // take the signature from the QDBusArgument object we're marshalling |
| tmpSignature = |
| qvariant_cast<QDBusArgument>(value).currentSignature().toLatin1(); |
| signature = tmpSignature.constData(); |
| } else { |
| // take the signatuer from the metatype we're marshalling |
| signature = QDBusMetaType::typeToSignature(id); |
| } |
| if (!signature) { |
| qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. " |
| "Use qDBusRegisterMetaType to register it", |
| QMetaType::typeName(id), id); |
| error(QLatin1String("Unregistered type %1 passed in arguments") |
| .arg(QLatin1String(QMetaType::typeName(id)))); |
| return false; |
| } |
| |
| QDBusMarshaller sub(capabilities); |
| open(sub, DBUS_TYPE_VARIANT, signature); |
| bool isOk = sub.appendVariantInternal(value); |
| // don't call sub.close(): it auto-closes |
| |
| return isOk; |
| } |
| |
| inline void QDBusMarshaller::append(const QStringList &arg) |
| { |
| if (ba) { |
| if (!skipSignature) |
| *ba += DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING; |
| return; |
| } |
| |
| QDBusMarshaller sub(capabilities); |
| open(sub, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING); |
| QStringList::ConstIterator it = arg.constBegin(); |
| QStringList::ConstIterator end = arg.constEnd(); |
| for ( ; it != end; ++it) |
| sub.append(*it); |
| // don't call sub.close(): it auto-closes |
| } |
| |
| inline QDBusMarshaller *QDBusMarshaller::beginStructure() |
| { |
| return beginCommon(DBUS_TYPE_STRUCT, 0); |
| } |
| |
| inline QDBusMarshaller *QDBusMarshaller::beginArray(int id) |
| { |
| const char *signature = QDBusMetaType::typeToSignature( QVariant::Type(id) ); |
| if (!signature) { |
| qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. " |
| "Use qDBusRegisterMetaType to register it", |
| QMetaType::typeName(id), id); |
| error(QLatin1String("Unregistered type %1 passed in arguments") |
| .arg(QLatin1String(QMetaType::typeName(id)))); |
| return this; |
| } |
| |
| return beginCommon(DBUS_TYPE_ARRAY, signature); |
| } |
| |
| inline QDBusMarshaller *QDBusMarshaller::beginMap(int kid, int vid) |
| { |
| const char *ksignature = QDBusMetaType::typeToSignature( QVariant::Type(kid) ); |
| if (!ksignature) { |
| qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. " |
| "Use qDBusRegisterMetaType to register it", |
| QMetaType::typeName(kid), kid); |
| error(QLatin1String("Unregistered type %1 passed in arguments") |
| .arg(QLatin1String(QMetaType::typeName(kid)))); |
| return this; |
| } |
| if (ksignature[1] != 0 || !QDBusUtil::isValidBasicType(*ksignature)) { |
| qWarning("QDBusMarshaller: type '%s' (%d) cannot be used as the key type in a D-BUS map.", |
| QMetaType::typeName(kid), kid); |
| error(QLatin1String("Type %1 passed in arguments cannot be used as a key in a map") |
| .arg(QLatin1String(QMetaType::typeName(kid)))); |
| return this; |
| } |
| |
| const char *vsignature = QDBusMetaType::typeToSignature( QVariant::Type(vid) ); |
| if (!vsignature) { |
| const char *typeName = QMetaType::typeName(vid); |
| qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. " |
| "Use qDBusRegisterMetaType to register it", |
| typeName, vid); |
| error(QLatin1String("Unregistered type %1 passed in arguments") |
| .arg(QLatin1String(typeName))); |
| return this; |
| } |
| |
| QByteArray signature; |
| signature = DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING; |
| signature += ksignature; |
| signature += vsignature; |
| signature += DBUS_DICT_ENTRY_END_CHAR_AS_STRING; |
| return beginCommon(DBUS_TYPE_ARRAY, signature); |
| } |
| |
| inline QDBusMarshaller *QDBusMarshaller::beginMapEntry() |
| { |
| return beginCommon(DBUS_TYPE_DICT_ENTRY, 0); |
| } |
| |
| void QDBusMarshaller::open(QDBusMarshaller &sub, int code, const char *signature) |
| { |
| sub.parent = this; |
| sub.ba = ba; |
| sub.ok = true; |
| sub.capabilities = capabilities; |
| sub.skipSignature = skipSignature; |
| |
| if (ba) { |
| if (!skipSignature) { |
| switch (code) { |
| case DBUS_TYPE_ARRAY: |
| *ba += char(code); |
| *ba += signature; |
| Q_FALLTHROUGH(); |
| |
| case DBUS_TYPE_DICT_ENTRY: |
| sub.closeCode = 0; |
| sub.skipSignature = true; |
| break; |
| |
| case DBUS_TYPE_STRUCT: |
| *ba += DBUS_STRUCT_BEGIN_CHAR; |
| sub.closeCode = DBUS_STRUCT_END_CHAR; |
| break; |
| } |
| } |
| } else { |
| q_dbus_message_iter_open_container(&iterator, code, signature, &sub.iterator); |
| } |
| } |
| |
| QDBusMarshaller *QDBusMarshaller::beginCommon(int code, const char *signature) |
| { |
| QDBusMarshaller *d = new QDBusMarshaller(capabilities); |
| open(*d, code, signature); |
| return d; |
| } |
| |
| inline QDBusMarshaller *QDBusMarshaller::endStructure() |
| { return endCommon(); } |
| |
| inline QDBusMarshaller *QDBusMarshaller::endArray() |
| { return endCommon(); } |
| |
| inline QDBusMarshaller *QDBusMarshaller::endMap() |
| { return endCommon(); } |
| |
| inline QDBusMarshaller *QDBusMarshaller::endMapEntry() |
| { return endCommon(); } |
| |
| QDBusMarshaller *QDBusMarshaller::endCommon() |
| { |
| QDBusMarshaller *retval = parent; |
| delete this; |
| return retval; |
| } |
| |
| void QDBusMarshaller::close() |
| { |
| if (ba) { |
| if (!skipSignature && closeCode) |
| *ba += closeCode; |
| } else if (parent) { |
| q_dbus_message_iter_close_container(&parent->iterator, &iterator); |
| } |
| } |
| |
| void QDBusMarshaller::error(const QString &msg) |
| { |
| ok = false; |
| if (parent) |
| parent->error(msg); |
| else |
| errorString = msg; |
| } |
| |
| bool QDBusMarshaller::appendVariantInternal(const QVariant &arg) |
| { |
| int id = arg.userType(); |
| if (id == QVariant::Invalid) { |
| qWarning("QDBusMarshaller: cannot add an invalid QVariant"); |
| error(QLatin1String("Variant containing QVariant::Invalid passed in arguments")); |
| return false; |
| } |
| |
| // intercept QDBusArgument parameters here |
| if (id == QDBusMetaTypeId::argument()) { |
| QDBusArgument dbusargument = qvariant_cast<QDBusArgument>(arg); |
| QDBusArgumentPrivate *d = QDBusArgumentPrivate::d(dbusargument); |
| if (!d->message) |
| return false; // can't append this one... |
| |
| QDBusDemarshaller demarshaller(capabilities); |
| demarshaller.message = q_dbus_message_ref(d->message); |
| |
| if (d->direction == Demarshalling) { |
| // it's demarshalling; just copy |
| demarshaller.iterator = static_cast<QDBusDemarshaller *>(d)->iterator; |
| } else { |
| // it's marshalling; start over |
| if (!q_dbus_message_iter_init(demarshaller.message, &demarshaller.iterator)) |
| return false; // error! |
| } |
| |
| return appendCrossMarshalling(&demarshaller); |
| } |
| |
| const char *signature = QDBusMetaType::typeToSignature( QVariant::Type(id) ); |
| if (!signature) { |
| qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. " |
| "Use qDBusRegisterMetaType to register it", |
| QMetaType::typeName(id), id); |
| error(QLatin1String("Unregistered type %1 passed in arguments") |
| .arg(QLatin1String(QMetaType::typeName(id)))); |
| return false; |
| } |
| |
| switch (*signature) { |
| #ifdef __OPTIMIZE__ |
| case DBUS_TYPE_BYTE: |
| case DBUS_TYPE_INT16: |
| case DBUS_TYPE_UINT16: |
| case DBUS_TYPE_INT32: |
| case DBUS_TYPE_UINT32: |
| case DBUS_TYPE_INT64: |
| case DBUS_TYPE_UINT64: |
| case DBUS_TYPE_DOUBLE: |
| qIterAppend(&iterator, ba, *signature, arg.constData()); |
| return true; |
| case DBUS_TYPE_BOOLEAN: |
| append( arg.toBool() ); |
| return true; |
| #else |
| case DBUS_TYPE_BYTE: |
| append( qvariant_cast<uchar>(arg) ); |
| return true; |
| case DBUS_TYPE_BOOLEAN: |
| append( arg.toBool() ); |
| return true; |
| case DBUS_TYPE_INT16: |
| append( qvariant_cast<short>(arg) ); |
| return true; |
| case DBUS_TYPE_UINT16: |
| append( qvariant_cast<ushort>(arg) ); |
| return true; |
| case DBUS_TYPE_INT32: |
| append( static_cast<dbus_int32_t>(arg.toInt()) ); |
| return true; |
| case DBUS_TYPE_UINT32: |
| append( static_cast<dbus_uint32_t>(arg.toUInt()) ); |
| return true; |
| case DBUS_TYPE_INT64: |
| append( arg.toLongLong() ); |
| return true; |
| case DBUS_TYPE_UINT64: |
| append( arg.toULongLong() ); |
| return true; |
| case DBUS_TYPE_DOUBLE: |
| append( arg.toDouble() ); |
| return true; |
| #endif |
| |
| case DBUS_TYPE_STRING: |
| append( arg.toString() ); |
| return true; |
| case DBUS_TYPE_OBJECT_PATH: |
| append( qvariant_cast<QDBusObjectPath>(arg) ); |
| return true; |
| case DBUS_TYPE_SIGNATURE: |
| append( qvariant_cast<QDBusSignature>(arg) ); |
| return true; |
| |
| // compound types: |
| case DBUS_TYPE_VARIANT: |
| // nested QVariant |
| return append( qvariant_cast<QDBusVariant>(arg) ); |
| |
| case DBUS_TYPE_ARRAY: |
| // could be many things |
| // find out what kind of array it is |
| switch (arg.type()) { |
| case QVariant::StringList: |
| append( arg.toStringList() ); |
| return true; |
| |
| case QVariant::ByteArray: |
| append( arg.toByteArray() ); |
| return true; |
| |
| default: |
| ; |
| } |
| Q_FALLTHROUGH(); |
| |
| case DBUS_TYPE_STRUCT: |
| case DBUS_STRUCT_BEGIN_CHAR: |
| return appendRegisteredType( arg ); |
| |
| case DBUS_TYPE_DICT_ENTRY: |
| case DBUS_DICT_ENTRY_BEGIN_CHAR: |
| qFatal("QDBusMarshaller::appendVariantInternal got a DICT_ENTRY!"); |
| return false; |
| |
| case DBUS_TYPE_UNIX_FD: |
| if (capabilities & QDBusConnection::UnixFileDescriptorPassing || ba) { |
| append(qvariant_cast<QDBusUnixFileDescriptor>(arg)); |
| return true; |
| } |
| Q_FALLTHROUGH(); |
| |
| default: |
| qWarning("QDBusMarshaller::appendVariantInternal: Found unknown D-BUS type '%s'", |
| signature); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool QDBusMarshaller::appendRegisteredType(const QVariant &arg) |
| { |
| ref.ref(); // reference up |
| QDBusArgument self(QDBusArgumentPrivate::create(this)); |
| return QDBusMetaType::marshall(self, arg.userType(), arg.constData()); |
| } |
| |
| bool QDBusMarshaller::appendCrossMarshalling(QDBusDemarshaller *demarshaller) |
| { |
| int code = q_dbus_message_iter_get_arg_type(&demarshaller->iterator); |
| if (QDBusUtil::isValidBasicType(code)) { |
| // easy: just append |
| // do exactly like the D-BUS docs suggest |
| // (see apidocs for q_dbus_message_iter_get_basic) |
| |
| qlonglong value; |
| q_dbus_message_iter_get_basic(&demarshaller->iterator, &value); |
| q_dbus_message_iter_next(&demarshaller->iterator); |
| q_dbus_message_iter_append_basic(&iterator, code, &value); |
| return true; |
| } |
| |
| if (code == DBUS_TYPE_ARRAY) { |
| int element = q_dbus_message_iter_get_element_type(&demarshaller->iterator); |
| if (QDBusUtil::isValidFixedType(element) && element != DBUS_TYPE_UNIX_FD) { |
| // another optimization: fixed size arrays |
| // code is exactly like QDBusDemarshaller::toByteArray |
| DBusMessageIter sub; |
| q_dbus_message_iter_recurse(&demarshaller->iterator, &sub); |
| q_dbus_message_iter_next(&demarshaller->iterator); |
| int len; |
| void* data; |
| q_dbus_message_iter_get_fixed_array(&sub,&data,&len); |
| |
| char signature[2] = { char(element), 0 }; |
| q_dbus_message_iter_open_container(&iterator, DBUS_TYPE_ARRAY, signature, &sub); |
| q_dbus_message_iter_append_fixed_array(&sub, element, &data, len); |
| q_dbus_message_iter_close_container(&iterator, &sub); |
| |
| return true; |
| } |
| } |
| |
| // We have to recurse |
| QDBusDemarshaller *drecursed = demarshaller->beginCommon(); |
| |
| QDBusMarshaller mrecursed(capabilities); // create on the stack makes it autoclose |
| QByteArray subSignature; |
| const char *sig = 0; |
| if (code == DBUS_TYPE_VARIANT || code == DBUS_TYPE_ARRAY) { |
| subSignature = drecursed->currentSignature().toLatin1(); |
| if (!subSignature.isEmpty()) |
| sig = subSignature.constData(); |
| } |
| open(mrecursed, code, sig); |
| |
| while (!drecursed->atEnd()) { |
| if (!mrecursed.appendCrossMarshalling(drecursed)) { |
| delete drecursed; |
| return false; |
| } |
| } |
| |
| delete drecursed; |
| return true; |
| } |
| |
| QT_END_NAMESPACE |
| |
| #endif // QT_NO_DBUS |