diff -r 000000000000 -r 1918ee327afb src/dbus/qdbusmessage.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/dbus/qdbusmessage.cpp Mon Jan 11 14:00:40 2010 +0000 @@ -0,0 +1,761 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDBus module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdbusmessage.h" + +#include +#include + +#include + +#include "qdbusargument_p.h" +#include "qdbuserror.h" +#include "qdbusmessage_p.h" +#include "qdbusmetatype.h" +#include "qdbusconnection_p.h" +#include "qdbusutil_p.h" + +QT_BEGIN_NAMESPACE + +static inline const char *data(const QByteArray &arr) +{ + return arr.isEmpty() ? 0 : arr.constData(); +} + +QDBusMessagePrivate::QDBusMessagePrivate() + : msg(0), reply(0), type(DBUS_MESSAGE_TYPE_INVALID), + timeout(-1), localReply(0), ref(1), delayedReply(false), localMessage(false), + parametersValidated(false) +{ +} + +QDBusMessagePrivate::~QDBusMessagePrivate() +{ + if (msg) + q_dbus_message_unref(msg); + if (reply) + q_dbus_message_unref(reply); + delete localReply; +} + +/*! + \since 4.3 + Returns the human-readable message associated with the error that was received. +*/ +QString QDBusMessage::errorMessage() const +{ + if (d_ptr->type == ErrorMessage) { + if (!d_ptr->message.isEmpty()) + return d_ptr->message; + if (!d_ptr->arguments.isEmpty()) + return d_ptr->arguments.at(0).toString(); + } + return QString(); +} + +/*! + \internal + Constructs a DBusMessage object from this object. The returned value must be de-referenced + with q_dbus_message_unref. + + The \a error object is set to indicate the error if anything went wrong with the + marshalling. Usually, this error message will be placed in the reply, as if the call failed. + The \a error pointer must not be null. +*/ +DBusMessage *QDBusMessagePrivate::toDBusMessage(const QDBusMessage &message, QDBusError *error) +{ + if (!qdbus_loadLibDBus()) { + *error = QDBusError(QDBusError::Failed, QLatin1String("Could not open lidbus-1 library")); + return 0; + } + + DBusMessage *msg = 0; + const QDBusMessagePrivate *d_ptr = message.d_ptr; + + switch (d_ptr->type) { + case DBUS_MESSAGE_TYPE_INVALID: + //qDebug() << "QDBusMessagePrivate::toDBusMessage" << "message is invalid"; + break; + case DBUS_MESSAGE_TYPE_METHOD_CALL: + // only service and interface can be empty -> path and name must not be empty + if (!d_ptr->parametersValidated) { + if (!QDBusUtil::checkBusName(d_ptr->service, QDBusUtil::EmptyAllowed, error)) + return 0; + if (!QDBusUtil::checkObjectPath(d_ptr->path, QDBusUtil::EmptyNotAllowed, error)) + return 0; + if (!QDBusUtil::checkInterfaceName(d_ptr->interface, QDBusUtil::EmptyAllowed, error)) + return 0; + if (!QDBusUtil::checkMemberName(d_ptr->name, QDBusUtil::EmptyNotAllowed, error, "method")) + return 0; + } + + msg = q_dbus_message_new_method_call(data(d_ptr->service.toUtf8()), d_ptr->path.toUtf8(), + data(d_ptr->interface.toUtf8()), d_ptr->name.toUtf8()); + break; + case DBUS_MESSAGE_TYPE_METHOD_RETURN: + msg = q_dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN); + if (!d_ptr->localMessage) { + q_dbus_message_set_destination(msg, q_dbus_message_get_sender(d_ptr->reply)); + q_dbus_message_set_reply_serial(msg, q_dbus_message_get_serial(d_ptr->reply)); + } + break; + case DBUS_MESSAGE_TYPE_ERROR: + // error name can't be empty + if (!d_ptr->parametersValidated + && !QDBusUtil::checkErrorName(d_ptr->name, QDBusUtil::EmptyNotAllowed, error)) + return 0; + + msg = q_dbus_message_new(DBUS_MESSAGE_TYPE_ERROR); + q_dbus_message_set_error_name(msg, d_ptr->name.toUtf8()); + if (!d_ptr->localMessage) { + q_dbus_message_set_destination(msg, q_dbus_message_get_sender(d_ptr->reply)); + q_dbus_message_set_reply_serial(msg, q_dbus_message_get_serial(d_ptr->reply)); + } + break; + case DBUS_MESSAGE_TYPE_SIGNAL: + // nothing can be empty here + if (!d_ptr->parametersValidated) { + if (!QDBusUtil::checkObjectPath(d_ptr->path, QDBusUtil::EmptyNotAllowed, error)) + return 0; + if (!QDBusUtil::checkInterfaceName(d_ptr->interface, QDBusUtil::EmptyAllowed, error)) + return 0; + if (!QDBusUtil::checkMemberName(d_ptr->name, QDBusUtil::EmptyNotAllowed, error, "method")) + return 0; + } + + msg = q_dbus_message_new_signal(d_ptr->path.toUtf8(), d_ptr->interface.toUtf8(), + d_ptr->name.toUtf8()); + break; + default: + Q_ASSERT(false); + break; + } + + // if we got here, the parameters validated + // and since the message parameters cannot be changed once the message is created + // we can record this fact + d_ptr->parametersValidated = true; + + QDBusMarshaller marshaller; + QVariantList::ConstIterator it = d_ptr->arguments.constBegin(); + QVariantList::ConstIterator cend = d_ptr->arguments.constEnd(); + q_dbus_message_iter_init_append(msg, &marshaller.iterator); + if (!d_ptr->message.isEmpty()) + // prepend the error message + marshaller.append(d_ptr->message); + for ( ; it != cend; ++it) + marshaller.appendVariantInternal(*it); + + // check if everything is ok + if (marshaller.ok) + return msg; + + // not ok; + q_dbus_message_unref(msg); + *error = QDBusError(QDBusError::Failed, QLatin1String("Marshalling failed: ") + marshaller.errorString); + return 0; +} + +/* +struct DBusMessage +{ + DBusAtomic refcount; + DBusHeader header; + DBusString body; + char byte_order; + unsigned int locked : 1; +DBUS_DISABLE_CHECKS + unsigned int in_cache : 1; +#endif + DBusList *size_counters; + long size_counter_delta; + dbus_uint32_t changed_stamp : CHANGED_STAMP_BITS; + DBusDataSlotList slot_list; +#ifndef DBUS_DISABLE_CHECKS + int generation; +#endif +}; +*/ + +/*! + \internal + Constructs a QDBusMessage by parsing the given DBusMessage object. +*/ +QDBusMessage QDBusMessagePrivate::fromDBusMessage(DBusMessage *dmsg) +{ + QDBusMessage message; + if (!dmsg) + return message; + + message.d_ptr->type = q_dbus_message_get_type(dmsg); + message.d_ptr->path = QString::fromUtf8(q_dbus_message_get_path(dmsg)); + message.d_ptr->interface = QString::fromUtf8(q_dbus_message_get_interface(dmsg)); + message.d_ptr->name = message.d_ptr->type == DBUS_MESSAGE_TYPE_ERROR ? + QString::fromUtf8(q_dbus_message_get_error_name(dmsg)) : + QString::fromUtf8(q_dbus_message_get_member(dmsg)); + message.d_ptr->service = QString::fromUtf8(q_dbus_message_get_sender(dmsg)); + message.d_ptr->signature = QString::fromUtf8(q_dbus_message_get_signature(dmsg)); + message.d_ptr->msg = q_dbus_message_ref(dmsg); + + QDBusDemarshaller demarshaller; + demarshaller.message = q_dbus_message_ref(dmsg); + if (q_dbus_message_iter_init(demarshaller.message, &demarshaller.iterator)) + while (!demarshaller.atEnd()) + message << demarshaller.toVariantInternal(); + return message; +} + +bool QDBusMessagePrivate::isLocal(const QDBusMessage &message) +{ + return message.d_ptr->localMessage; +} + +QDBusMessage QDBusMessagePrivate::makeLocal(const QDBusConnectionPrivate &conn, + const QDBusMessage &asSent) +{ + // simulate the message being sent to the bus and then received back + // the only field that the bus sets when delivering the message + // (as opposed to the message as we send it), is the sender + // so we simply set the sender to our unique name + + // determine if we are carrying any complex types + QString computedSignature; + QVariantList::ConstIterator it = asSent.d_ptr->arguments.constBegin(); + QVariantList::ConstIterator end = asSent.d_ptr->arguments.constEnd(); + for ( ; it != end; ++it) { + int id = it->userType(); + const char *signature = QDBusMetaType::typeToSignature(id); + if ((id != QVariant::StringList && id != QVariant::ByteArray && + qstrlen(signature) != 1) || id == qMetaTypeId()) { + // yes, we are + // we must marshall and demarshall again so as to create QDBusArgument + // entries for the complex types + QDBusError error; + DBusMessage *message = toDBusMessage(asSent, &error); + if (!message) { + // failed to marshall, so it's a call error + return QDBusMessage::createError(error); + } + + q_dbus_message_set_sender(message, conn.baseService.toUtf8()); + + QDBusMessage retval = fromDBusMessage(message); + retval.d_ptr->localMessage = true; + q_dbus_message_unref(message); + if (retval.d_ptr->service.isEmpty()) + retval.d_ptr->service = conn.baseService; + return retval; + } else { + computedSignature += QLatin1String(signature); + } + } + + // no complex types seen + // optimise by using the variant list itself + QDBusMessage retval; + QDBusMessagePrivate *d = retval.d_ptr; + d->arguments = asSent.d_ptr->arguments; + d->path = asSent.d_ptr->path; + d->interface = asSent.d_ptr->interface; + d->name = asSent.d_ptr->name; + d->message = asSent.d_ptr->message; + d->type = asSent.d_ptr->type; + + d->service = conn.baseService; + d->signature = computedSignature; + d->localMessage = true; + return retval; +} + +QDBusMessage QDBusMessagePrivate::makeLocalReply(const QDBusConnectionPrivate &conn, + const QDBusMessage &callMsg) +{ + // simulate the reply (return or error) message being sent to the bus and + // then received back. + if (callMsg.d_ptr->localReply) + return makeLocal(conn, *callMsg.d_ptr->localReply); + return QDBusMessage(); // failed +} + +/*! + \class QDBusMessage + \inmodule QtDBus + \since 4.2 + + \brief The QDBusMessage class represents one message sent or + received over the D-Bus bus. + + This object can represent any of the four different types of + messages (MessageType) that can occur on the bus: + + \list + \o Method calls + \o Method return values + \o Signal emissions + \o Error codes + \endlist + + Objects of this type are created with the static createError(), + createMethodCall() and createSignal() functions. Use the + QDBusConnection::send() function to send the messages. +*/ + +/*! + \enum QDBusMessage::MessageType + The possible message types: + + \value MethodCallMessage a message representing an outgoing or incoming method call + \value SignalMessage a message representing an outgoing or incoming signal emission + \value ReplyMessage a message representing the return values of a method call + \value ErrorMessage a message representing an error condition in response to a method call + \value InvalidMessage an invalid message: this is never set on messages received from D-Bus +*/ + +/*! + Constructs a new DBus message with the given \a path, \a interface + and \a name, representing a signal emission. + + A DBus signal is emitted from one application and is received by + all applications that are listening for that signal from that + interface. + + The QDBusMessage object that is returned can be sent using the + QDBusConnection::send() function. +*/ +QDBusMessage QDBusMessage::createSignal(const QString &path, const QString &interface, + const QString &name) +{ + QDBusMessage message; + message.d_ptr->type = DBUS_MESSAGE_TYPE_SIGNAL; + message.d_ptr->path = path; + message.d_ptr->interface = interface; + message.d_ptr->name = name; + + return message; +} + +/*! + Constructs a new DBus message representing a method call. + A method call always informs its destination address + (\a service, \a path, \a interface and \a method). + + The DBus bus allows calling a method on a given remote object without specifying the + destination interface, if the method name is unique. However, if two interfaces on the + remote object export the same method name, the result is undefined (one of the two may be + called or an error may be returned). + + When using DBus in a peer-to-peer context (i.e., not on a bus), the \a service parameter is + optional. + + The QDBusObject and QDBusInterface classes provide a simpler abstraction to synchronous + method calling. + + This function returns a QDBusMessage object that can be sent with + QDBusConnection::call(). +*/ +QDBusMessage QDBusMessage::createMethodCall(const QString &service, const QString &path, + const QString &interface, const QString &method) +{ + QDBusMessage message; + message.d_ptr->type = DBUS_MESSAGE_TYPE_METHOD_CALL; + message.d_ptr->service = service; + message.d_ptr->path = path; + message.d_ptr->interface = interface; + message.d_ptr->name = method; + + return message; +} + +/*! + Constructs a new DBus message representing an error, + with the given \a name and \a msg. +*/ +QDBusMessage QDBusMessage::createError(const QString &name, const QString &msg) +{ + QDBusMessage error; + error.d_ptr->type = DBUS_MESSAGE_TYPE_ERROR; + error.d_ptr->name = name; + error.d_ptr->message = msg; + + return error; +} + +/*! + \fn QDBusMessage QDBusMessage::createError(const QDBusError &error) + + Constructs a new DBus message representing the given \a error. +*/ + +/*! + \fn QDBusMessage QDBusMessage::createError(QDBusError::ErrorType type, const QString &msg) + + Constructs a new DBus message for the error type \a type using + the message \a msg. Returns the DBus message. +*/ + +/*! + \fn QDBusMessage QDBusMessage::createReply(const QList &arguments) const + + Constructs a new DBus message representing a reply, with the given + \a arguments. +*/ +QDBusMessage QDBusMessage::createReply(const QVariantList &arguments) const +{ + QDBusMessage reply; + reply.setArguments(arguments); + reply.d_ptr->type = DBUS_MESSAGE_TYPE_METHOD_RETURN; + if (d_ptr->msg) + reply.d_ptr->reply = q_dbus_message_ref(d_ptr->msg); + if (d_ptr->localMessage) { + reply.d_ptr->localMessage = true; + d_ptr->localReply = new QDBusMessage(reply); // keep an internal copy + } + + // the reply must have a msg or be a local-loop optimisation + Q_ASSERT(reply.d_ptr->reply || reply.d_ptr->localMessage); + return reply; +} + +/*! + Constructs a new DBus message representing an error reply message, + with the given \a name and \a msg. +*/ +QDBusMessage QDBusMessage::createErrorReply(const QString name, const QString &msg) const +{ + QDBusMessage reply = QDBusMessage::createError(name, msg); + if (d_ptr->msg) + reply.d_ptr->reply = q_dbus_message_ref(d_ptr->msg); + if (d_ptr->localMessage) { + reply.d_ptr->localMessage = true; + d_ptr->localReply = new QDBusMessage(reply); // keep an internal copy + } + + // the reply must have a msg or be a local-loop optimisation + Q_ASSERT(reply.d_ptr->reply || reply.d_ptr->localMessage); + return reply; +} + +/*! + \fn QDBusMessage QDBusMessage::createReply(const QVariant &argument) const + + Constructs a new DBus message representing a reply, with the + given \a argument. +*/ + +/*! + \fn QDBusMessage QDBusMessage::createErrorReply(const QDBusError &error) const + + Constructs a new DBus message representing an error reply message, + from the given \a error object. +*/ + +/*! + \fn QDBusMessage QDBusMessage::createErrorReply(QDBusError::ErrorType type, const QString &msg) const + + Constructs a new DBus reply message for the error type \a type using + the message \a msg. Returns the DBus message. +*/ +QDBusMessage QDBusMessage::createErrorReply(QDBusError::ErrorType atype, const QString &amsg) const +{ + QDBusMessage msg = createErrorReply(QDBusError::errorString(atype), amsg); + msg.d_ptr->parametersValidated = true; + return msg; +} + + +/*! + Constructs an empty, invalid QDBusMessage object. + + \sa createError(), createMethodCall(), createSignal() +*/ +QDBusMessage::QDBusMessage() +{ + d_ptr = new QDBusMessagePrivate; +} + +/*! + Constructs a copy of the object given by \a other. + + Note: QDBusMessage objects are shared. Modifications made to the + copy will affect the original one as well. See setDelayedReply() + for more information. +*/ +QDBusMessage::QDBusMessage(const QDBusMessage &other) +{ + d_ptr = other.d_ptr; + d_ptr->ref.ref(); +} + +/*! + Disposes of the object and frees any resources that were being held. +*/ +QDBusMessage::~QDBusMessage() +{ + if (!d_ptr->ref.deref()) + delete d_ptr; +} + +/*! + Copies the contents of the object given by \a other. + + Note: QDBusMessage objects are shared. Modifications made to the + copy will affect the original one as well. See setDelayedReply() + for more information. +*/ +QDBusMessage &QDBusMessage::operator=(const QDBusMessage &other) +{ + qAtomicAssign(d_ptr, other.d_ptr); + return *this; +} + +/*! + Returns the name of the service or the bus address of the remote method call. +*/ +QString QDBusMessage::service() const +{ + return d_ptr->service; +} + +/*! + Returns the path of the object that this message is being sent to (in the case of a + method call) or being received from (for a signal). +*/ +QString QDBusMessage::path() const +{ + return d_ptr->path; +} + +/*! + Returns the interface of the method being called (in the case of a method call) or of + the signal being received from. +*/ +QString QDBusMessage::interface() const +{ + return d_ptr->interface; +} + +/*! + Returns the name of the signal that was emitted or the name of the method that was called. +*/ +QString QDBusMessage::member() const +{ + if (d_ptr->type != ErrorMessage) + return d_ptr->name; + return QString(); +} + +/*! + Returns the name of the error that was received. +*/ +QString QDBusMessage::errorName() const +{ + if (d_ptr->type == ErrorMessage) + return d_ptr->name; + return QString(); +} + +/*! + Returns the signature of the signal that was received or for the output arguments + of a method call. +*/ +QString QDBusMessage::signature() const +{ + return d_ptr->signature; +} + +/*! + Returns the flag that indicates if this message should see a reply + or not. This is only meaningful for \l {MethodCallMessage}{method + call messages}: any other kind of message cannot have replies and + this function will always return false for them. +*/ +bool QDBusMessage::isReplyRequired() const +{ + if (!d_ptr->msg) + return d_ptr->localMessage; // if it's a local message, reply is required + return !q_dbus_message_get_no_reply(d_ptr->msg); +} + +/*! + Sets whether the message will be replied later (if \a enable is + true) or if an automatic reply should be generated by QtDBus + (if \a enable is false). + + In D-Bus, all method calls must generate a reply to the caller, unless the + caller explicitly indicates otherwise (see isReplyRequired()). QtDBus + automatically generates such replies for any slots being called, but it + also allows slots to indicate whether they will take responsibility + of sending the reply at a later time, after the function has finished + processing. + + \sa {Delayed Replies} +*/ +void QDBusMessage::setDelayedReply(bool enable) const +{ + d_ptr->delayedReply = enable; +} + +/*! + Returns the delayed reply flag, as set by setDelayedReply(). By default, this + flag is false, which means QtDBus will generate automatic replies + when necessary. +*/ +bool QDBusMessage::isDelayedReply() const +{ + return d_ptr->delayedReply; +} + +/*! + Sets the arguments that are going to be sent over D-Bus to \a arguments. Those + will be the arguments to a method call or the parameters in the signal. + + \sa arguments() +*/ +void QDBusMessage::setArguments(const QList &arguments) +{ + // FIXME: should we detach? + d_ptr->arguments = arguments; +} + +/*! + Returns the list of arguments that are going to be sent or were received from + D-Bus. +*/ +QList QDBusMessage::arguments() const +{ + return d_ptr->arguments; +} + +/*! + Appends the argument \a arg to the list of arguments to be sent over D-Bus in + a method call or signal emission. +*/ + +QDBusMessage &QDBusMessage::operator<<(const QVariant &arg) +{ + // FIXME: should we detach? + d_ptr->arguments.append(arg); + return *this; +} + +/*! + Returns the message type. +*/ +QDBusMessage::MessageType QDBusMessage::type() const +{ + switch (d_ptr->type) { + case DBUS_MESSAGE_TYPE_METHOD_CALL: + return MethodCallMessage; + case DBUS_MESSAGE_TYPE_METHOD_RETURN: + return ReplyMessage; + case DBUS_MESSAGE_TYPE_ERROR: + return ErrorMessage; + case DBUS_MESSAGE_TYPE_SIGNAL: + return SignalMessage; + default: + break; + } + return InvalidMessage; +} + +/*! + Sends the message without waiting for a reply. This is suitable + for errors, signals, and return values as well as calls whose + return values are not necessary. + + Returns true if the message was queued successfully; + otherwise returns false. + + \sa QDBusConnection::send() +*/ +#ifndef QT_NO_DEBUG_STREAM +static QDebug operator<<(QDebug dbg, QDBusMessage::MessageType t) +{ + switch (t) + { + case QDBusMessage::MethodCallMessage: + return dbg << "MethodCall"; + case QDBusMessage::ReplyMessage: + return dbg << "MethodReturn"; + case QDBusMessage::SignalMessage: + return dbg << "Signal"; + case QDBusMessage::ErrorMessage: + return dbg << "Error"; + default: + return dbg << "Invalid"; + } +} + +static void debugVariantList(QDebug dbg, const QVariantList &list) +{ + bool first = true; + QVariantList::ConstIterator it = list.constBegin(); + QVariantList::ConstIterator end = list.constEnd(); + for ( ; it != end; ++it) { + if (!first) + dbg.nospace() << ", "; + dbg.nospace() << qPrintable(QDBusUtil::argumentToString(*it)); + first = false; + } +} + +QDebug operator<<(QDebug dbg, const QDBusMessage &msg) +{ + dbg.nospace() << "QDBusMessage(type=" << msg.type() + << ", service=" << msg.service(); + if (msg.type() == QDBusMessage::MethodCallMessage || + msg.type() == QDBusMessage::SignalMessage) + dbg.nospace() << ", path=" << msg.path() + << ", interface=" << msg.interface() + << ", member=" << msg.member(); + if (msg.type() == QDBusMessage::ErrorMessage) + dbg.nospace() << ", error name=" << msg.errorName() + << ", error message=" << msg.errorMessage(); + dbg.nospace() << ", signature=" << msg.signature() + << ", contents=("; + debugVariantList(dbg, msg.arguments()); + dbg.nospace() << ") )"; + return dbg.space(); +} +#endif + +QT_END_NAMESPACE +