diff options
author | Alexis Menard <alexis.menard@nokia.com> | 2009-04-17 10:40:52 (GMT) |
---|---|---|
committer | Alexis Menard <alexis.menard@nokia.com> | 2009-04-17 10:40:52 (GMT) |
commit | bb2e4df9bee3148e819c98410aa36e22dad95d7a (patch) | |
tree | a6e6e8c070a72378d4b2e5f39ad3cc9c368b61ab /src/dbus | |
download | Qt-bb2e4df9bee3148e819c98410aa36e22dad95d7a.zip Qt-bb2e4df9bee3148e819c98410aa36e22dad95d7a.tar.gz Qt-bb2e4df9bee3148e819c98410aa36e22dad95d7a.tar.bz2 |
Initial import of kinetic-animations branch from the old kinetic
repository to the new repository
Diffstat (limited to 'src/dbus')
60 files changed, 18152 insertions, 0 deletions
diff --git a/src/dbus/dbus.pro b/src/dbus/dbus.pro new file mode 100644 index 0000000..39adfe1 --- /dev/null +++ b/src/dbus/dbus.pro @@ -0,0 +1,78 @@ +TARGET = QtDBus +QPRO_PWD = $$PWD +QT = core xml +CONFIG += link_pkgconfig +DEFINES += QDBUS_MAKEDLL DBUS_API_SUBJECT_TO_CHANGE +QMAKE_CXXFLAGS += $$QT_CFLAGS_DBUS + +contains(QT_CONFIG, dbus-linked) { + LIBS += $$QT_LIBS_DBUS + DEFINES += QT_LINKED_LIBDBUS +} + +#INCLUDEPATH += . + +unix { + QMAKE_PKGCONFIG_DESCRIPTION = Qt DBus module + QMAKE_PKGCONFIG_REQUIRES = QtCore QtXml +} + +win32 { + LIBS += -lws2_32 -ladvapi32 -lnetapi32 -luser32 + CONFIG(debug, debug|release):LIBS += -ldbus-1d + else:LIBS += -ldbus-1 +} + +include(../qbase.pri) + +PUB_HEADERS = qdbusargument.h \ + qdbusconnectioninterface.h \ + qdbusmacros.h \ + qdbuserror.h \ + qdbusextratypes.h \ + qdbusmessage.h \ + qdbusserver.h \ + qdbusconnection.h \ + qdbusabstractinterface.h \ + qdbusinterface.h \ + qdbusabstractadaptor.h \ + qdbusreply.h \ + qdbusmetatype.h \ + qdbuspendingcall.h \ + qdbuspendingreply.h \ + qdbuscontext.h + +HEADERS += $$PUB_HEADERS \ + qdbusconnection_p.h qdbusmessage_p.h \ + qdbusinterface_p.h qdbusxmlparser_p.h qdbusabstractadaptor_p.h \ + qdbusargument_p.h qdbusutil_p.h qdbusabstractinterface_p.h \ + qdbuscontext_p.h qdbusthreaddebug_p.h qdbusintegrator_p.h \ + qdbuspendingcall_p.h qdbus_symbols_p.h + +SOURCES += qdbusconnection.cpp \ + qdbusconnectioninterface.cpp \ + qdbuserror.cpp \ + qdbusintegrator.cpp \ + qdbusmessage.cpp \ + qdbusserver.cpp \ + qdbusabstractinterface.cpp \ + qdbusinterface.cpp \ + qdbusxmlparser.cpp \ + qdbusutil.cpp \ + qdbusintrospection.cpp \ + qdbusabstractadaptor.cpp \ + qdbusthread.cpp \ + qdbusinternalfilters.cpp \ + qdbusmetaobject.cpp \ + qdbusxmlgenerator.cpp \ + qdbusmisc.cpp \ + qdbusargument.cpp \ + qdbusreply.cpp \ + qdbusmetatype.cpp \ + qdbusextratypes.cpp \ + qdbusmarshaller.cpp \ + qdbuscontext.cpp \ + qdbuspendingcall.cpp \ + qdbuspendingreply.cpp \ + qdbus_symbols.cpp + diff --git a/src/dbus/qdbus_symbols.cpp b/src/dbus/qdbus_symbols.cpp new file mode 100644 index 0000000..549af1f --- /dev/null +++ b/src/dbus/qdbus_symbols.cpp @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qglobal.h> +#include <QtCore/qlibrary.h> +#include <QtCore/qmutex.h> +#include <private/qmutexpool_p.h> + +QT_BEGIN_NAMESPACE + +void *qdbus_resolve_me(const char *name); + +#if !defined QT_LINKED_LIBDBUS + +static QLibrary *qdbus_libdbus = 0; + +void qdbus_unloadLibDBus() +{ + delete qdbus_libdbus; + qdbus_libdbus = 0; +} + +bool qdbus_loadLibDBus() +{ + static volatile bool triedToLoadLibrary = false; +#ifndef QT_NO_THREAD + QMutexLocker locker(QMutexPool::globalInstanceGet((void *)&qdbus_resolve_me)); +#endif + QLibrary *&lib = qdbus_libdbus; + if (triedToLoadLibrary) + return lib && lib->isLoaded(); + + lib = new QLibrary; + triedToLoadLibrary = true; + + static int majorversions[] = { 3, 2, -1 }; + lib->unload(); + lib->setFileName(QLatin1String("dbus-1")); + for (uint i = 0; i < sizeof(majorversions) / sizeof(majorversions[0]); ++i) { + lib->setFileNameAndVersion(lib->fileName(), majorversions[i]); + if (lib->load() && lib->resolve("dbus_connection_open_private")) + return true; + + lib->unload(); + } + + delete lib; + lib = 0; + return false; +} + +void *qdbus_resolve_conditionally(const char *name) +{ + if (qdbus_loadLibDBus()) + return qdbus_libdbus->resolve(name); + return 0; +} + +void *qdbus_resolve_me(const char *name) +{ + void *ptr = 0; + if (!qdbus_loadLibDBus()) + qFatal("Cannot find libdbus-1 in your system to resolve symbol '%s'.", name); + + ptr = qdbus_libdbus->resolve(name); + if (!ptr) + qFatal("Cannot resolve '%s' in your libdbus-1.", name); + + return ptr; +} + +Q_DESTRUCTOR_FUNCTION(qdbus_unloadLibDBus) + +QT_END_NAMESPACE + +#endif diff --git a/src/dbus/qdbus_symbols_p.h b/src/dbus/qdbus_symbols_p.h new file mode 100644 index 0000000..764d368 --- /dev/null +++ b/src/dbus/qdbus_symbols_p.h @@ -0,0 +1,362 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the public API. This header file may +// change from version to version without notice, or even be +// removed. +// +// We mean it. +// +// + +#ifndef QDBUS_SYMBOLS_P_H +#define QDBUS_SYMBOLS_P_H + +#include <QtCore/qglobal.h> +#include <dbus/dbus.h> + +QT_BEGIN_NAMESPACE + +#if !defined QT_LINKED_LIBDBUS + +void *qdbus_resolve_conditionally(const char *name); // doesn't print a warning +void *qdbus_resolve_me(const char *name); // prints a warning +bool qdbus_loadLibDBus(); + +# define DEFINEFUNC(ret, func, args, argcall, funcret) \ + typedef ret (* _q_PTR_##func) args; \ + static inline ret q_##func args \ + { \ + static _q_PTR_##func ptr; \ + if (!ptr) \ + ptr = (_q_PTR_##func) qdbus_resolve_me(#func); \ + funcret ptr argcall; \ + } + +#else // defined QT_LINKED_LIBDBUS + +inline bool qdbus_loadLibDBus() { return true; } + +# define DEFINEFUNC(ret, func, args, argcall, funcret) \ + static inline ret q_##func args { funcret func argcall; } + +#endif // defined QT_LINKED_LIBDBUS + +/* dbus-bus.h */ +DEFINEFUNC(void, dbus_bus_add_match, (DBusConnection *connection, + const char *rule, + DBusError *error), + (connection, rule, error), ) +DEFINEFUNC(void, dbus_bus_remove_match, (DBusConnection *connection, + const char *rule, + DBusError *error), + (connection, rule, error), ) +DEFINEFUNC(dbus_bool_t, dbus_bus_register,(DBusConnection *connection, + DBusError *error), + (connection, error), return) +DEFINEFUNC(DBusConnection *, dbus_bus_get_private, (DBusBusType type, + DBusError *error), + (type, error), return) +DEFINEFUNC(const char*, dbus_bus_get_unique_name, (DBusConnection *connection), + (connection), return) + +/* dbus-connection.h */ +DEFINEFUNC(dbus_bool_t , dbus_connection_add_filter, (DBusConnection *connection, + DBusHandleMessageFunction function, + void *user_data, + DBusFreeFunction free_data_function), + (connection, function, user_data, free_data_function), return) +DEFINEFUNC(void , dbus_connection_close, (DBusConnection *connection), + (connection), return) +DEFINEFUNC(DBusDispatchStatus , dbus_connection_dispatch, (DBusConnection *connection), + (connection), return) +DEFINEFUNC(DBusDispatchStatus , dbus_connection_get_dispatch_status, (DBusConnection *connection), + (connection), return) +DEFINEFUNC(dbus_bool_t , dbus_connection_get_is_connected, (DBusConnection *connection), + (connection), return) +DEFINEFUNC(DBusConnection* , dbus_connection_open_private, (const char *address, + DBusError *error), + (address, error), return) +DEFINEFUNC(DBusConnection* , dbus_connection_ref, (DBusConnection *connection), + (connection), return) +DEFINEFUNC(dbus_bool_t , dbus_connection_send, (DBusConnection *connection, + DBusMessage *message, + dbus_uint32_t *client_serial), + (connection, message, client_serial), return) +DEFINEFUNC(dbus_bool_t , dbus_connection_send_with_reply, (DBusConnection *connection, + DBusMessage *message, + DBusPendingCall **pending_return, + int timeout_milliseconds), + (connection, message, pending_return, timeout_milliseconds), return) +DEFINEFUNC(DBusMessage * , dbus_connection_send_with_reply_and_block, (DBusConnection *connection, + DBusMessage *message, + int timeout_milliseconds, + DBusError *error), + (connection, message, timeout_milliseconds, error), return) +DEFINEFUNC(void , dbus_connection_set_exit_on_disconnect, (DBusConnection *connection, + dbus_bool_t exit_on_disconnect), + (connection, exit_on_disconnect), ) +DEFINEFUNC(dbus_bool_t , dbus_connection_set_timeout_functions, (DBusConnection *connection, + DBusAddTimeoutFunction add_function, + DBusRemoveTimeoutFunction remove_function, + DBusTimeoutToggledFunction toggled_function, + void *data, + DBusFreeFunction free_data_function), + (connection, add_function, remove_function, toggled_function, data, free_data_function), return) +DEFINEFUNC(dbus_bool_t , dbus_connection_set_watch_functions, (DBusConnection *connection, + DBusAddWatchFunction add_function, + DBusRemoveWatchFunction remove_function, + DBusWatchToggledFunction toggled_function, + void *data, + DBusFreeFunction free_data_function), + (connection, add_function, remove_function, toggled_function, data, free_data_function), return) +DEFINEFUNC(void , dbus_connection_set_wakeup_main_function, (DBusConnection *connection, + DBusWakeupMainFunction wakeup_main_function, + void *data, + DBusFreeFunction free_data_function), + (connection, wakeup_main_function, data, free_data_function), ) +DEFINEFUNC(void , dbus_connection_set_dispatch_status_function, (DBusConnection *connection, + DBusDispatchStatusFunction function, + void *data, + DBusFreeFunction free_data_function), + (connection, function, data, free_data_function), ) + +DEFINEFUNC(void , dbus_connection_unref, (DBusConnection *connection), + (connection), ) +DEFINEFUNC(dbus_bool_t , dbus_timeout_get_enabled, (DBusTimeout *timeout), + (timeout), return) +DEFINEFUNC(int , dbus_timeout_get_interval, (DBusTimeout *timeout), + (timeout), return) +DEFINEFUNC(dbus_bool_t , dbus_timeout_handle, (DBusTimeout *timeout), + (timeout), return) + +DEFINEFUNC(dbus_bool_t , dbus_watch_get_enabled, (DBusWatch *watch), + (watch), return) +DEFINEFUNC(int , dbus_watch_get_fd, (DBusWatch *watch), + (watch), return) +DEFINEFUNC(unsigned int , dbus_watch_get_flags, (DBusWatch *watch), + (watch), return) +DEFINEFUNC(dbus_bool_t , dbus_watch_handle, (DBusWatch *watch, + unsigned int flags), + (watch, flags), return) + +/* dbus-errors.h */ +DEFINEFUNC(void , dbus_error_free, (DBusError *error), + (error), ) +DEFINEFUNC(void , dbus_error_init, (DBusError *error), + (error), ) +DEFINEFUNC(dbus_bool_t , dbus_error_is_set, (const DBusError *error), + (error), return) + +/* dbus-memory.h */ +DEFINEFUNC(void , dbus_free, (void *memory), (memory), ) + +/* dbus-message.h */ +DEFINEFUNC(DBusMessage* , dbus_message_copy, (const DBusMessage *message), + (message), return) +DEFINEFUNC(const char* , dbus_message_get_error_name, (DBusMessage *message), + (message), return) +DEFINEFUNC(const char* , dbus_message_get_interface, (DBusMessage *message), + (message), return) +DEFINEFUNC(const char* , dbus_message_get_member, (DBusMessage *message), + (message), return) +DEFINEFUNC(dbus_bool_t , dbus_message_get_no_reply, (DBusMessage *message), + (message), return) +DEFINEFUNC(const char* , dbus_message_get_path, (DBusMessage *message), + (message), return) +DEFINEFUNC(const char* , dbus_message_get_sender, (DBusMessage *message), + (message), return) +DEFINEFUNC(dbus_uint32_t , dbus_message_get_serial, (DBusMessage *message), + (message), return) +DEFINEFUNC(const char* , dbus_message_get_signature, (DBusMessage *message), + (message), return) +DEFINEFUNC(int , dbus_message_get_type, (DBusMessage *message), + (message), return) +DEFINEFUNC(dbus_bool_t , dbus_message_iter_append_basic, (DBusMessageIter *iter, + int type, + const void *value), + (iter, type, value), return) +DEFINEFUNC(dbus_bool_t , dbus_message_iter_append_fixed_array, (DBusMessageIter *iter, + int element_type, + const void *value, + int n_elements), + (iter, element_type, value, n_elements), return) +DEFINEFUNC(dbus_bool_t , dbus_message_iter_close_container, (DBusMessageIter *iter, + DBusMessageIter *sub), + (iter, sub), return) +DEFINEFUNC(int , dbus_message_iter_get_arg_type, (DBusMessageIter *iter), + (iter), return) +DEFINEFUNC(void , dbus_message_iter_get_basic, (DBusMessageIter *iter, + void *value), + (iter, value), ) +DEFINEFUNC(int , dbus_message_iter_get_element_type, (DBusMessageIter *iter), + (iter), return) +DEFINEFUNC(void , dbus_message_iter_get_fixed_array, (DBusMessageIter *iter, + void *value, + int *n_elements), + (iter, value, n_elements), return) +DEFINEFUNC(char* , dbus_message_iter_get_signature, (DBusMessageIter *iter), + (iter), return) +DEFINEFUNC(dbus_bool_t , dbus_message_iter_init, (DBusMessage *message, + DBusMessageIter *iter), + (message, iter), return) +DEFINEFUNC(void , dbus_message_iter_init_append, (DBusMessage *message, + DBusMessageIter *iter), + (message, iter), return) +DEFINEFUNC(dbus_bool_t , dbus_message_iter_next, (DBusMessageIter *iter), + (iter), return) +DEFINEFUNC(dbus_bool_t , dbus_message_iter_open_container, (DBusMessageIter *iter, + int type, + const char *contained_signature, + DBusMessageIter *sub), + (iter, type, contained_signature, sub), return) +DEFINEFUNC(void , dbus_message_iter_recurse, (DBusMessageIter *iter, + DBusMessageIter *sub), + (iter, sub), ) +DEFINEFUNC(DBusMessage* , dbus_message_new, (int message_type), + (message_type), return) +DEFINEFUNC(DBusMessage* , dbus_message_new_method_call, (const char *bus_name, + const char *path, + const char *interface, + const char *method), + (bus_name, path, interface, method), return) +DEFINEFUNC(DBusMessage* , dbus_message_new_signal, (const char *path, + const char *interface, + const char *name), + (path, interface, name), return) +DEFINEFUNC(DBusMessage* , dbus_message_ref, (DBusMessage *message), + (message), return) +DEFINEFUNC(dbus_bool_t , dbus_message_set_destination, (DBusMessage *message, + const char *destination), + (message, destination), return) +DEFINEFUNC(dbus_bool_t , dbus_message_set_error_name, (DBusMessage *message, + const char *name), + (message, name), return) +DEFINEFUNC(void , dbus_message_set_no_reply, (DBusMessage *message, + dbus_bool_t no_reply), + (message, no_reply), return) +DEFINEFUNC(dbus_bool_t , dbus_message_set_path, (DBusMessage *message, + const char *object_path), + (message, object_path), return) +DEFINEFUNC(dbus_bool_t , dbus_message_set_reply_serial, (DBusMessage *message, + dbus_uint32_t reply_serial), + (message, reply_serial), return) +DEFINEFUNC(dbus_bool_t , dbus_message_set_sender, (DBusMessage *message, + const char *sender), + (message, sender), return) +DEFINEFUNC(void , dbus_message_unref, (DBusMessage *message), + (message), ) + +/* dbus-pending-call.h */ +DEFINEFUNC(dbus_bool_t , dbus_pending_call_set_notify, (DBusPendingCall *pending, + DBusPendingCallNotifyFunction function, + void *user_data, + DBusFreeFunction free_user_data), + (pending, function, user_data, free_user_data), return) +DEFINEFUNC(void , dbus_pending_call_block, (DBusPendingCall *pending), + (pending), ) +DEFINEFUNC(void , dbus_pending_call_cancel, (DBusPendingCall *pending), + (pending), ) +DEFINEFUNC(dbus_bool_t , dbus_pending_call_get_completed, (DBusPendingCall *pending), + (pending), return) +DEFINEFUNC(DBusMessage* , dbus_pending_call_steal_reply, (DBusPendingCall *pending), + (pending), return) +DEFINEFUNC(void , dbus_pending_call_unref, (DBusPendingCall *pending), + (pending), return) + +/* dbus-server.h */ +DEFINEFUNC(dbus_bool_t , dbus_server_allocate_data_slot, (dbus_int32_t *slot_p), + (slot_p), return) +DEFINEFUNC(void , dbus_server_disconnect, (DBusServer *server), + (server), ) +DEFINEFUNC(char* , dbus_server_get_address, (DBusServer *server), + (server), return) +DEFINEFUNC(dbus_bool_t , dbus_server_get_is_connected, (DBusServer *server), + (server), return) +DEFINEFUNC(DBusServer* , dbus_server_listen, (const char *address, + DBusError *error), + (address, error), return) +DEFINEFUNC(dbus_bool_t , dbus_server_set_data, (DBusServer *server, + int slot, + void *data, + DBusFreeFunction free_data_func), + (server, slot, data, free_data_func), return) +DEFINEFUNC(void , dbus_server_set_new_connection_function, (DBusServer *server, + DBusNewConnectionFunction function, + void *data, + DBusFreeFunction free_data_function), + (server, function, data, free_data_function), ) +DEFINEFUNC(dbus_bool_t , dbus_server_set_timeout_functions, (DBusServer *server, + DBusAddTimeoutFunction add_function, + DBusRemoveTimeoutFunction remove_function, + DBusTimeoutToggledFunction toggled_function, + void *data, + DBusFreeFunction free_data_function), + (server, add_function, remove_function, toggled_function, data, free_data_function), return) +DEFINEFUNC(dbus_bool_t , dbus_server_set_watch_functions, (DBusServer *server, + DBusAddWatchFunction add_function, + DBusRemoveWatchFunction remove_function, + DBusWatchToggledFunction toggled_function, + void *data, + DBusFreeFunction free_data_function), + (server, add_function, remove_function, toggled_function, data, free_data_function), return) +DEFINEFUNC(void , dbus_server_unref, (DBusServer *server), + (server), ) + +/* dbus-signature.h */ +DEFINEFUNC(dbus_bool_t , dbus_signature_validate, (const char *signature, + DBusError *error), + (signature, error), return) +DEFINEFUNC(dbus_bool_t , dbus_signature_validate_single, (const char *signature, + DBusError *error), + (signature, error), return) +DEFINEFUNC(dbus_bool_t , dbus_type_is_basic, (int typecode), + (typecode), return); +DEFINEFUNC(dbus_bool_t , dbus_type_is_fixed, (int typecode), + (typecode), return) + +QT_END_NAMESPACE + +#endif diff --git a/src/dbus/qdbusabstractadaptor.cpp b/src/dbus/qdbusabstractadaptor.cpp new file mode 100644 index 0000000..883aabf --- /dev/null +++ b/src/dbus/qdbusabstractadaptor.cpp @@ -0,0 +1,380 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdbusabstractadaptor.h" + +#include <QtCore/qcoreapplication.h> +#include <QtCore/qmetaobject.h> +#include <QtCore/qset.h> +#include <QtCore/qtimer.h> +#include <QtCore/qthread.h> + +#include "qdbusconnection.h" + +#include "qdbusconnection_p.h" // for qDBusParametersForMethod +#include "qdbusabstractadaptor_p.h" +#include "qdbusmetatype_p.h" + +QT_BEGIN_NAMESPACE + +QDBusAdaptorConnector *qDBusFindAdaptorConnector(QObject *obj) +{ + if (!obj) + return 0; + const QObjectList &children = obj->children(); + QObjectList::ConstIterator it = children.constBegin(); + QObjectList::ConstIterator end = children.constEnd(); + for ( ; it != end; ++it) { + QDBusAdaptorConnector *connector = qobject_cast<QDBusAdaptorConnector *>(*it); + if (connector) { + connector->polish(); + return connector; + } + } + return 0; +} + +QDBusAdaptorConnector *qDBusFindAdaptorConnector(QDBusAbstractAdaptor *adaptor) +{ + return qDBusFindAdaptorConnector(adaptor->parent()); +} + +QDBusAdaptorConnector *qDBusCreateAdaptorConnector(QObject *obj) +{ + QDBusAdaptorConnector *connector = qDBusFindAdaptorConnector(obj); + if (connector) + return connector; + return new QDBusAdaptorConnector(obj); +} + +QString QDBusAbstractAdaptorPrivate::retrieveIntrospectionXml(QDBusAbstractAdaptor *adaptor) +{ + return adaptor->d_func()->xml; +} + +void QDBusAbstractAdaptorPrivate::saveIntrospectionXml(QDBusAbstractAdaptor *adaptor, + const QString &xml) +{ + adaptor->d_func()->xml = xml; +} + +/*! + \class QDBusAbstractAdaptor + \inmodule QtDBus + \since 4.2 + + \brief The QDBusAbstractAdaptor class is the base class of D-Bus adaptor classes. + + The QDBusAbstractAdaptor class is the starting point for all objects intending to provide + interfaces to the external world using D-Bus. This is accomplished by attaching a one or more + classes derived from QDBusAbstractAdaptor to a normal QObject and then registering that QObject + with QDBusConnection::registerObject. QDBusAbstractAdaptor objects are intended to be + light-weight wrappers, mostly just relaying calls into the real object (its parent) and the + signals from it. + + Each QDBusAbstractAdaptor-derived class should define the D-Bus interface it is implementing + using the Q_CLASSINFO macro in the class definition. Note that only one interface can be + exposed in this way. + + QDBusAbstractAdaptor uses the standard QObject mechanism of signals, slots and properties to + determine what signals, methods and properties to export to the bus. Any signal emitted by + QDBusAbstractAdaptor-derived classes will be automatically be relayed through any D-Bus + connections the object is registered on. + + Classes derived from QDBusAbstractAdaptor must be created on the heap using the \a new operator + and must not be deleted by the user (they will be deleted automatically when the object they are + connected to is also deleted). + + \sa {usingadaptors.html}{Using adaptors}, QDBusConnection +*/ + +/*! + Constructs a QDBusAbstractAdaptor with \a obj as the parent object. +*/ +QDBusAbstractAdaptor::QDBusAbstractAdaptor(QObject* obj) + : QObject(*new QDBusAbstractAdaptorPrivate, obj) +{ + QDBusAdaptorConnector *connector = qDBusCreateAdaptorConnector(obj); + + connector->waitingForPolish = true; + QMetaObject::invokeMethod(connector, "polish", Qt::QueuedConnection); +} + +/*! + Destroys the adaptor. + + \warning Adaptors are destroyed automatically when the real object they refer to is + destroyed. Do not delete the adaptors yourself. +*/ +QDBusAbstractAdaptor::~QDBusAbstractAdaptor() +{ +} + +/*! + Toggles automatic signal relaying from the real object (see object()). + + Automatic signal relaying consists of signal-to-signal connection of the signals on the parent + that have the exact same method signatue in both classes. + + If \a enable is set to true, connect the signals; if set to false, disconnect all signals. +*/ +void QDBusAbstractAdaptor::setAutoRelaySignals(bool enable) +{ + const QMetaObject *us = metaObject(); + const QMetaObject *them = parent()->metaObject(); + bool connected = false; + for (int idx = staticMetaObject.methodCount(); idx < us->methodCount(); ++idx) { + QMetaMethod mm = us->method(idx); + + if (mm.methodType() != QMetaMethod::Signal) + continue; + + // try to connect/disconnect to a signal on the parent that has the same method signature + QByteArray sig = QMetaObject::normalizedSignature(mm.signature()); + if (them->indexOfSignal(sig) == -1) + continue; + sig.prepend(QSIGNAL_CODE + '0'); + parent()->disconnect(sig, this, sig); + if (enable) + connected = connect(parent(), sig, sig) || connected; + } + d_func()->autoRelaySignals = connected; +} + +/*! + Returns true if automatic signal relaying from the real object (see object()) is enabled, + otherwiser returns false. + + \sa setAutoRelaySignals() +*/ +bool QDBusAbstractAdaptor::autoRelaySignals() const +{ + return d_func()->autoRelaySignals; +} + +QDBusAdaptorConnector::QDBusAdaptorConnector(QObject *obj) + : QObject(obj), waitingForPolish(false) +{ +} + +QDBusAdaptorConnector::~QDBusAdaptorConnector() +{ +} + +void QDBusAdaptorConnector::addAdaptor(QDBusAbstractAdaptor *adaptor) +{ + // find the interface name + const QMetaObject *mo = adaptor->metaObject(); + int ciid = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTERFACE); + if (ciid != -1) { + QMetaClassInfo mci = mo->classInfo(ciid); + if (*mci.value()) { + // find out if this interface exists first + const char *interface = mci.value(); + AdaptorMap::Iterator it = qLowerBound(adaptors.begin(), adaptors.end(), + QByteArray(interface)); + if (it != adaptors.end() && qstrcmp(interface, it->interface) == 0) { + // exists. Replace it (though it's probably the same) + if (it->adaptor != adaptor) { + // reconnect the signals + disconnectAllSignals(it->adaptor); + connectAllSignals(adaptor); + } + it->adaptor = adaptor; + } else { + // create a new one + AdaptorData entry; + entry.interface = interface; + entry.adaptor = adaptor; + adaptors << entry; + + // connect the adaptor's signals to our relaySlot slot + connectAllSignals(adaptor); + } + } + } +} + +void QDBusAdaptorConnector::disconnectAllSignals(QObject *obj) +{ + QMetaObject::disconnect(obj, -1, this, metaObject()->methodOffset()); +} + +void QDBusAdaptorConnector::connectAllSignals(QObject *obj) +{ + QMetaObject::connect(obj, -1, this, metaObject()->methodOffset(), Qt::DirectConnection); +} + +void QDBusAdaptorConnector::polish() +{ + if (!waitingForPolish) + return; // avoid working multiple times if multiple adaptors were added + + waitingForPolish = false; + const QObjectList &objs = parent()->children(); + QObjectList::ConstIterator it = objs.constBegin(); + QObjectList::ConstIterator end = objs.constEnd(); + for ( ; it != end; ++it) { + QDBusAbstractAdaptor *adaptor = qobject_cast<QDBusAbstractAdaptor *>(*it); + if (adaptor) + addAdaptor(adaptor); + } + + // sort the adaptor list + qSort(adaptors); +} + +void QDBusAdaptorConnector::relaySlot(void **argv) +{ + QObjectPrivate *d = static_cast<QObjectPrivate *>(d_ptr); + relay(d->currentSender->sender, d->currentSender->signal, argv); +} + +void QDBusAdaptorConnector::relay(QObject *senderObj, int lastSignalIdx, void **argv) +{ + if (lastSignalIdx < QObject::staticMetaObject.methodCount()) + // QObject signal (destroyed(QObject *)) -- ignore + return; + + const QMetaObject *senderMetaObject = senderObj->metaObject(); + QMetaMethod mm = senderMetaObject->method(lastSignalIdx); + + QObject *realObject = senderObj; + if (qobject_cast<QDBusAbstractAdaptor *>(senderObj)) + // it's an adaptor, so the real object is in fact its parent + realObject = realObject->parent(); + + // break down the parameter list + QList<int> types; + int inputCount = qDBusParametersForMethod(mm, types); + if (inputCount == -1) + // invalid signal signature + // qDBusParametersForMethod has already complained + return; + if (inputCount + 1 != types.count() || + types.at(inputCount) == QDBusMetaTypeId::message) { + // invalid signal signature + // qDBusParametersForMethod has not yet complained about this one + qWarning("QDBusAbstractAdaptor: Cannot relay signal %s::%s", + senderMetaObject->className(), mm.signature()); + return; + } + + QVariantList args; + for (int i = 1; i < types.count(); ++i) + args << QVariant(types.at(i), argv[i]); + + // now emit the signal with all the information + emit relaySignal(realObject, senderMetaObject, lastSignalIdx, args); +} + +// our Meta Object +// modify carefully: this has been hand-edited! +// the relaySlot slot has local ID 0 (we use this when calling QMetaObject::connect) +// it also gets called with the void** array + +static const uint qt_meta_data_QDBusAdaptorConnector[] = { + // content: + 1, // revision + 0, // classname + 0, 0, // classinfo + 3, 10, // methods + 0, 0, // properties + 0, 0, // enums/sets + + // slots: signature, parameters, type, tag, flags + 106, 22, 22, 22, 0x0a, + 118, 22, 22, 22, 0x0a, + + // signals: signature, parameters, type, tag, flags + 47, 23, 22, 22, 0x05, + + 0 // eod +}; + +static const char qt_meta_stringdata_QDBusAdaptorConnector[] = { + "QDBusAdaptorConnector\0\0obj,metaobject,sid,args\0" + "relaySignal(QObject*,const QMetaObject*,int,QVariantList)\0\0relaySlot()\0" + "polish()\0" +}; + +const QMetaObject QDBusAdaptorConnector::staticMetaObject = { + { &QObject::staticMetaObject, qt_meta_stringdata_QDBusAdaptorConnector, + qt_meta_data_QDBusAdaptorConnector, 0 } +}; + +const QMetaObject *QDBusAdaptorConnector::metaObject() const +{ + return &staticMetaObject; +} + +void *QDBusAdaptorConnector::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_QDBusAdaptorConnector)) + return static_cast<void*>(const_cast<QDBusAdaptorConnector*>(this)); + return QObject::qt_metacast(_clname); +} + +int QDBusAdaptorConnector::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QObject::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: relaySlot(_a); break; // HAND EDIT: add the _a parameter + case 1: polish(); break; + case 2: relaySignal((*reinterpret_cast< QObject*(*)>(_a[1])),(*reinterpret_cast< const QMetaObject*(*)>(_a[2])),(*reinterpret_cast< int(*)>(_a[3])),(*reinterpret_cast< const QVariantList(*)>(_a[4]))); break; + } + _id -= 3; + } + return _id; +} + +// SIGNAL 0 +void QDBusAdaptorConnector::relaySignal(QObject * _t1, const QMetaObject * _t2, int _t3, const QVariantList & _t4) +{ + void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)), const_cast<void*>(reinterpret_cast<const void*>(&_t2)), const_cast<void*>(reinterpret_cast<const void*>(&_t3)), const_cast<void*>(reinterpret_cast<const void*>(&_t4)) }; + QMetaObject::activate(this, &staticMetaObject, 2, _a); +} + +QT_END_NAMESPACE diff --git a/src/dbus/qdbusabstractadaptor.h b/src/dbus/qdbusabstractadaptor.h new file mode 100644 index 0000000..d829920 --- /dev/null +++ b/src/dbus/qdbusabstractadaptor.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDBUSABSTRACTADAPTOR_H +#define QDBUSABSTRACTADAPTOR_H + +#include <QtCore/qobject.h> +#include <QtDBus/qdbusmacros.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(DBus) + +class QDBusAbstractAdaptorPrivate; +class QDBUS_EXPORT QDBusAbstractAdaptor: public QObject +{ + Q_OBJECT +protected: + QDBusAbstractAdaptor(QObject *parent); + +public: + ~QDBusAbstractAdaptor(); + +protected: + void setAutoRelaySignals(bool enable); + bool autoRelaySignals() const; + +private: + Q_DECLARE_PRIVATE(QDBusAbstractAdaptor) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/dbus/qdbusabstractadaptor_p.h b/src/dbus/qdbusabstractadaptor_p.h new file mode 100644 index 0000000..9a1543c --- /dev/null +++ b/src/dbus/qdbusabstractadaptor_p.h @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the public API. This header file may +// change from version to version without notice, or even be +// removed. +// +// We mean it. +// +// + +#ifndef QDBUSABSTRACTADAPTORPRIVATE_H +#define QDBUSABSTRACTADAPTORPRIVATE_H + +#include <qdbusabstractadaptor.h> + +#include <QtCore/qobject.h> +#include <QtCore/qmap.h> +#include <QtCore/qhash.h> +#include <QtCore/qreadwritelock.h> +#include <QtCore/qvariant.h> +#include <QtCore/qvector.h> +#include "private/qobject_p.h" + +#define QCLASSINFO_DBUS_INTERFACE "D-Bus Interface" +#define QCLASSINFO_DBUS_INTROSPECTION "D-Bus Introspection" + +QT_BEGIN_NAMESPACE + +class QDBusAbstractAdaptor; +class QDBusAdaptorConnector; +class QDBusAdaptorManager; +class QDBusConnectionPrivate; + +class QDBusAbstractAdaptorPrivate: public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDBusAbstractAdaptor) +public: + QDBusAbstractAdaptorPrivate() : autoRelaySignals(false) {} + QString xml; + bool autoRelaySignals; + + static QString retrieveIntrospectionXml(QDBusAbstractAdaptor *adaptor); + static void saveIntrospectionXml(QDBusAbstractAdaptor *adaptor, const QString &xml); +}; + +class QDBusAdaptorConnector: public QObject +{ + Q_OBJECT_FAKE + +public: // typedefs + struct AdaptorData + { + const char *interface; + QDBusAbstractAdaptor *adaptor; + + inline bool operator<(const AdaptorData &other) const + { return QByteArray(interface) < other.interface; } + inline bool operator<(const QString &other) const + { return QLatin1String(interface) < other; } + inline bool operator<(const QByteArray &other) const + { return interface < other; } + }; + typedef QVector<AdaptorData> AdaptorMap; + +public: // methods + explicit QDBusAdaptorConnector(QObject *parent); + ~QDBusAdaptorConnector(); + + void addAdaptor(QDBusAbstractAdaptor *adaptor); + void connectAllSignals(QObject *object); + void disconnectAllSignals(QObject *object); + void relay(QObject *sender, int id, void **); + +//public slots: + void relaySlot(void **); + void polish(); + +protected: +//signals: + void relaySignal(QObject *obj, const QMetaObject *metaObject, int sid, const QVariantList &args); + +public: // member variables + AdaptorMap adaptors; + bool waitingForPolish : 1; +}; + +extern QDBusAdaptorConnector *qDBusFindAdaptorConnector(QObject *object); +extern QDBusAdaptorConnector *qDBusCreateAdaptorConnector(QObject *object); + +QT_END_NAMESPACE + +#endif // QDBUSABSTRACTADAPTORPRIVATE_H diff --git a/src/dbus/qdbusabstractinterface.cpp b/src/dbus/qdbusabstractinterface.cpp new file mode 100644 index 0000000..c238653 --- /dev/null +++ b/src/dbus/qdbusabstractinterface.cpp @@ -0,0 +1,729 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdbusabstractinterface.h" +#include "qdbusabstractinterface_p.h" + +#include "qdbusargument.h" +#include "qdbuspendingcall.h" +#include "qdbusmetaobject_p.h" +#include "qdbusmetatype_p.h" +#include "qdbusutil_p.h" + +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +QDBusAbstractInterfacePrivate::QDBusAbstractInterfacePrivate(const QString &serv, + const QString &p, + const QString &iface, + const QDBusConnection& con, + bool isDynamic) + : connection(con), service(serv), path(p), interface(iface), isValid(true) +{ + if (isDynamic) { + // QDBusInterface: service and object path can't be empty, but interface can +#if 0 + Q_ASSERT_X(QDBusUtil::isValidBusName(service), + "QDBusInterface::QDBusInterface", "Invalid service name"); + Q_ASSERT_X(QDBusUtil::isValidObjectPath(path), + "QDBusInterface::QDBusInterface", "Invalid object path given"); + Q_ASSERT_X(interface.isEmpty() || QDBusUtil::isValidInterfaceName(interface), + "QDBusInterface::QDBusInterface", "Invalid interface name"); +#else + if (!QDBusUtil::isValidBusName(service)) { + lastError = QDBusError(QDBusError::Disconnected, + QLatin1String("Invalid service name")); + isValid = false; + } else if (!QDBusUtil::isValidObjectPath(path)) { + lastError = QDBusError(QDBusError::Disconnected, + QLatin1String("Invalid object name given")); + isValid = false; + } else if (!interface.isEmpty() && !QDBusUtil::isValidInterfaceName(interface)) { + lastError = QDBusError(QDBusError::Disconnected, + QLatin1String("Invalid interface name")); + isValid = false; + } +#endif + } else { + // all others: service and path can be empty here, but interface can't +#if 0 + Q_ASSERT_X(service.isEmpty() || QDBusUtil::isValidBusName(service), + "QDBusAbstractInterface::QDBusAbstractInterface", "Invalid service name"); + Q_ASSERT_X(path.isEmpty() || QDBusUtil::isValidObjectPath(path), + "QDBusAbstractInterface::QDBusAbstractInterface", "Invalid object path given"); + Q_ASSERT_X(QDBusUtil::isValidInterfaceName(interface), + "QDBusAbstractInterface::QDBusAbstractInterface", "Invalid interface class!"); +#else + if (!service.isEmpty() && !QDBusUtil::isValidBusName(service)) { + lastError = QDBusError(QDBusError::Disconnected, + QLatin1String("Invalid service name")); + isValid = false; + } else if (!path.isEmpty() && !QDBusUtil::isValidObjectPath(path)) { + lastError = QDBusError(QDBusError::Disconnected, + QLatin1String("Invalid object path given")); + isValid = false; + } else if (!QDBusUtil::isValidInterfaceName(interface)) { + lastError = QDBusError(QDBusError::Disconnected, + QLatin1String("Invalid interface class")); + isValid = false; + } +#endif + } + + if (!isValid) + return; + + if (!connection.isConnected()) { + lastError = QDBusError(QDBusError::Disconnected, + QLatin1String("Not connected to D-Bus server")); + isValid = false; + } else if (!service.isEmpty()) { + currentOwner = connectionPrivate()->getNameOwner(service); // verify the name owner + if (currentOwner.isEmpty()) { + isValid = false; + lastError = connectionPrivate()->lastError; + } + } +} + +QVariant QDBusAbstractInterfacePrivate::property(const QMetaProperty &mp) const +{ + if (!connection.isConnected()) // not connected + return QVariant(); + + // is this metatype registered? + int mid; + const char *expectedSignature; + if (mp.type() == QVariant::LastType) { + // We're asking to read a QVariant + mid = qMetaTypeId<QDBusVariant>(); + expectedSignature = "v"; + } else { + mid = QMetaType::type(mp.typeName()); + expectedSignature = QDBusMetaType::typeToSignature(mid); + if (expectedSignature == 0) { + qWarning("QDBusAbstractInterface: type %s must be registered with QtDBus before it can be " + "used to read property %s.%s", + mp.typeName(), qPrintable(interface), mp.name()); + return QVariant(); + } + } + + // try to read this property + QDBusMessage msg = QDBusMessage::createMethodCall(service, path, + QLatin1String(DBUS_INTERFACE_PROPERTIES), + QLatin1String("Get")); + msg << interface << QString::fromUtf8(mp.name()); + QDBusMessage reply = connection.call(msg, QDBus::Block); + + if (reply.type() != QDBusMessage::ReplyMessage) { + lastError = reply; + return QVariant(); + } + if (reply.signature() != QLatin1String("v")) { + QString errmsg = QLatin1String("Invalid signature `%1' in return from call to " + DBUS_INTERFACE_PROPERTIES); + lastError = QDBusError(QDBusError::InvalidSignature, errmsg.arg(reply.signature())); + return QVariant(); + } + + QByteArray foundSignature; + const char *foundType = 0; + QVariant value = qvariant_cast<QDBusVariant>(reply.arguments().at(0)).variant(); + + if (value.userType() == mid) + return value; // simple match + + if (value.userType() == qMetaTypeId<QDBusArgument>()) { + QDBusArgument arg = qvariant_cast<QDBusArgument>(value); + + foundType = "user type"; + foundSignature = arg.currentSignature().toLatin1(); + if (foundSignature == expectedSignature) { + void *null = 0; + QVariant result(mid, null); + QDBusMetaType::demarshall(arg, mid, result.data()); + + if (mp.type() == QVariant::LastType) + // special case: QVariant + return qvariant_cast<QDBusVariant>(result).variant(); + return result; + } + } else { + foundType = value.typeName(); + foundSignature = QDBusMetaType::typeToSignature(value.userType()); + } + + // there was an error... + QString errmsg = QLatin1String("Unexpected `%1' (%2) when retrieving property `%3.%4' " + "(expected type `%5' (%6))"); + lastError = QDBusError(QDBusError::InvalidSignature, + errmsg.arg(QString::fromLatin1(foundType), + QString::fromLatin1(foundSignature), + interface, + QString::fromUtf8(mp.name()), + QString::fromLatin1(mp.typeName()), + QString::fromLatin1(expectedSignature))); + return QVariant(); +} + +void QDBusAbstractInterfacePrivate::setProperty(const QMetaProperty &mp, const QVariant &value) +{ + if (!connection.isConnected()) // not connected + return; + + // send the value + QDBusMessage msg = QDBusMessage::createMethodCall(service, path, + QLatin1String(DBUS_INTERFACE_PROPERTIES), + QLatin1String("Set")); + msg << interface << QString::fromUtf8(mp.name()) << qVariantFromValue(QDBusVariant(value)); + QDBusMessage reply = connection.call(msg, QDBus::Block); + + if (reply.type() != QDBusMessage::ReplyMessage) + lastError = reply; +} + +void QDBusAbstractInterfacePrivate::_q_serviceOwnerChanged(const QString &name, + const QString &oldOwner, + const QString &newOwner) +{ + Q_UNUSED(oldOwner); + //qDebug() << "QDBusAbstractInterfacePrivate serviceOwnerChanged" << name << oldOwner << newOwner; + if (name == service) { + currentOwner = newOwner; + isValid = !newOwner.isEmpty(); + } +} + + +/*! + \class QDBusAbstractInterface + \inmodule QtDBus + \since 4.2 + + \brief The QDBusAbstractInterface class is the base class for all D-Bus interfaces in the QtDBus binding, allowing access to remote interfaces + + Generated-code classes also derive from QDBusAbstractInterface, + all methods described here are also valid for generated-code + classes. In addition to those described here, generated-code + classes provide member functions for the remote methods, which + allow for compile-time checking of the correct parameters and + return values, as well as property type-matching and signal + parameter-matching. + + \sa {qdbusxml2cpp.html}{The QDBus compiler}, QDBusInterface +*/ + +/*! + \internal + This is the constructor called from QDBusInterface::QDBusInterface. +*/ +QDBusAbstractInterface::QDBusAbstractInterface(QDBusAbstractInterfacePrivate &d, QObject *parent) + : QObject(d, parent) +{ + // keep track of the service owner + if (d_func()->isValid) + QObject::connect(d_func()->connectionPrivate(), SIGNAL(serviceOwnerChanged(QString,QString,QString)), + this, SLOT(_q_serviceOwnerChanged(QString,QString,QString))); +} + +/*! + \internal + This is the constructor called from static classes derived from + QDBusAbstractInterface (i.e., those generated by dbusxml2cpp). +*/ +QDBusAbstractInterface::QDBusAbstractInterface(const QString &service, const QString &path, + const char *interface, const QDBusConnection &con, + QObject *parent) + : QObject(*new QDBusAbstractInterfacePrivate(service, path, QString::fromLatin1(interface), + con, false), parent) +{ + // keep track of the service owner + if (d_func()->connection.isConnected()) + QObject::connect(d_func()->connectionPrivate(), SIGNAL(serviceOwnerChanged(QString,QString,QString)), + this, SLOT(_q_serviceOwnerChanged(QString,QString,QString))); +} + +/*! + Releases this object's resources. +*/ +QDBusAbstractInterface::~QDBusAbstractInterface() +{ +} + +/*! + Returns true if this is a valid reference to a remote object. It returns false if + there was an error during the creation of this interface (for instance, if the remote + application does not exist). + + Note: when dealing with remote objects, it is not always possible to determine if it + exists when creating a QDBusInterface. +*/ +bool QDBusAbstractInterface::isValid() const +{ + return d_func()->isValid; +} + +/*! + Returns the connection this interface is assocated with. +*/ +QDBusConnection QDBusAbstractInterface::connection() const +{ + return d_func()->connection; +} + +/*! + Returns the name of the service this interface is associated with. +*/ +QString QDBusAbstractInterface::service() const +{ + return d_func()->service; +} + +/*! + Returns the object path that this interface is associated with. +*/ +QString QDBusAbstractInterface::path() const +{ + return d_func()->path; +} + +/*! + Returns the name of this interface. +*/ +QString QDBusAbstractInterface::interface() const +{ + return d_func()->interface; +} + +/*! + Returns the error the last operation produced, or an invalid error if the last operation did not + produce an error. +*/ +QDBusError QDBusAbstractInterface::lastError() const +{ + return d_func()->lastError; +} + +/*! + Places a call to the remote method specified by \a method on this interface, using \a args as + arguments. This function returns the message that was received as a reply, which can be a normal + QDBusMessage::ReplyMessage (indicating success) or QDBusMessage::ErrorMessage (if the call + failed). The \a mode parameter specifies how this call should be placed. + + If the call succeeds, lastError() will be cleared; otherwise, it will contain the error this + call produced. + + Normally, you should place calls using call(). + + \warning If you use \c UseEventLoop, your code must be prepared to deal with any reentrancy: + other method calls and signals may be delivered before this function returns, as well + as other Qt queued signals and events. + + \threadsafe +*/ +QDBusMessage QDBusAbstractInterface::callWithArgumentList(QDBus::CallMode mode, + const QString& method, + const QList<QVariant>& args) +{ + Q_D(QDBusAbstractInterface); + + QString m = method; + // split out the signature from the method + int pos = method.indexOf(QLatin1Char('.')); + if (pos != -1) + m.truncate(pos); + + if (mode == QDBus::AutoDetect) { + // determine if this a sync or async call + mode = QDBus::Block; + const QMetaObject *mo = metaObject(); + QByteArray match = m.toLatin1() + '('; + + for (int i = staticMetaObject.methodCount(); i < mo->methodCount(); ++i) { + QMetaMethod mm = mo->method(i); + if (QByteArray(mm.signature()).startsWith(match)) { + // found a method with the same name as what we're looking for + // hopefully, nobody is overloading asynchronous and synchronous methods with + // the same name + + QList<QByteArray> tags = QByteArray(mm.tag()).split(' '); + if (tags.contains("Q_NOREPLY")) + mode = QDBus::NoBlock; + + break; + } + } + } + +// qDebug() << "QDBusAbstractInterface" << "Service" << service() << "Path:" << path(); + QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(), interface(), m); + msg.setArguments(args); + + QDBusMessage reply = d->connection.call(msg, mode); + d->lastError = reply; // will clear if reply isn't an error + + // ensure that there is at least one element + if (reply.arguments().isEmpty()) + reply << QVariant(); + + return reply; +} + +/*! + \since 4.5 + Places a call to the remote method specified by \a method on this + interface, using \a args as arguments. This function returns a + QDBusPendingCall object that can be used to track the status of the + reply and access its contents once it has arrived. + + Normally, you should place calls using asyncCall(). + + \threadsafe +*/ +QDBusPendingCall QDBusAbstractInterface::asyncCallWithArgumentList(const QString& method, + const QList<QVariant>& args) +{ + Q_D(QDBusAbstractInterface); + + QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(), interface(), method); + msg.setArguments(args); + return d->connection.asyncCall(msg); +} + +/*! + Places a call to the remote method specified by \a method + on this interface, using \a args as arguments. This function + returns immediately after queueing the call. The reply from + the remote function is delivered to the \a returnMethod on + object \a receiver. If an error occurs, the \a errorMethod + on object \a receiver is called instead. + + This function returns true if the queueing succeeds. It does + not indicate that the executed call succeeded. If it fails, + the \a errorMethod is called. + + The \a returnMethod must have as its parameters the types returned + by the function call. Optionally, it may have a QDBusMessage + parameter as its last or only parameter. The \a errorMethod must + have a QDBusError as its only parameter. + + \since 4.3 + \sa QDBusError, QDBusMessage + */ +bool QDBusAbstractInterface::callWithCallback(const QString &method, + const QList<QVariant> &args, + QObject *receiver, + const char *returnMethod, + const char *errorMethod) +{ + Q_D(QDBusAbstractInterface); + + QDBusMessage msg = QDBusMessage::createMethodCall(service(), + path(), + interface(), + method); + msg.setArguments(args); + + d->lastError = 0; + return d->connection.callWithCallback(msg, + receiver, + returnMethod, + errorMethod); +} + +/*! + \overload + + This function is deprecated. Please use the overloaded version. + + Places a call to the remote method specified by \a method + on this interface, using \a args as arguments. This function + returns immediately after queueing the call. The reply from + the remote function or any errors emitted by it are delivered + to the \a slot slot on object \a receiver. + + This function returns true if the queueing succeeded: it does + not indicate that the call succeeded. If it failed, the slot + will be called with an error message. lastError() will not be + set under those circumstances. + + \sa QDBusError, QDBusMessage +*/ +bool QDBusAbstractInterface::callWithCallback(const QString &method, + const QList<QVariant> &args, + QObject *receiver, + const char *slot) +{ + return callWithCallback(method, args, receiver, slot, 0); +} + +/*! + \internal + Catch signal connections. +*/ +void QDBusAbstractInterface::connectNotify(const char *signal) +{ + // we end up recursing here, so optimise away + if (qstrcmp(signal + 1, "destroyed(QObject*)") == 0) + return; + + // someone connecting to one of our signals + Q_D(QDBusAbstractInterface); + + QDBusConnectionPrivate *conn = d->connectionPrivate(); + if (conn) + conn->connectRelay(d->service, d->currentOwner, d->path, d->interface, + this, signal); +} + +/*! + \internal + Catch signal disconnections. +*/ +void QDBusAbstractInterface::disconnectNotify(const char *signal) +{ + // someone disconnecting from one of our signals + Q_D(QDBusAbstractInterface); + + QDBusConnectionPrivate *conn = d->connectionPrivate(); + if (conn) + conn->disconnectRelay(d->service, d->currentOwner, d->path, d->interface, + this, signal); +} + +/*! + \internal + Get the value of the property \a propname. +*/ +QVariant QDBusAbstractInterface::internalPropGet(const char *propname) const +{ + // assume this property exists and is readable + // we're only called from generated code anyways + + int idx = metaObject()->indexOfProperty(propname); + if (idx != -1) + return d_func()->property(metaObject()->property(idx)); + qWarning("QDBusAbstractInterface::internalPropGet called with unknown property '%s'", propname); + return QVariant(); // error +} + +/*! + \internal + Set the value of the property \a propname to \a value. +*/ +void QDBusAbstractInterface::internalPropSet(const char *propname, const QVariant &value) +{ + Q_D(QDBusAbstractInterface); + + // assume this property exists and is writeable + // we're only called from generated code anyways + + int idx = metaObject()->indexOfProperty(propname); + if (idx != -1) + d->setProperty(metaObject()->property(idx), value); + else + qWarning("QDBusAbstractInterface::internalPropGet called with unknown property '%s'", propname); +} + +/*! + Calls the method \a method on this interface and passes the parameters to this function to the + method. + + The parameters to \c call are passed on to the remote function via D-Bus as input + arguments. Output arguments are returned in the QDBusMessage reply. If the reply is an error + reply, lastError() will also be set to the contents of the error message. + + This function can be used with up to 8 parameters, passed in arguments \a arg1, \a arg2, + \a arg3, \a arg4, \a arg5, \a arg6, \a arg7 and \a arg8. If you need more than 8 + parameters or if you have a variable number of parameters to be passed, use + callWithArgumentList(). + + It can be used the following way: + + \snippet doc/src/snippets/code/src_qdbus_qdbusabstractinterface.cpp 0 + + This example illustrates function calling with 0, 1 and 2 parameters and illustrates different + parameter types passed in each (the first call to \c "ProcessWorkUnicode" will contain one + Unicode string, the second call to \c "ProcessWork" will contain one string and one byte array). +*/ +QDBusMessage QDBusAbstractInterface::call(const QString &method, const QVariant &arg1, + const QVariant &arg2, + const QVariant &arg3, + const QVariant &arg4, + const QVariant &arg5, + const QVariant &arg6, + const QVariant &arg7, + const QVariant &arg8) +{ + return call(QDBus::AutoDetect, method, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); +} + +/*! + \overload + + Calls the method \a method on this interface and passes the + parameters to this function to the method. If \a mode is \c + NoWaitForReply, then this function will return immediately after + placing the call, without waiting for a reply from the remote + method. Otherwise, \a mode indicates whether this function should + activate the Qt Event Loop while waiting for the reply to arrive. + + This function can be used with up to 8 parameters, passed in arguments \a arg1, \a arg2, + \a arg3, \a arg4, \a arg5, \a arg6, \a arg7 and \a arg8. If you need more than 8 + parameters or if you have a variable number of parameters to be passed, use + callWithArgumentList(). + + If this function reenters the Qt event loop in order to wait for the + reply, it will exclude user input. During the wait, it may deliver + signals and other method calls to your application. Therefore, it + must be prepared to handle a reentrancy whenever a call is placed + with call(). +*/ +QDBusMessage QDBusAbstractInterface::call(QDBus::CallMode mode, const QString &method, + const QVariant &arg1, + const QVariant &arg2, + const QVariant &arg3, + const QVariant &arg4, + const QVariant &arg5, + const QVariant &arg6, + const QVariant &arg7, + const QVariant &arg8) +{ + QList<QVariant> argList; + int count = 0 + arg1.isValid() + arg2.isValid() + arg3.isValid() + arg4.isValid() + + arg5.isValid() + arg6.isValid() + arg7.isValid() + arg8.isValid(); + + switch (count) { + case 8: + argList.prepend(arg8); + case 7: + argList.prepend(arg7); + case 6: + argList.prepend(arg6); + case 5: + argList.prepend(arg5); + case 4: + argList.prepend(arg4); + case 3: + argList.prepend(arg3); + case 2: + argList.prepend(arg2); + case 1: + argList.prepend(arg1); + } + + return callWithArgumentList(mode, method, argList); +} + + +/*! + \since 4.5 + Calls the method \a method on this interface and passes the parameters to this function to the + method. + + The parameters to \c call are passed on to the remote function via D-Bus as input + arguments. The returned QDBusPendingCall object can be used to find out information about + the reply. + + This function can be used with up to 8 parameters, passed in arguments \a arg1, \a arg2, + \a arg3, \a arg4, \a arg5, \a arg6, \a arg7 and \a arg8. If you need more than 8 + parameters or if you have a variable number of parameters to be passed, use + asyncCallWithArgumentList(). + + It can be used the following way: + + \snippet doc/src/snippets/code/src_qdbus_qdbusabstractinterface.cpp 1 + + This example illustrates function calling with 0, 1 and 2 parameters and illustrates different + parameter types passed in each (the first call to \c "ProcessWorkUnicode" will contain one + Unicode string, the second call to \c "ProcessWork" will contain one string and one byte array). +*/ +QDBusPendingCall QDBusAbstractInterface::asyncCall(const QString &method, const QVariant &arg1, + const QVariant &arg2, + const QVariant &arg3, + const QVariant &arg4, + const QVariant &arg5, + const QVariant &arg6, + const QVariant &arg7, + const QVariant &arg8) +{ + QList<QVariant> argList; + int count = 0 + arg1.isValid() + arg2.isValid() + arg3.isValid() + arg4.isValid() + + arg5.isValid() + arg6.isValid() + arg7.isValid() + arg8.isValid(); + + switch (count) { + case 8: + argList.prepend(arg8); + case 7: + argList.prepend(arg7); + case 6: + argList.prepend(arg6); + case 5: + argList.prepend(arg5); + case 4: + argList.prepend(arg4); + case 3: + argList.prepend(arg3); + case 2: + argList.prepend(arg2); + case 1: + argList.prepend(arg1); + } + + return asyncCallWithArgumentList(method, argList); +} + +/*! + \internal +*/ +QDBusMessage QDBusAbstractInterface::internalConstCall(QDBus::CallMode mode, + const QString &method, + const QList<QVariant> &args) const +{ + // ### move the code here, and make the other functions call this + return const_cast<QDBusAbstractInterface*>(this)->callWithArgumentList(mode, method, args); +} + +QT_END_NAMESPACE + +#include "moc_qdbusabstractinterface.cpp" diff --git a/src/dbus/qdbusabstractinterface.h b/src/dbus/qdbusabstractinterface.h new file mode 100644 index 0000000..ef69c44 --- /dev/null +++ b/src/dbus/qdbusabstractinterface.h @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDBUSABSTRACTINTERFACE_H +#define QDBUSABSTRACTINTERFACE_H + +#include <QtCore/qstring.h> +#include <QtCore/qvariant.h> +#include <QtCore/qlist.h> +#include <QtCore/qobject.h> + +#include <QtDBus/qdbusmessage.h> +#include <QtDBus/qdbusextratypes.h> +#include <QtDBus/qdbusconnection.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(DBus) + +class QDBusError; +class QDBusPendingCall; + +class QDBusAbstractInterfacePrivate; +class QDBUS_EXPORT QDBusAbstractInterface: public QObject +{ + Q_OBJECT + +public: + virtual ~QDBusAbstractInterface(); + bool isValid() const; + + QDBusConnection connection() const; + + QString service() const; + QString path() const; + QString interface() const; + + QDBusError lastError() const; + + QDBusMessage call(const QString &method, + const QVariant &arg1 = QVariant(), + const QVariant &arg2 = QVariant(), + const QVariant &arg3 = QVariant(), + const QVariant &arg4 = QVariant(), + const QVariant &arg5 = QVariant(), + const QVariant &arg6 = QVariant(), + const QVariant &arg7 = QVariant(), + const QVariant &arg8 = QVariant()); + + QDBusMessage call(QDBus::CallMode mode, + const QString &method, + const QVariant &arg1 = QVariant(), + const QVariant &arg2 = QVariant(), + const QVariant &arg3 = QVariant(), + const QVariant &arg4 = QVariant(), + const QVariant &arg5 = QVariant(), + const QVariant &arg6 = QVariant(), + const QVariant &arg7 = QVariant(), + const QVariant &arg8 = QVariant()); + + QDBusMessage callWithArgumentList(QDBus::CallMode mode, + const QString &method, + const QList<QVariant> &args); + + bool callWithCallback(const QString &method, + const QList<QVariant> &args, + QObject *receiver, const char *member, const char *errorSlot); + bool callWithCallback(const QString &method, + const QList<QVariant> &args, + QObject *receiver, const char *member); + + QDBusPendingCall asyncCall(const QString &method, + const QVariant &arg1 = QVariant(), + const QVariant &arg2 = QVariant(), + const QVariant &arg3 = QVariant(), + const QVariant &arg4 = QVariant(), + const QVariant &arg5 = QVariant(), + const QVariant &arg6 = QVariant(), + const QVariant &arg7 = QVariant(), + const QVariant &arg8 = QVariant()); + QDBusPendingCall asyncCallWithArgumentList(const QString &method, + const QList<QVariant> &args); + +protected: + QDBusAbstractInterface(const QString &service, const QString &path, const char *interface, + const QDBusConnection &connection, QObject *parent); + QDBusAbstractInterface(QDBusAbstractInterfacePrivate &, QObject *parent); + + void connectNotify(const char *signal); + void disconnectNotify(const char *signal); + QVariant internalPropGet(const char *propname) const; + void internalPropSet(const char *propname, const QVariant &value); + QDBusMessage internalConstCall(QDBus::CallMode mode, + const QString &method, + const QList<QVariant> &args = QList<QVariant>()) const; + +private: + Q_DECLARE_PRIVATE(QDBusAbstractInterface) + Q_PRIVATE_SLOT(d_func(), void _q_serviceOwnerChanged(QString,QString,QString)) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/dbus/qdbusabstractinterface_p.h b/src/dbus/qdbusabstractinterface_p.h new file mode 100644 index 0000000..4735769 --- /dev/null +++ b/src/dbus/qdbusabstractinterface_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the public API. This header file may +// change from version to version without notice, or even be +// removed. +// +// We mean it. +// +// + +#ifndef QDBUSABSTRACTINTERFACEPRIVATE_H +#define QDBUSABSTRACTINTERFACEPRIVATE_H + +#include <qdbusabstractinterface.h> +#include <qdbusconnection.h> +#include <qdbuserror.h> +#include "qdbusconnection_p.h" +#include "private/qobject_p.h" + +#define ANNOTATION_NO_WAIT "org.freedesktop.DBus.Method.NoReply" + +QT_BEGIN_NAMESPACE + +class QDBusAbstractInterfacePrivate : public QObjectPrivate +{ +public: + Q_DECLARE_PUBLIC(QDBusAbstractInterface) + + mutable QDBusConnection connection; // mutable because we want to make calls from const functions + QString service; + QString currentOwner; + QString path; + QString interface; + mutable QDBusError lastError; + bool isValid; + + QDBusAbstractInterfacePrivate(const QString &serv, const QString &p, + const QString &iface, const QDBusConnection& con, bool dynamic); + virtual ~QDBusAbstractInterfacePrivate() { } + + // these functions do not check if the property is valid + QVariant property(const QMetaProperty &mp) const; + void setProperty(const QMetaProperty &mp, const QVariant &value); + + // return conn's d pointer + inline QDBusConnectionPrivate *connectionPrivate() const + { return QDBusConnectionPrivate::d(connection); } + + void _q_serviceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/dbus/qdbusargument.cpp b/src/dbus/qdbusargument.cpp new file mode 100644 index 0000000..fa2b68f --- /dev/null +++ b/src/dbus/qdbusargument.cpp @@ -0,0 +1,1331 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdbusargument.h" + +#include <qatomic.h> +#include <qbytearray.h> +#include <qlist.h> +#include <qmap.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qvariant.h> +#include <qdatetime.h> +#include <qrect.h> +#include <qline.h> + +#include "qdbusargument_p.h" +#include "qdbusmetatype_p.h" +#include "qdbusutil_p.h" + +QT_BEGIN_NAMESPACE + +QDBusArgumentPrivate::~QDBusArgumentPrivate() +{ + if (message) + q_dbus_message_unref(message); +} + +QByteArray QDBusArgumentPrivate::createSignature(int id) +{ + if (!qdbus_loadLibDBus()) + return ""; + + QByteArray signature; + QDBusMarshaller *marshaller = new QDBusMarshaller; + marshaller->ba = &signature; + + // run it + void *null = 0; + QVariant v(id, null); + QDBusArgument arg(marshaller); + QDBusMetaType::marshall(arg, v.userType(), v.constData()); + arg.d = 0; + + // delete it + bool ok = marshaller->ok; + delete marshaller; + + if (signature.isEmpty() || !ok || !QDBusUtil::isValidSingleSignature(QString::fromLatin1(signature))) { + qWarning("QDBusMarshaller: type `%s' produces invalid D-BUS signature `%s' " + "(Did you forget to call beginStructure() ?)", + QVariant::typeToName( QVariant::Type(id) ), + signature.isEmpty() ? "<empty>" : signature.constData()); + return ""; + } else if ((signature.at(0) != DBUS_TYPE_ARRAY && signature.at(0) != DBUS_STRUCT_BEGIN_CHAR) || + (signature.at(0) == DBUS_TYPE_ARRAY && (signature.at(1) == DBUS_TYPE_BYTE || + signature.at(1) == DBUS_TYPE_STRING))) { + qWarning("QDBusMarshaller: type `%s' attempts to redefine basic D-BUS type '%s' (%s) " + "(Did you forget to call beginStructure() ?)", + QVariant::typeToName( QVariant::Type(id) ), + signature.constData(), + QVariant::typeToName( QVariant::Type(QDBusMetaType::signatureToType(signature))) ); + return ""; + } + return signature; +} + +bool QDBusArgumentPrivate::checkWrite(QDBusArgumentPrivate *&d) +{ + if (!d) + return false; + if (d->direction == Marshalling) { + if (!d->marshaller()->ok) + return false; + + if (d->message && d->ref != 1) { + QDBusMarshaller *dd = new QDBusMarshaller; + dd->message = q_dbus_message_copy(d->message); + q_dbus_message_iter_init_append(dd->message, &dd->iterator); + + if (!d->ref.deref()) + delete d; + d = dd; + } + return true; + } + +#ifdef QT_DEBUG + qFatal("QDBusArgument: write from a read-only object"); +#else + qWarning("QDBusArgument: write from a read-only object"); +#endif + return false; +} + +bool QDBusArgumentPrivate::checkRead(QDBusArgumentPrivate *d) +{ + if (!d) + return false; + if (d->direction == Demarshalling) + return true; + +#ifdef QT_DEBUG + qFatal("QDBusArgument: read from a write-only object"); +#else + qWarning("QDBusArgument: read from a write-only object"); +#endif + + return false; +} + +bool QDBusArgumentPrivate::checkReadAndDetach(QDBusArgumentPrivate *&d) +{ + if (!checkRead(d)) + return false; // don't bother + + if (d->ref == 1) + return true; // no need to detach + + QDBusDemarshaller *dd = new QDBusDemarshaller; + dd->message = q_dbus_message_ref(d->message); + dd->iterator = static_cast<QDBusDemarshaller*>(d)->iterator; + + if (!d->ref.deref()) + delete d; + d = dd; + return true; +} + +/*! + \class QDBusArgument + \inmodule QtDBus + \since 4.2 + + \brief The QDBusArgument class is used to marshall and demarshall D-Bus arguments. + + The class is used to send arguments over D-Bus to remote + applications and to receive them back. D-Bus offers an extensible + type system, based on a few primitive types and associations of + them. See the \l {qdbustypesystem.html}{QtDBus type system} page + for more information on the type system. + + QDBusArgument is the central class in the QtDBus type system, + providing functions to marshall and demarshall the primitive + types. The compound types are then created by association of one + or more of the primitive types in arrays, dictionaries or + structures. + + The following example illustrates how a structure containing an + integer and a string can be constructed using the \l + {qdbustypesystem.html}{QtDBus type system}: + + \snippet doc/src/snippets/code/src_qdbus_qdbusargument.cpp 0 + + The type has to be registered with qDBusRegisterMetaType() before + it can be used with QDBusArgument. Therefore, somewhere in your + program, you should add the following code: + + \snippet doc/src/snippets/code/src_qdbus_qdbusargument.cpp 1 + + Once registered, a type can be used in outgoing method calls + (placed with QDBusAbstractInterface::call()), signal emissions + from registered objects or in incoming calls from remote + applications. + + It is important to note that the \c{operator<<} and \c{operator>>} + streaming functions must always produce the same number of entries + in case of structures, both in reading and in writing (marshalling + and demarshalling), otherwise calls and signals may start to + silently fail. + + The following example illustrates this wrong usage + in context of a class that may contain invalid data: + + \badcode + // Wrongly marshall the MyTime data into a D-Bus argument + QDBusArgument &operator<<(QDBusArgument &argument, const MyTime &mytime) + { + argument.beginStructure(); + if (mytime.isValid) + argument << true << mytime.hour + << mytime.minute << mytime.second; + else + argument << false; + argument.endStructure(); + return argument; + } + \endcode + + In this example, both the \c{operator<<} and the \c{operator>>} + functions may produce a different number of reads/writes. This can + confuse the QtDBus type system and should be avoided. + + \sa QDBusAbstractInterface, {qdbustypesystem.html}{The QtDBus type + system}, {usingadaptors.html}{Using Adaptors}, qdbus_cast() +*/ + +/*! + \enum QDBusArgument::ElementType + \since 4.5 + + This enum describes the type of element held by the argument. + + \value BasicType A basic element, which is understood by + QVariant. The following types are considered basic: bool, + byte, short, ushort, int, uint, qint64, quint64, double, + QString, QByteArray, QDBusObjectPath, QDBusSignature + + \value VariantType The variant element (QDBusVariant) + + \value ArrayType An array element, usually represented by QList<T> + or QVector<T>. Note: QByteArray and associative maps are not + considered arrays, even if the D-Bus protocol transports them as such. + + \value StructureType A custom type represented by a structure, + like QDateTime, QPoint, etc. + + \value MapType An associative container, like QMap<Key, Value> or + QHash<Key, Value> + + \value MapEntryType One entry in an associative container: both + the key and the value form one map-entry type. + + \value UnknownType The type is unknown or we have reached the end + of the list. + + \sa currentType() +*/ + +/*! + \fn qdbus_cast(const QDBusArgument &argument) + \relates QDBusArgument + \since 4.2 + + Attempts to demarshall the contents of \a argument into the type + \c{T}. For example: + + \snippet doc/src/snippets/code/src_qdbus_qdbusargument.cpp 2 + + Note that it is equivalent to the following: + + \snippet doc/src/snippets/code/src_qdbus_qdbusargument.cpp 3 +*/ + +/*! + Constructs an empty QDBusArgument argument. + + An empty QDBusArgument object does not allow either reading or + writing to be performed. +*/ +QDBusArgument::QDBusArgument() +{ + if (!qdbus_loadLibDBus()) { + d = 0; + return; + } + + QDBusMarshaller *dd = new QDBusMarshaller; + d = dd; + + // create a new message with any type, we won't sent it anyways + dd->message = q_dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL); + q_dbus_message_iter_init_append(dd->message, &dd->iterator); +} + +/*! + Constructs a copy of the \a other QDBusArgument object. + + Both objects will therefore contain the same state from this point + forward. QDBusArguments are explicitly shared and, therefore, any + modification to either copy will affect the other one too. +*/ +QDBusArgument::QDBusArgument(const QDBusArgument &other) + : d(other.d) +{ + if (d) + d->ref.ref(); +} + +/*! + \internal +*/ +QDBusArgument::QDBusArgument(QDBusArgumentPrivate *dd) + : d(dd) +{ +} + +/*! + Copies the \a other QDBusArgument object into this one. + + Both objects will therefore contain the same state from this point + forward. QDBusArguments are explicitly shared and, therefore, any + modification to either copy will affect the other one too. +*/ +QDBusArgument &QDBusArgument::operator=(const QDBusArgument &other) +{ + qAtomicAssign(d, other.d); + return *this; +} + +/*! + Disposes of the resources associated with this QDBusArgument + object. +*/ +QDBusArgument::~QDBusArgument() +{ + if (d && !d->ref.deref()) + delete d; +} + +/*! + Appends the primitive value \a arg of type \c{BYTE} to the D-Bus stream. +*/ +QDBusArgument &QDBusArgument::operator<<(uchar arg) +{ + if (QDBusArgumentPrivate::checkWrite(d)) + d->marshaller()->append(arg); + return *this; +} + +/*! + \overload + Appends the primitive value \a arg of type \c{BOOLEAN} to the D-Bus stream. +*/ +QDBusArgument &QDBusArgument::operator<<(bool arg) +{ + if (QDBusArgumentPrivate::checkWrite(d)) + d->marshaller()->append(arg); + return *this; +} + +/*! + \overload + Appends the primitive value \a arg of type \c{INT16} to the D-Bus stream. +*/ +QDBusArgument &QDBusArgument::operator<<(short arg) +{ + if (QDBusArgumentPrivate::checkWrite(d)) + d->marshaller()->append(arg); + return *this; +} + +/*! + \overload + Appends the primitive value \a arg of type \c{UINT16} to the D-Bus stream. +*/ +QDBusArgument &QDBusArgument::operator<<(ushort arg) +{ + if (QDBusArgumentPrivate::checkWrite(d)) + d->marshaller()->append(arg); + return *this; +} + +/*! + \overload + Appends the primitive value \a arg of type \c{INT32} to the D-Bus stream. +*/ +QDBusArgument &QDBusArgument::operator<<(int arg) +{ + if (QDBusArgumentPrivate::checkWrite(d)) + d->marshaller()->append(arg); + return *this; +} + +/*! + \overload + Appends the primitive value \a arg of type \c{UINT32} to the D-Bus stream. +*/ +QDBusArgument &QDBusArgument::operator<<(uint arg) +{ + if (QDBusArgumentPrivate::checkWrite(d)) + d->marshaller()->append(arg); + return *this; +} + +/*! + \overload + Appends the primitive value \a arg of type \c{INT64} to the D-Bus stream. +*/ +QDBusArgument &QDBusArgument::operator<<(qlonglong arg) +{ + if (QDBusArgumentPrivate::checkWrite(d)) + d->marshaller()->append(arg); + return *this; +} + +/*! + \overload + Appends the primitive value \a arg of type \c{UINT64} to the D-Bus stream. +*/ +QDBusArgument &QDBusArgument::operator<<(qulonglong arg) +{ + if (QDBusArgumentPrivate::checkWrite(d)) + d->marshaller()->append(arg); + return *this; +} + +/*! + \overload + Appends the primitive value \a arg of type \c{DOUBLE} (double-precision + floating-point) to the D-Bus stream. +*/ +QDBusArgument &QDBusArgument::operator<<(double arg) +{ + if (QDBusArgumentPrivate::checkWrite(d)) + d->marshaller()->append(arg); + return *this; +} + +/*! + \overload + Appends the primitive value \a arg of type \c{STRING} (Unicode character + string) to the D-Bus stream. +*/ +QDBusArgument &QDBusArgument::operator<<(const QString &arg) +{ + if (QDBusArgumentPrivate::checkWrite(d)) + d->marshaller()->append(arg); + return *this; +} + +/*! + \overload + \internal + Appends the primitive value \a arg of type \c{OBJECT_PATH} (path to a D-Bus + object) to the D-Bus stream. +*/ +QDBusArgument &QDBusArgument::operator<<(const QDBusObjectPath &arg) +{ + if (QDBusArgumentPrivate::checkWrite(d)) + d->marshaller()->append(arg); + return *this; +} + +/*! + \overload + \internal + Appends the primitive value \a arg of type \c{SIGNATURE} (D-Bus type + signature) to the D-Bus stream. +*/ +QDBusArgument &QDBusArgument::operator<<(const QDBusSignature &arg) +{ + if (QDBusArgumentPrivate::checkWrite(d)) + d->marshaller()->append(arg); + return *this; +} + +/*! + \overload + Appends the primitive value \a arg of type \c{VARIANT} to the D-Bus stream. + + A D-Bus variant type can contain any type, including other + variants. It is similar to the Qt QVariant type. +*/ +QDBusArgument &QDBusArgument::operator<<(const QDBusVariant &arg) +{ + if (QDBusArgumentPrivate::checkWrite(d)) + d->marshaller()->append(arg); + return *this; +} + +/*! + \overload + Appends the QStringList given by \a arg as \c{ARRAY of STRING} + to the D-Bus stream. + + QStringList and QByteArray are the only two non-primitive types + that are supported directly by QDBusArgument because of their + widespread usage in Qt applications. + + Other arrays are supported through compound types in QtDBus. +*/ +QDBusArgument &QDBusArgument::operator<<(const QStringList &arg) +{ + if (QDBusArgumentPrivate::checkWrite(d)) + d->marshaller()->append(arg); + return *this; +} + +/*! + \overload + Appends the QByteArray given by \a arg as \c{ARRAY of BYTE} + to the D-Bus stream. + + QStringList and QByteArray are the only two non-primitive types + that are supported directly by QDBusArgument because of their + widespread usage in Qt applications. + + Other arrays are supported through compound types in QtDBus. +*/ +QDBusArgument &QDBusArgument::operator<<(const QByteArray &arg) +{ + if (QDBusArgumentPrivate::checkWrite(d)) + d->marshaller()->append(arg); + return *this; +} + +/*! + \internal + Returns the type signature of the D-Bus type this QDBusArgument + \since 4.5 + + Appends the variant \a v. + + \sa asVariant() +*/ +void QDBusArgument::appendVariant(const QVariant &v) +{ + if (QDBusArgumentPrivate::checkWrite(d)) + d->marshaller()->appendVariantInternal(v); +} + +/*! + \internal + Returns the type signature of the D-Bus type this QDBusArgument + object is currently pointing to. +*/ +QString QDBusArgument::currentSignature() const +{ + if (!d) + return QString(); + if (d->direction == QDBusArgumentPrivate::Demarshalling) + return d->demarshaller()->currentSignature(); + else + return d->marshaller()->currentSignature(); +} + +/*! + \since 4.5 + Returns the classification of the current element type. If an + error decoding the type occurs or if we're at the end of the + argument, this function returns QDBusArgument::UnknownType. + + This function only makes sense when demarshalling arguments. If it + is used while marshalling, it will always return UnknownType. +*/ +QDBusArgument::ElementType QDBusArgument::currentType() const +{ + if (!d) + return UnknownType; + if (d->direction == QDBusArgumentPrivate::Demarshalling) + return d->demarshaller()->currentType(); + return UnknownType; +} + +/*! + Extracts one D-BUS primitive argument of type \c{BYTE} from the + D-BUS stream and puts it into \a arg. +*/ +const QDBusArgument &QDBusArgument::operator>>(uchar &arg) const +{ + if (QDBusArgumentPrivate::checkReadAndDetach(d)) + arg = d->demarshaller()->toByte(); + return *this; +} + +/*! + \overload + Extracts one D-Bus primitive argument of type \c{BOOLEAN} from the + D-Bus stream. +*/ +const QDBusArgument &QDBusArgument::operator>>(bool &arg) const +{ + if (QDBusArgumentPrivate::checkReadAndDetach(d)) + arg = d->demarshaller()->toBool(); + return *this; +} + +/*! + \overload + Extracts one D-Bus primitive argument of type \c{UINT16} from the + D-Bus stream. +*/ +const QDBusArgument &QDBusArgument::operator>>(ushort &arg) const +{ + if (QDBusArgumentPrivate::checkReadAndDetach(d)) + arg = d->demarshaller()->toUShort(); + return *this; +} + +/*! + \overload + Extracts one D-Bus primitive argument of type \c{INT16} from the + D-Bus stream. +*/ +const QDBusArgument &QDBusArgument::operator>>(short &arg) const +{ + if (QDBusArgumentPrivate::checkReadAndDetach(d)) + arg = d->demarshaller()->toShort(); + return *this; +} + +/*! + \overload + Extracts one D-Bus primitive argument of type \c{INT32} from the + D-Bus stream. +*/ +const QDBusArgument &QDBusArgument::operator>>(int &arg) const +{ + if (QDBusArgumentPrivate::checkReadAndDetach(d)) + arg = d->demarshaller()->toInt(); + return *this; +} + +/*! + \overload + Extracts one D-Bus primitive argument of type \c{UINT32} from the + D-Bus stream. +*/ +const QDBusArgument &QDBusArgument::operator>>(uint &arg) const +{ + if (QDBusArgumentPrivate::checkReadAndDetach(d)) + arg = d->demarshaller()->toUInt(); + return *this; +} + +/*! + \overload + Extracts one D-Bus primitive argument of type \c{INT64} from the + D-Bus stream. +*/ +const QDBusArgument &QDBusArgument::operator>>(qlonglong &arg) const +{ + if (QDBusArgumentPrivate::checkReadAndDetach(d)) + arg = d->demarshaller()->toLongLong(); + return *this; +} + +/*! + \overload + Extracts one D-Bus primitive argument of type \c{UINT64} from the + D-Bus stream. +*/ +const QDBusArgument &QDBusArgument::operator>>(qulonglong &arg) const +{ + if (QDBusArgumentPrivate::checkReadAndDetach(d)) + arg = d->demarshaller()->toULongLong(); + return *this; +} + +/*! + \overload + Extracts one D-Bus primitive argument of type \c{DOUBLE} + (double-precision floating pount) from the D-Bus stream. +*/ +const QDBusArgument &QDBusArgument::operator>>(double &arg) const +{ + if (QDBusArgumentPrivate::checkReadAndDetach(d)) + arg = d->demarshaller()->toDouble(); + return *this; +} + +/*! + \overload + Extracts one D-Bus primitive argument of type \c{STRING} (Unicode + character string) from the D-Bus stream. +*/ +const QDBusArgument &QDBusArgument::operator>>(QString &arg) const +{ + if (QDBusArgumentPrivate::checkReadAndDetach(d)) + arg = d->demarshaller()->toString(); + return *this; +} + +/*! + \overload + \internal + Extracts one D-Bus primitive argument of type \c{OBJECT_PATH} + (D-Bus path to an object) from the D-Bus stream. +*/ +const QDBusArgument &QDBusArgument::operator>>(QDBusObjectPath &arg) const +{ + if (QDBusArgumentPrivate::checkReadAndDetach(d)) + arg = d->demarshaller()->toObjectPath(); + return *this; +} + +/*! + \overload + \internal + Extracts one D-Bus primitive argument of type \c{SIGNATURE} (D-Bus + type signature) from the D-Bus stream. +*/ +const QDBusArgument &QDBusArgument::operator>>(QDBusSignature &arg) const +{ + if (QDBusArgumentPrivate::checkReadAndDetach(d)) + arg = d->demarshaller()->toSignature(); + return *this; +} + +/*! + \overload + Extracts one D-Bus primitive argument of type \c{VARIANT} from the + D-Bus stream. + + A D-Bus variant type can contain any type, including other + variants. It is similar to the Qt QVariant type. + + In case the variant contains a type not directly supported by + QDBusArgument, the value of the returned QDBusVariant will contain + another QDBusArgument. It is your responsibility to further + demarshall it into another type. +*/ +const QDBusArgument &QDBusArgument::operator>>(QDBusVariant &arg) const +{ + if (QDBusArgumentPrivate::checkReadAndDetach(d)) + arg = d->demarshaller()->toVariant(); + return *this; +} + +/*! + \overload + Extracts an array of strings from the D-Bus stream and return it + as a QStringList. + + QStringList and QByteArray are the only two non-primitive types + that are supported directly by QDBusArgument because of their + widespread usage in Qt applications. + + Other arrays are supported through compound types in QtDBus. +*/ +const QDBusArgument &QDBusArgument::operator>>(QStringList &arg) const +{ + if (QDBusArgumentPrivate::checkReadAndDetach(d)) + arg = d->demarshaller()->toStringList(); + return *this; +} + +/*! + \overload + Extracts an array of bytes from the D-Bus stream and return it + as a QByteArray. + + QStringList and QByteArray are the only two non-primitive types + that are supported directly by QDBusArgument because of their + widespread usage in Qt applications. + + Other arrays are supported through compound types in QtDBus. +*/ +const QDBusArgument &QDBusArgument::operator>>(QByteArray &arg) const +{ + if (QDBusArgumentPrivate::checkReadAndDetach(d)) + arg = d->demarshaller()->toByteArray(); + return *this; +} + +/*! + Opens a new D-Bus structure suitable for appending new arguments. + + This function is used usually in \c{operator<<} streaming + operators, as in the following example: + + \snippet doc/src/snippets/code/src_qdbus_qdbusargument.cpp 4 + + Structures can contain other structures, so the following code is + also valid: + + \snippet doc/src/snippets/code/src_qdbus_qdbusargument.cpp 5 + + \sa endStructure(), beginArray(), beginMap() +*/ +void QDBusArgument::beginStructure() +{ + if (QDBusArgumentPrivate::checkWrite(d)) + d = d->marshaller()->beginStructure(); +} + +/*! + Closes a D-Bus structure opened with beginStructure(). This function must be called + same number of times that beginStructure() is called. + + \sa beginStructure(), endArray(), endMap() +*/ +void QDBusArgument::endStructure() +{ + if (QDBusArgumentPrivate::checkWrite(d)) + d = d->marshaller()->endStructure(); +} + +/*! + Opens a new D-Bus array suitable for appending elements of meta-type \a id. + + This function is used usually in \c{operator<<} streaming + operators, as in the following example: + + \snippet doc/src/snippets/code/src_qdbus_qdbusargument.cpp 6 + + If the type you want to marshall is a QList, QVector or any of the + Qt's \l {Generic Containers} that take one template parameter, + you need not declare an \c{operator<<} function for it, since + QtDBus provides generic templates to do the job of marshalling + the data. The same applies for STL's sequence containers, such + as \c {std::list}, \c {std::vector}, etc. + + \sa endArray(), beginStructure(), beginMap() +*/ +void QDBusArgument::beginArray(int id) +{ + if (QDBusArgumentPrivate::checkWrite(d)) + d = d->marshaller()->beginArray(id); +} + +/*! + Closes a D-Bus array opened with beginArray(). This function must be called + same number of times that beginArray() is called. + + \sa beginArray(), endStructure(), endMap() +*/ +void QDBusArgument::endArray() +{ + if (QDBusArgumentPrivate::checkWrite(d)) + d = d->marshaller()->endArray(); +} + +/*! + Opens a new D-Bus map suitable for + appending elements. Maps are containers that associate one entry + (the key) to another (the value), such as Qt's QMap or QHash. The + ids of the map's key and value meta types must be passed in \a kid + and \a vid respectively. + + This function is used usually in \c{operator<<} streaming + operators, as in the following example: + + \snippet doc/src/snippets/code/src_qdbus_qdbusargument.cpp 7 + + If the type you want to marshall is a QMap or QHash, you need not + declare an \c{operator<<} function for it, since QtDBus provides + generic templates to do the job of marshalling the data. + + \sa endMap(), beginStructure(), beginArray(), beginMapEntry() +*/ +void QDBusArgument::beginMap(int kid, int vid) +{ + if (QDBusArgumentPrivate::checkWrite(d)) + d = d->marshaller()->beginMap(kid, vid); +} + +/*! + Closes a D-Bus map opened with beginMap(). This function must be called + same number of times that beginMap() is called. + + \sa beginMap(), endStructure(), endArray() +*/ +void QDBusArgument::endMap() +{ + if (QDBusArgumentPrivate::checkWrite(d)) + d = d->marshaller()->endMap(); +} + +/*! + Opens a D-Bus map entry suitable for + appending the key and value entries. This function is only valid + when a map has been opened with beginMap(). + + See beginMap() for an example of usage of this function. + + \sa endMapEntry(), beginMap() +*/ +void QDBusArgument::beginMapEntry() +{ + if (QDBusArgumentPrivate::checkWrite(d)) + d = d->marshaller()->beginMapEntry(); +} + +/*! + Closes a D-Bus map entry opened with beginMapEntry(). This function must be called + same number of times that beginMapEntry() is called. + + \sa beginMapEntry() +*/ +void QDBusArgument::endMapEntry() +{ + if (QDBusArgumentPrivate::checkWrite(d)) + d = d->marshaller()->endMapEntry(); +} + +/*! + Opens a D-Bus structure suitable for extracting elements. + + This function is used usually in \c{operator>>} streaming + operators, as in the following example: + + \snippet doc/src/snippets/code/src_qdbus_qdbusargument.cpp 8 + + \sa endStructure(), beginArray(), beginMap() +*/ +void QDBusArgument::beginStructure() const +{ + if (QDBusArgumentPrivate::checkReadAndDetach(d)) + d = d->demarshaller()->beginStructure(); +} + +/*! + Closes the D-Bus structure and allow extracting of the next element + after the structure. + + \sa beginStructure() +*/ +void QDBusArgument::endStructure() const +{ + if (QDBusArgumentPrivate::checkReadAndDetach(d)) + d = d->demarshaller()->endStructure(); +} + +/*! + Recurses into the D-Bus array to allow extraction of + the array elements. + + This function is used usually in \c{operator>>} streaming + operators, as in the following example: + + \snippet doc/src/snippets/code/src_qdbus_qdbusargument.cpp 9 + + If the type you want to demarshall is a QList, QVector or any of the + Qt's \l {Generic Containers} that take one template parameter, you + need not declare an \c{operator>>} function for it, since QtDBus + provides generic templates to do the job of demarshalling the data. + The same applies for STL's sequence containers, such as \c {std::list}, + \c {std::vector}, etc. + + \sa atEnd(), beginStructure(), beginMap() +*/ +void QDBusArgument::beginArray() const +{ + if (QDBusArgumentPrivate::checkReadAndDetach(d)) + d = d->demarshaller()->beginArray(); +} + +/*! + Closes the D-Bus array and allow extracting of the next element + after the array. + + \sa beginArray() +*/ +void QDBusArgument::endArray() const +{ + if (QDBusArgumentPrivate::checkReadAndDetach(d)) + d = d->demarshaller()->endArray(); +} + +/*! + Recurses into the D-Bus map to allow extraction of + the map's elements. + + This function is used usually in \c{operator>>} streaming + operators, as in the following example: + + \snippet doc/src/snippets/code/src_qdbus_qdbusargument.cpp 10 + + If the type you want to demarshall is a QMap or QHash, you need not + declare an \c{operator>>} function for it, since QtDBus provides + generic templates to do the job of demarshalling the data. + + \sa endMap(), beginStructure(), beginArray(), beginMapEntry() +*/ +void QDBusArgument::beginMap() const +{ + if (QDBusArgumentPrivate::checkReadAndDetach(d)) + d = d->demarshaller()->beginMap(); +} + +/*! + Closes the D-Bus map and allow extracting of the next element + after the map. + + \sa beginMap() +*/ +void QDBusArgument::endMap() const +{ + if (QDBusArgumentPrivate::checkReadAndDetach(d)) + d = d->demarshaller()->endMap(); +} + +/*! + Recurses into the D-Bus map entry to allow extraction + of the key and value pair. + + See beginMap() for an example of how this function is usually used. + + \sa endMapEntry(), beginMap() +*/ +void QDBusArgument::beginMapEntry() const +{ + if (QDBusArgumentPrivate::checkReadAndDetach(d)) + d = d->demarshaller()->beginMapEntry(); +} + +/*! + Closes the D-Bus map entry and allow extracting of the next element + on the map. + + \sa beginMapEntry() +*/ +void QDBusArgument::endMapEntry() const +{ + if (QDBusArgumentPrivate::checkReadAndDetach(d)) + d = d->demarshaller()->endMapEntry(); +} + +/*! + Returns true if there are no more elements to be extracted from + this QDBusArgument. This function is usually used in QDBusArgument + objects returned from beginMap() and beginArray(). +*/ +bool QDBusArgument::atEnd() const +{ + if (QDBusArgumentPrivate::checkRead(d)) + return d->demarshaller()->atEnd(); + + return true; // at least, stop reading +} + +/*! + \since 4.5 + + Returns the current argument in the form of a QVariant. Basic + types will be decoded and returned in the QVariant, but for + complex types, this function will return a QDBusArgument object in + the QVariant. It is the caller's responsibility to decode the + argument (for example, by calling asVariant() in it). + + For example, if the current argument is an INT32, this function + will return a QVariant with an argument of type QVariant::Int. For + an array of INT32, it will return a QVariant containing a + QDBusArgument. + + If an error occurs or if there are no more arguments to decode + (i.e., we are at the end of the argument list), this function will + return an invalid QVariant. + + \sa atEnd() +*/ +QVariant QDBusArgument::asVariant() const +{ + if (QDBusArgumentPrivate::checkRead(d)) + return d->demarshaller()->toVariantInternal(); + + return QVariant(); +} + +QT_END_NAMESPACE + +// for optimization purposes, we include the marshallers here +#include "qdbusmarshaller.cpp" +#include "qdbusdemarshaller.cpp" + +QT_BEGIN_NAMESPACE + +// QDBusArgument operators + +const QDBusArgument &operator>>(const QDBusArgument &a, QVariant &v) +{ + QDBusVariant dbv; + a >> dbv; + v = dbv.variant(); + return a; +} + +// QVariant types +#ifndef QDBUS_NO_SPECIALTYPES +const QDBusArgument &operator>>(const QDBusArgument &a, QDate &date) +{ + int y, m, d; + a.beginStructure(); + a >> y >> m >> d; + a.endStructure(); + + if (y != 0 && m != 0 && d != 0) + date.setYMD(y, m, d); + else + date = QDate(); + return a; +} + +QDBusArgument &operator<<(QDBusArgument &a, const QDate &date) +{ + a.beginStructure(); + if (date.isValid()) + a << date.year() << date.month() << date.day(); + else + a << 0 << 0 << 0; + a.endStructure(); + return a; +} + +const QDBusArgument &operator>>(const QDBusArgument &a, QTime &time) +{ + int h, m, s, ms; + a.beginStructure(); + a >> h >> m >> s >> ms; + a.endStructure(); + + if (h < 0) + time = QTime(); + else + time.setHMS(h, m, s, ms); + return a; +} + +QDBusArgument &operator<<(QDBusArgument &a, const QTime &time) +{ + a.beginStructure(); + if (time.isValid()) + a << time.hour() << time.minute() << time.second() << time.msec(); + else + a << -1 << -1 << -1 << -1; + a.endStructure(); + return a; +} + +const QDBusArgument &operator>>(const QDBusArgument &a, QDateTime &dt) +{ + QDate date; + QTime time; + int timespec; + + a.beginStructure(); + a >> date >> time >> timespec; + a.endStructure(); + + dt = QDateTime(date, time, Qt::TimeSpec(timespec)); + return a; +} + +QDBusArgument &operator<<(QDBusArgument &a, const QDateTime &dt) +{ + a.beginStructure(); + a << dt.date() << dt.time() << int(dt.timeSpec()); + a.endStructure(); + return a; +} + +const QDBusArgument &operator>>(const QDBusArgument &a, QRect &rect) +{ + int x, y, width, height; + a.beginStructure(); + a >> x >> y >> width >> height; + a.endStructure(); + + rect.setRect(x, y, width, height); + return a; +} + +QDBusArgument &operator<<(QDBusArgument &a, const QRect &rect) +{ + a.beginStructure(); + a << rect.x() << rect.y() << rect.width() << rect.height(); + a.endStructure(); + + return a; +} + +const QDBusArgument &operator>>(const QDBusArgument &a, QRectF &rect) +{ + double x, y, width, height; + a.beginStructure(); + a >> x >> y >> width >> height; + a.endStructure(); + + rect.setRect(qreal(x), qreal(y), qreal(width), qreal(height)); + return a; +} + +QDBusArgument &operator<<(QDBusArgument &a, const QRectF &rect) +{ + a.beginStructure(); + a << double(rect.x()) << double(rect.y()) << double(rect.width()) << double(rect.height()); + a.endStructure(); + + return a; +} + +const QDBusArgument &operator>>(const QDBusArgument &a, QSize &size) +{ + a.beginStructure(); + a >> size.rwidth() >> size.rheight(); + a.endStructure(); + + return a; +} + +QDBusArgument &operator<<(QDBusArgument &a, const QSize &size) +{ + a.beginStructure(); + a << size.width() << size.height(); + a.endStructure(); + + return a; +} + +const QDBusArgument &operator>>(const QDBusArgument &a, QSizeF &size) +{ + double width, height; + a.beginStructure(); + a >> width >> height; + a.endStructure(); + + size.setWidth(qreal(width)); + size.setHeight(qreal(height)); + return a; +} + +QDBusArgument &operator<<(QDBusArgument &a, const QSizeF &size) +{ + a.beginStructure(); + a << double(size.width()) << double(size.height()); + a.endStructure(); + + return a; +} + +const QDBusArgument &operator>>(const QDBusArgument &a, QPoint &pt) +{ + a.beginStructure(); + a >> pt.rx() >> pt.ry(); + a.endStructure(); + + return a; +} + +QDBusArgument &operator<<(QDBusArgument &a, const QPoint &pt) +{ + a.beginStructure(); + a << pt.x() << pt.y(); + a.endStructure(); + + return a; +} + +const QDBusArgument &operator>>(const QDBusArgument &a, QPointF &pt) +{ + double x, y; + a.beginStructure(); + a >> x >> y; + a.endStructure(); + + pt.setX(qreal(x)); + pt.setY(qreal(y)); + return a; +} + +QDBusArgument &operator<<(QDBusArgument &a, const QPointF &pt) +{ + a.beginStructure(); + a << double(pt.x()) << double(pt.y()); + a.endStructure(); + + return a; +} + +const QDBusArgument &operator>>(const QDBusArgument &a, QLine &line) +{ + QPoint p1, p2; + a.beginStructure(); + a >> p1 >> p2; + a.endStructure(); + + line = QLine(p1, p2); + return a; +} + +QDBusArgument &operator<<(QDBusArgument &a, const QLine &line) +{ + a.beginStructure(); + a << line.p1() << line.p2(); + a.endStructure(); + + return a; +} + +const QDBusArgument &operator>>(const QDBusArgument &a, QLineF &line) +{ + QPointF p1, p2; + a.beginStructure(); + a >> p1 >> p2; + a.endStructure(); + + line = QLineF(p1, p2); + return a; +} + +QDBusArgument &operator<<(QDBusArgument &a, const QLineF &line) +{ + a.beginStructure(); + a << line.p1() << line.p2(); + a.endStructure(); + + return a; +} +#endif + +QT_END_NAMESPACE diff --git a/src/dbus/qdbusargument.h b/src/dbus/qdbusargument.h new file mode 100644 index 0000000..6491f0f --- /dev/null +++ b/src/dbus/qdbusargument.h @@ -0,0 +1,383 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDBUSARGUMENT_H +#define QDBUSARGUMENT_H + +#include <QtCore/qbytearray.h> +#include <QtCore/qhash.h> +#include <QtCore/qglobal.h> +#include <QtCore/qlist.h> +#include <QtCore/qmap.h> +#include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qvariant.h> +#include <QtDBus/qdbusextratypes.h> +#include <QtDBus/qdbusmacros.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(DBus) + +class QDBusArgumentPrivate; +class QDBusDemarshaller; +class QDBusMarshaller; +class QDBUS_EXPORT QDBusArgument +{ +public: + enum ElementType { + BasicType, + VariantType, + ArrayType, + StructureType, + MapType, + MapEntryType, + UnknownType = -1 + }; + + QDBusArgument(); + QDBusArgument(const QDBusArgument &other); + QDBusArgument &operator=(const QDBusArgument &other); + ~QDBusArgument(); + + // used for marshalling (Qt -> D-BUS) + QDBusArgument &operator<<(uchar arg); + QDBusArgument &operator<<(bool arg); + QDBusArgument &operator<<(short arg); + QDBusArgument &operator<<(ushort arg); + QDBusArgument &operator<<(int arg); + QDBusArgument &operator<<(uint arg); + QDBusArgument &operator<<(qlonglong arg); + QDBusArgument &operator<<(qulonglong arg); + QDBusArgument &operator<<(double arg); + QDBusArgument &operator<<(const QString &arg); + QDBusArgument &operator<<(const QDBusVariant &arg); + QDBusArgument &operator<<(const QDBusObjectPath &arg); + QDBusArgument &operator<<(const QDBusSignature &arg); + QDBusArgument &operator<<(const QStringList &arg); + QDBusArgument &operator<<(const QByteArray &arg); + + void beginStructure(); + void endStructure(); + void beginArray(int elementMetaTypeId); + void endArray(); + void beginMap(int keyMetaTypeId, int valueMetaTypeId); + void endMap(); + void beginMapEntry(); + void endMapEntry(); + + void appendVariant(const QVariant &v); + + // used for de-marshalling (D-BUS -> Qt) + QString currentSignature() const; + ElementType currentType() const; + + const QDBusArgument &operator>>(uchar &arg) const; + const QDBusArgument &operator>>(bool &arg) const; + const QDBusArgument &operator>>(short &arg) const; + const QDBusArgument &operator>>(ushort &arg) const; + const QDBusArgument &operator>>(int &arg) const; + const QDBusArgument &operator>>(uint &arg) const; + const QDBusArgument &operator>>(qlonglong &arg) const; + const QDBusArgument &operator>>(qulonglong &arg) const; + const QDBusArgument &operator>>(double &arg) const; + const QDBusArgument &operator>>(QString &arg) const; + const QDBusArgument &operator>>(QDBusVariant &arg) const; + const QDBusArgument &operator>>(QDBusObjectPath &arg) const; + const QDBusArgument &operator>>(QDBusSignature &arg) const; + const QDBusArgument &operator>>(QStringList &arg) const; + const QDBusArgument &operator>>(QByteArray &arg) const; + + void beginStructure() const; + void endStructure() const; + void beginArray() const; + void endArray() const; + void beginMap() const; + void endMap() const; + void beginMapEntry() const; + void endMapEntry() const; + bool atEnd() const; + + QVariant asVariant() const; + +protected: + QDBusArgument(QDBusArgumentPrivate *d); + friend class QDBusArgumentPrivate; + mutable QDBusArgumentPrivate *d; +}; + +template<typename T> inline T qdbus_cast(const QDBusArgument &arg +#ifndef Q_QDOC +, T * = 0 +#endif + ) +{ + T item; + arg >> item; + return item; +} + +template<typename T> inline T qdbus_cast(const QVariant &v +#ifndef Q_QDOC +, T * = 0 +#endif + ) +{ + int id = v.userType(); + if (id == qMetaTypeId<QDBusArgument>()) + return qdbus_cast<T>(qvariant_cast<QDBusArgument>(v)); + else + return qvariant_cast<T>(v); +} + +// specialise for QVariant, allowing it to be used in place of QDBusVariant +template<> inline QVariant qdbus_cast<QVariant>(const QDBusArgument &arg, QVariant *) +{ + QDBusVariant item; + arg >> item; + return item.variant(); +} +template<> inline QVariant qdbus_cast<QVariant>(const QVariant &v, QVariant *) +{ + return qdbus_cast<QDBusVariant>(v).variant(); +} + +QDBUS_EXPORT const QDBusArgument &operator>>(const QDBusArgument &a, QVariant &v); + +// QVariant types +#ifndef QDBUS_NO_SPECIALTYPES + +QDBUS_EXPORT const QDBusArgument &operator>>(const QDBusArgument &a, QDate &date); +QDBUS_EXPORT QDBusArgument &operator<<(QDBusArgument &a, const QDate &date); + +QDBUS_EXPORT const QDBusArgument &operator>>(const QDBusArgument &a, QTime &time); +QDBUS_EXPORT QDBusArgument &operator<<(QDBusArgument &a, const QTime &time); + +QDBUS_EXPORT const QDBusArgument &operator>>(const QDBusArgument &a, QDateTime &dt); +QDBUS_EXPORT QDBusArgument &operator<<(QDBusArgument &a, const QDateTime &dt); + +QDBUS_EXPORT const QDBusArgument &operator>>(const QDBusArgument &a, QRect &rect); +QDBUS_EXPORT QDBusArgument &operator<<(QDBusArgument &a, const QRect &rect); + +QDBUS_EXPORT const QDBusArgument &operator>>(const QDBusArgument &a, QRectF &rect); +QDBUS_EXPORT QDBusArgument &operator<<(QDBusArgument &a, const QRectF &rect); + +QDBUS_EXPORT const QDBusArgument &operator>>(const QDBusArgument &a, QSize &size); +QDBUS_EXPORT QDBusArgument &operator<<(QDBusArgument &a, const QSize &size); + +QDBUS_EXPORT const QDBusArgument &operator>>(const QDBusArgument &a, QSizeF &size); +QDBUS_EXPORT QDBusArgument &operator<<(QDBusArgument &a, const QSizeF &size); + +QDBUS_EXPORT const QDBusArgument &operator>>(const QDBusArgument &a, QPoint &pt); +QDBUS_EXPORT QDBusArgument &operator<<(QDBusArgument &a, const QPoint &pt); + +QDBUS_EXPORT const QDBusArgument &operator>>(const QDBusArgument &a, QPointF &pt); +QDBUS_EXPORT QDBusArgument &operator<<(QDBusArgument &a, const QPointF &pt); + +QDBUS_EXPORT const QDBusArgument &operator>>(const QDBusArgument &a, QLine &line); +QDBUS_EXPORT QDBusArgument &operator<<(QDBusArgument &a, const QLine &line); + +QDBUS_EXPORT const QDBusArgument &operator>>(const QDBusArgument &a, QLineF &line); +QDBUS_EXPORT QDBusArgument &operator<<(QDBusArgument &a, const QLineF &line); +#endif + +template<template <typename> class Container, typename T> +inline QDBusArgument &operator<<(QDBusArgument &arg, const Container<T> &list) +{ + int id = qMetaTypeId<T>(); + arg.beginArray(id); + typename Container<T>::const_iterator it = list.begin(); + typename Container<T>::const_iterator end = list.end(); + for ( ; it != end; ++it) + arg << *it; + arg.endArray(); + return arg; +} + +template<template <typename> class Container, typename T> +inline const QDBusArgument &operator>>(const QDBusArgument &arg, Container<T> &list) +{ + arg.beginArray(); + list.clear(); + while (!arg.atEnd()) { + T item; + arg >> item; + list.push_back(item); + } + + arg.endArray(); + return arg; +} + +// QList specializations +template<typename T> +inline QDBusArgument &operator<<(QDBusArgument &arg, const QList<T> &list) +{ + int id = qMetaTypeId<T>(); + arg.beginArray(id); + typename QList<T>::ConstIterator it = list.constBegin(); + typename QList<T>::ConstIterator end = list.constEnd(); + for ( ; it != end; ++it) + arg << *it; + arg.endArray(); + return arg; +} + +template<typename T> +inline const QDBusArgument &operator>>(const QDBusArgument &arg, QList<T> &list) +{ + arg.beginArray(); + list.clear(); + while (!arg.atEnd()) { + T item; + arg >> item; + list.push_back(item); + } + arg.endArray(); + + return arg; +} + +inline QDBusArgument &operator<<(QDBusArgument &arg, const QVariantList &list) +{ + int id = qMetaTypeId<QDBusVariant>(); + arg.beginArray(id); + QVariantList::ConstIterator it = list.constBegin(); + QVariantList::ConstIterator end = list.constEnd(); + for ( ; it != end; ++it) + arg << QDBusVariant(*it); + arg.endArray(); + return arg; +} + +// QMap specializations +template<typename Key, typename T> +inline QDBusArgument &operator<<(QDBusArgument &arg, const QMap<Key, T> &map) +{ + int kid = qMetaTypeId<Key>(); + int vid = qMetaTypeId<T>(); + arg.beginMap(kid, vid); + typename QMap<Key, T>::ConstIterator it = map.constBegin(); + typename QMap<Key, T>::ConstIterator end = map.constEnd(); + for ( ; it != end; ++it) { + arg.beginMapEntry(); + arg << it.key() << it.value(); + arg.endMapEntry(); + } + arg.endMap(); + return arg; +} + +template<typename Key, typename T> +inline const QDBusArgument &operator>>(const QDBusArgument &arg, QMap<Key, T> &map) +{ + arg.beginMap(); + map.clear(); + while (!arg.atEnd()) { + Key key; + T value; + arg.beginMapEntry(); + arg >> key >> value; + map.insertMulti(key, value); + arg.endMapEntry(); + } + arg.endMap(); + return arg; +} + +inline QDBusArgument &operator<<(QDBusArgument &arg, const QVariantMap &map) +{ + arg.beginMap(QVariant::String, qMetaTypeId<QDBusVariant>()); + QVariantMap::ConstIterator it = map.constBegin(); + QVariantMap::ConstIterator end = map.constEnd(); + for ( ; it != end; ++it) { + arg.beginMapEntry(); + arg << it.key() << QDBusVariant(it.value()); + arg.endMapEntry(); + } + arg.endMap(); + return arg; +} + +// QHash specializations +template<typename Key, typename T> +inline QDBusArgument &operator<<(QDBusArgument &arg, const QHash<Key, T> &map) +{ + int kid = qMetaTypeId<Key>(); + int vid = qMetaTypeId<T>(); + arg.beginMap(kid, vid); + typename QHash<Key, T>::ConstIterator it = map.constBegin(); + typename QHash<Key, T>::ConstIterator end = map.constEnd(); + for ( ; it != end; ++it) { + arg.beginMapEntry(); + arg << it.key() << it.value(); + arg.endMapEntry(); + } + arg.endMap(); + return arg; +} + +template<typename Key, typename T> +inline const QDBusArgument &operator>>(const QDBusArgument &arg, QHash<Key, T> &map) +{ + arg.beginMap(); + map.clear(); + while (!arg.atEnd()) { + Key key; + T value; + arg.beginMapEntry(); + arg >> key >> value; + map.insertMulti(key, value); + arg.endMapEntry(); + } + arg.endMap(); + return arg; +} + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QDBusArgument) + +QT_END_HEADER + +#endif diff --git a/src/dbus/qdbusargument_p.h b/src/dbus/qdbusargument_p.h new file mode 100644 index 0000000..757f345 --- /dev/null +++ b/src/dbus/qdbusargument_p.h @@ -0,0 +1,208 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDBUSARGUMENT_P_H +#define QDBUSARGUMENT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include <qdbusargument.h> +#include <qdbus_symbols_p.h> + +QT_BEGIN_NAMESPACE + +class QDBusMarshaller; +class QDBusDemarshaller; +class QDBusArgumentPrivate +{ +public: + inline QDBusArgumentPrivate() + : message(0), ref(1) + { } + ~QDBusArgumentPrivate(); + + static bool checkRead(QDBusArgumentPrivate *d); + static bool checkReadAndDetach(QDBusArgumentPrivate *&d); + static bool checkWrite(QDBusArgumentPrivate *&d); + + QDBusMarshaller *marshaller(); + QDBusDemarshaller *demarshaller(); + + static QByteArray createSignature(int id); + static inline QDBusArgument create(QDBusArgumentPrivate *d) + { + QDBusArgument q(d); + return q; + } + static inline QDBusArgumentPrivate *d(QDBusArgument &q) + { return q.d; } + +public: + DBusMessage *message; + QAtomicInt ref; + enum Direction { + Marshalling, + Demarshalling + } direction; +}; + +class QDBusMarshaller: public QDBusArgumentPrivate +{ +public: + QDBusMarshaller() : parent(0), ba(0), closeCode(0), ok(true) + { direction = Marshalling; } + ~QDBusMarshaller(); + + QString currentSignature(); + + void append(uchar arg); + void append(bool arg); + void append(short arg); + void append(ushort arg); + void append(int arg); + void append(uint arg); + void append(qlonglong arg); + void append(qulonglong arg); + void append(double arg); + void append(const QString &arg); + void append(const QDBusObjectPath &arg); + void append(const QDBusSignature &arg); + void append(const QStringList &arg); + void append(const QByteArray &arg); + bool append(const QDBusVariant &arg); // this one can fail + + QDBusMarshaller *beginStructure(); + QDBusMarshaller *endStructure(); + QDBusMarshaller *beginArray(int id); + QDBusMarshaller *endArray(); + QDBusMarshaller *beginMap(int kid, int vid); + QDBusMarshaller *endMap(); + QDBusMarshaller *beginMapEntry(); + QDBusMarshaller *endMapEntry(); + QDBusMarshaller *beginCommon(int code, const char *signature); + QDBusMarshaller *endCommon(); + void open(QDBusMarshaller &sub, int code, const char *signature); + void close(); + void error(); + + bool appendVariantInternal(const QVariant &arg); + bool appendRegisteredType(const QVariant &arg); + bool appendCrossMarshalling(QDBusDemarshaller *arg); + +public: + DBusMessageIter iterator; + QDBusMarshaller *parent; + QByteArray *ba; + char closeCode; + bool ok; + +private: + Q_DISABLE_COPY(QDBusMarshaller) +}; + +class QDBusDemarshaller: public QDBusArgumentPrivate +{ +public: + inline QDBusDemarshaller() : parent(0) { direction = Demarshalling; } + ~QDBusDemarshaller(); + + QString currentSignature(); + + uchar toByte(); + bool toBool(); + ushort toUShort(); + short toShort(); + int toInt(); + uint toUInt(); + qlonglong toLongLong(); + qulonglong toULongLong(); + double toDouble(); + QString toString(); + QDBusObjectPath toObjectPath(); + QDBusSignature toSignature(); + QDBusVariant toVariant(); + QStringList toStringList(); + QByteArray toByteArray(); + + QDBusDemarshaller *beginStructure(); + QDBusDemarshaller *endStructure(); + QDBusDemarshaller *beginArray(); + QDBusDemarshaller *endArray(); + QDBusDemarshaller *beginMap(); + QDBusDemarshaller *endMap(); + QDBusDemarshaller *beginMapEntry(); + QDBusDemarshaller *endMapEntry(); + QDBusDemarshaller *beginCommon(); + QDBusDemarshaller *endCommon(); + QDBusArgument duplicate(); + inline void close() { } + + bool atEnd(); + + QVariant toVariantInternal(); + QDBusArgument::ElementType currentType(); + +public: + DBusMessageIter iterator; + QDBusDemarshaller *parent; + +private: + Q_DISABLE_COPY(QDBusDemarshaller) +}; + +inline QDBusMarshaller *QDBusArgumentPrivate::marshaller() +{ return static_cast<QDBusMarshaller *>(this); } + +inline QDBusDemarshaller *QDBusArgumentPrivate::demarshaller() +{ return static_cast<QDBusDemarshaller *>(this); } + +QT_END_NAMESPACE + +#endif diff --git a/src/dbus/qdbusconnection.cpp b/src/dbus/qdbusconnection.cpp new file mode 100644 index 0000000..04f4976 --- /dev/null +++ b/src/dbus/qdbusconnection.cpp @@ -0,0 +1,1045 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qdebug.h> +#include <qcoreapplication.h> +#include <qstringlist.h> + +#include "qdbusconnection.h" +#include "qdbusconnectioninterface.h" +#include "qdbuserror.h" +#include "qdbusmessage.h" +#include "qdbusmessage_p.h" +#include "qdbusconnection_p.h" +#include "qdbusinterface_p.h" +#include "qdbusutil_p.h" + +#include "qdbusthreaddebug_p.h" + +QT_BEGIN_NAMESPACE + +class QDBusConnectionManager +{ +public: + QDBusConnectionManager() {} + ~QDBusConnectionManager(); + + QDBusConnectionPrivate *connection(const QString &name) const; + void removeConnection(const QString &name); + void setConnection(const QString &name, QDBusConnectionPrivate *c); + + QDBusConnectionPrivate *sender() const; + void setSender(const QDBusConnectionPrivate *s); + + mutable QMutex mutex; +private: + QHash<QString, QDBusConnectionPrivate *> connectionHash; + + mutable QMutex senderMutex; + QString senderName; // internal; will probably change +}; + +Q_GLOBAL_STATIC(QDBusConnectionManager, _q_manager) + +QDBusConnectionPrivate *QDBusConnectionManager::sender() const +{ + QMutexLocker locker(&senderMutex); + return connection(senderName); +} + +void QDBusConnectionManager::setSender(const QDBusConnectionPrivate *s) +{ + QMutexLocker locker(&senderMutex); + senderName = (s ? s->name : QString()); +} + +QDBusConnectionPrivate *QDBusConnectionManager::connection(const QString &name) const +{ + return connectionHash.value(name, 0); +} + +void QDBusConnectionManager::removeConnection(const QString &name) +{ + QDBusConnectionPrivate *d = 0; + d = connectionHash.take(name); + if (d && !d->ref.deref()) + d->deleteYourself(); + + // Static objects may be keeping the connection open. + // However, it is harmless to have outstanding references to a connection that is + // closing as long as those references will be soon dropped without being used. + + // ### Output a warning if connections are being used after they have been removed. +} + +QDBusConnectionManager::~QDBusConnectionManager() +{ + for (QHash<QString, QDBusConnectionPrivate *>::const_iterator it = connectionHash.constBegin(); + it != connectionHash.constEnd(); ++it) { + QDBusConnectionPrivate *d = it.value(); + if (!d->ref.deref()) + d->deleteYourself(); + else + d->closeConnection(); + } + connectionHash.clear(); +} + +QDBUS_EXPORT void qDBusBindToApplication(); +void qDBusBindToApplication() +{ +} + +void QDBusConnectionManager::setConnection(const QString &name, QDBusConnectionPrivate *c) +{ + connectionHash[name] = c; + c->name = name; +} + +/*! + \fn QDBusConnection &QDBusConnection::sessionBus() + \relates QDBusConnection + + Returns a QDBusConnection object opened with the session bus. The object reference returned + by this function is valid until the QCoreApplication's destructor is run, when the + connection will be closed and the object, deleted. +*/ +/*! + \fn QDBusConnection &QDBusConnection::systemBus() + \relates QDBusConnection + + Returns a QDBusConnection object opened with the system bus. The object reference returned + by this function is valid until the QCoreApplication's destructor is run, when the + connection will be closed and the object, deleted. +*/ + +/*! + \class QDBusConnection + \inmodule QtDBus + \since 4.2 + + \brief The QDBusConnection class represents a connection to the D-Bus bus daemon. + + This class is the initial point in a D-Bus session. Using it, you + can get access to remote objects, interfaces; connect remote + signals to your object's slots; register objects, etc. + + D-Bus connections are created using the connectToBus() function, + which opens a connection to the server daemon and does the initial + handshaking, associating that connection with a name. Further + attempts to connect using the same name will return the same + connection. + + The connection is then torn down using the disconnectFromBus() + function. + + As a convenience for the two most common connection types, the + sessionBus() and systemBus() functions return open connections to + the session server daemon and the system server daemon, + respectively. Those connections are opened when first used and are + closed when the QCoreApplication destructor is run. + + D-Bus also supports peer-to-peer connections, without the need for + a bus server daemon. Using this facility, two applications can + talk to each other and exchange messages. This can be achieved by + passing an address to connectToBus() function, which was opened by + another D-Bus application using QDBusServer. +*/ + +/*! + \enum QDBusConnection::BusType + Specifies the type of the bus connection. The valid bus types are: + + \value SessionBus the session bus, associated with the running desktop session + \value SystemBus the system bus, used to communicate with system-wide processes + \value ActivationBus the activation bus, the "alias" for the bus that started the + service + + On the Session Bus, one can find other applications by the same user that are sharing the same + desktop session (hence the name). On the System Bus, however, processes shared for the whole + system are usually found. +*/ + +/*! + \enum QDBusConnection::RegisterOption + Specifies the options for registering objects with the connection. The possible values are: + + \value ExportAdaptors export the contents of adaptors found in this object + + \value ExportScriptableSlots export this object's scriptable slots + \value ExportScriptableSignals export this object's scriptable signals + \value ExportScriptableProperties export this object's scriptable properties + \value ExportScriptableContents shorthand form for ExportScriptableSlots | + ExportScriptableSignals | + ExportScriptableProperties + + \value ExportNonScriptableSlots export this object's non-scriptable slots + \value ExportNonScriptableSignals export this object's non-scriptable signals + \value ExportNonScriptableProperties export this object's non-scriptable properties + \value ExportNonScriptableContents shorthand form for ExportNonScriptableSlots | + ExportNonScriptableSignals | + ExportNonScriptableProperties + + \value ExportAllSlots export all of this object's slots + \value ExportAllSignals export all of this object's signals + \value ExportAllProperties export all of this object's properties + \value ExportAllContents export all of this object's contents + + \value ExportChildObjects export this object's child objects + + \sa registerObject(), QDBusAbstractAdaptor, {usingadaptors.html}{Using adaptors} +*/ + +/*! + \enum QDBusConnection::UnregisterMode + The mode for unregistering an object path: + + \value UnregisterNode unregister this node only: do not unregister child objects + \value UnregisterTree unregister this node and all its sub-tree + + Note, however, if this object was registered with the ExportChildObjects option, UnregisterNode + will unregister the child objects too. +*/ + +/*! + Creates a QDBusConnection object attached to the connection with name \a name. + + This does not open the connection. You have to call connectToBus() to open it. +*/ +QDBusConnection::QDBusConnection(const QString &name) +{ + if (name.isEmpty()) { + d = 0; + } else { + QMutexLocker locker(&_q_manager()->mutex); + d = _q_manager()->connection(name); + if (d) + d->ref.ref(); + } +} + +/*! + Creates a copy of the \a other connection. +*/ +QDBusConnection::QDBusConnection(const QDBusConnection &other) +{ + d = other.d; + if (d) + d->ref.ref(); +} + +/*! + \internal + Creates a connection object with the given \a dd as private object. +*/ +QDBusConnection::QDBusConnection(QDBusConnectionPrivate *dd) +{ + d = dd; + if (d) + d->ref.ref(); +} + +/*! + Disposes of this object. This does not close the connection: you + have to call disconnectFromBus() to do that. +*/ +QDBusConnection::~QDBusConnection() +{ + if (d && !d->ref.deref()) + d->deleteYourself(); +} + +/*! + Creates a copy of the connection \a other in this object. Note + that the connection this object referenced before the copy, is not + spontaneously disconnected. + + \sa disconnectFromBus() +*/ +QDBusConnection &QDBusConnection::operator=(const QDBusConnection &other) +{ + if (other.d) + other.d->ref.ref(); + if (d && !d->ref.deref()) + d->deleteYourself(); + d = other.d; + return *this; +} + +/*! + Opens a connection of type \a type to one of the known busses and + associate with it the connection name \a name. Returns a + QDBusConnection object associated with that connection. +*/ +QDBusConnection QDBusConnection::connectToBus(BusType type, const QString &name) +{ +// Q_ASSERT_X(QCoreApplication::instance(), "QDBusConnection::addConnection", +// "Cannot create connection without a Q[Core]Application instance"); + if (!qdbus_loadLibDBus()) { + QDBusConnectionPrivate *d = 0; + return QDBusConnection(d); + } + + QMutexLocker locker(&_q_manager()->mutex); + + QDBusConnectionPrivate *d = _q_manager()->connection(name); + if (d || name.isEmpty()) + return QDBusConnection(d); + + d = new QDBusConnectionPrivate; + DBusConnection *c = 0; + QDBusErrorInternal error; + switch (type) { + case SystemBus: + c = q_dbus_bus_get_private(DBUS_BUS_SYSTEM, error); + break; + case SessionBus: + c = q_dbus_bus_get_private(DBUS_BUS_SESSION, error); + break; + case ActivationBus: + c = q_dbus_bus_get_private(DBUS_BUS_STARTER, error); + break; + } + d->setConnection(c, error); //setConnection does the error handling for us + + _q_manager()->setConnection(name, d); + + QDBusConnection retval(d); + + // create the bus service + // will lock in QDBusConnectionPrivate::connectRelay() + d->setBusService(retval); + + return retval; +} + +/*! + Opens a peer-to-peer connection on address \a address and associate with it the + connection name \a name. Returns a QDBusConnection object associated with that connection. +*/ +QDBusConnection QDBusConnection::connectToBus(const QString &address, + const QString &name) +{ +// Q_ASSERT_X(QCoreApplication::instance(), "QDBusConnection::addConnection", +// "Cannot create connection without a Q[Core]Application instance"); + if (!qdbus_loadLibDBus()){ + QDBusConnectionPrivate *d = 0; + return QDBusConnection(d); + } + + QMutexLocker locker(&_q_manager()->mutex); + + QDBusConnectionPrivate *d = _q_manager()->connection(name); + if (d || name.isEmpty()) + return QDBusConnection(d); + + d = new QDBusConnectionPrivate; + // setConnection does the error handling for us + QDBusErrorInternal error; + DBusConnection *c = q_dbus_connection_open_private(address.toUtf8().constData(), error); + if (c) { + if (!q_dbus_bus_register(c, error)) { + q_dbus_connection_unref(c); + c = 0; + } + } + d->setConnection(c, error); + _q_manager()->setConnection(name, d); + + QDBusConnection retval(d); + + // create the bus service + // will lock in QDBusConnectionPrivate::connectRelay() + d->setBusService(retval); + + return retval; +} + +/*! + Closes the connection of name \a name. + + Note that if there are still QDBusConnection objects associated + with the same connection, the connection will not be closed until + all references are dropped. However, no further references can be + created using the QDBusConnection constructor. +*/ +void QDBusConnection::disconnectFromBus(const QString &name) +{ + if (_q_manager()) { + QMutexLocker locker(&_q_manager()->mutex); + _q_manager()->removeConnection(name); + } +} + +/*! + Sends the \a message over this connection, 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, false otherwise. +*/ +bool QDBusConnection::send(const QDBusMessage &message) const +{ + if (!d || !d->connection) { + QDBusError err = QDBusError(QDBusError::Disconnected, + QLatin1String("Not connected to D-BUS server")); + if (d) + d->lastError = err; + return false; + } + return d->send(message) != 0; +} + +/*! + Sends the \a message over this connection and returns immediately. + When the reply is received, the method \a returnMethod is called in + the \a receiver object. If an error occurs, the method \a errorMethod + will be called instead. + + If no reply is received within \a timeout milliseconds, an automatic + error will be delivered indicating the expiration of the call. + The default \a timeout is -1, which will be replaced with an + implementation-defined value that is suitable for inter-process + communications (generally, 25 seconds). + + This function is suitable for method calls only. It is guaranteed + that the slot will be called exactly once with the reply, as long + as the parameter types match and no error occurs. + + Returns true if the message was sent, or false if the message could + not be sent. +*/ +bool QDBusConnection::callWithCallback(const QDBusMessage &message, QObject *receiver, + const char *returnMethod, const char *errorMethod, + int timeout) const +{ + if (!d || !d->connection) { + QDBusError err = QDBusError(QDBusError::Disconnected, + QLatin1String("Not connected to D-BUS server")); + if (d) + d->lastError = err; + return false; + } + return d->sendWithReplyAsync(message, receiver, returnMethod, errorMethod, timeout) != 0; +} + +/*! + \overload + \deprecated + Sends the \a message over this connection and returns immediately. + When the reply is received, the method \a returnMethod is called in + the \a receiver object. + + This function is suitable for method calls only. It is guaranteed + that the slot will be called exactly once with the reply, as long + as the parameter types match and no error occurs. + + This function is dangerous because it cannot report errors, including + the expiration of the timeout. + + Returns true if the message was sent, or false if the message could + not be sent. +*/ +bool QDBusConnection::callWithCallback(const QDBusMessage &message, QObject *receiver, + const char *returnMethod, int timeout) const +{ + return callWithCallback(message, receiver, returnMethod, 0, timeout); +} + +/*! + Sends the \a message over this connection and blocks, waiting for + a reply, for at most \a timeout milliseconds. This function is + suitable for method calls only. It returns the reply message as + its return value, which will be either of type + QDBusMessage::ReplyMessage or QDBusMessage::ErrorMessage. + + See the QDBusInterface::call() function for a more friendly way + of placing calls. + + \warning If \a mode is QDBus::BlockWithGui, this function will + reenter the Qt event loop in order to wait for the + reply. During the wait, it may deliver signals and other + method calls to your application. Therefore, it must be + prepared to handle a reentrancy whenever a call is + placed with call(). +*/ +QDBusMessage QDBusConnection::call(const QDBusMessage &message, QDBus::CallMode mode, int timeout) const +{ + if (!d || !d->connection) { + QDBusError err = QDBusError(QDBusError::Disconnected, + QLatin1String("Not connected to D-Bus server")); + if (d) + d->lastError = err; + + return QDBusMessage::createError(err); + } + + if (mode != QDBus::NoBlock) + return d->sendWithReply(message, mode, timeout); + + d->send(message); + QDBusMessage retval; + retval << QVariant(); // add one argument (to avoid .at(0) problems) + return retval; +} + +/*! + \since 4.5 + Sends the \a message over this connection and returns + immediately. This function is suitable for method calls only. It + returns an object of type QDBusPendingCall which can be used to + track the status of the reply. The \a timeout parameter is used to + determine when an auto-generated error reply may be emitted and is + also the upper limit for waiting in QDBusPendingCall::waitForFinished(). + + See the QDBusInterface::asyncCall() function for a more friendly way + of placing calls. +*/ +QDBusPendingCall QDBusConnection::asyncCall(const QDBusMessage &message, int timeout) const +{ + if (!d || !d->connection) { + return QDBusPendingCall(0); // null pointer -> disconnected + } + + QDBusPendingCallPrivate *priv = d->sendWithReplyAsync(message, timeout); + return QDBusPendingCall(priv); +} + +/*! + Connects the signal specified by the \a service, \a path, \a interface and \a name parameters to + the slot \a slot in object \a receiver. The arguments \a service and \a path can be empty, + denoting a connection to any signal of the (\a interface, \a name) pair, from any remote + application. + + Returns true if the connection was successful. + + \warning The signal will only be delivered to the slot if the parameters match. This verification + can be done only when the signal is received, not at connection time. +*/ +bool QDBusConnection::connect(const QString &service, const QString &path, const QString& interface, + const QString &name, QObject *receiver, const char *slot) +{ + return connect(service, path, interface, name, QString(), receiver, slot); +} + +/*! + Disconnects the signal specified by the \a service, \a path, \a interface and \a name parameters from + the slot \a slot in object \a receiver. The arguments \a service and \a path can be empty, + denoting a disconnection from all signals of the (\a interface, \a name) pair, from all remote + applications. + + Returns true if the disconnection was successful. +*/ +bool QDBusConnection::disconnect(const QString &service, const QString &path, const QString &interface, + const QString &name, QObject *receiver, const char *slot) +{ + return disconnect(service, path, interface, name, QString(), receiver, slot); +} + +/*! + \overload + + Connects the signal to the slot \a slot in object \a + receiver. Unlike the other connect() overload, this function + allows one to specify the parameter signature to be connected + using the \a signature variable. The function will then verify + that this signature can be delivered to the slot specified by \a + slot and return false otherwise. + + \note This function verifies that the signal signature matches the + slot's parameters, but it does not verify that the actual + signal exists with the given signature in the remote + service. +*/ +bool QDBusConnection::connect(const QString &service, const QString &path, const QString& interface, + const QString &name, const QString &signature, + QObject *receiver, const char *slot) +{ + if (!receiver || !slot || !d || !d->connection) + return false; + if (!interface.isEmpty() && !QDBusUtil::isValidInterfaceName(interface)) + return false; + if (interface.isEmpty() && name.isEmpty()) + return false; + + // check the slot + QDBusConnectionPrivate::SignalHook hook; + QString key; + QString name2 = name; + if (name2.isNull()) + name2.detach(); + + QString owner = d->getNameOwner(service); // we don't care if the owner is empty + hook.signature = signature; // it might get started later + if (!d->prepareHook(hook, key, service, owner, path, interface, name, receiver, slot, 0, false)) + return false; // don't connect + + // avoid duplicating: + QDBusWriteLocker locker(ConnectAction, d); + QDBusConnectionPrivate::SignalHookHash::ConstIterator it = d->signalHooks.find(key); + QDBusConnectionPrivate::SignalHookHash::ConstIterator end = d->signalHooks.constEnd(); + for ( ; it != end && it.key() == key; ++it) { + const QDBusConnectionPrivate::SignalHook &entry = it.value(); + if (entry.service == hook.service && + entry.owner == hook.owner && + entry.path == hook.path && + entry.signature == hook.signature && + entry.obj == hook.obj && + entry.midx == hook.midx) { + // no need to compare the parameters if it's the same slot + return true; // already there + } + } + + d->connectSignal(key, hook); + return true; +} + +/*! + \overload + + Disconnects the signal from the slot \a slot in object \a + receiver. Unlike the other disconnect() overload, this function + allows one to specify the parameter signature to be disconnected + using the \a signature variable. The function will then verify + that this signature is connected to the slot specified by \a slot + and return false otherwise. +*/ +bool QDBusConnection::disconnect(const QString &service, const QString &path, const QString& interface, + const QString &name, const QString &signature, + QObject *receiver, const char *slot) +{ + if (!receiver || !slot || !d || !d->connection) + return false; + if (!interface.isEmpty() && !QDBusUtil::isValidInterfaceName(interface)) + return false; + if (interface.isEmpty() && name.isEmpty()) + return false; + + // check the slot + QDBusConnectionPrivate::SignalHook hook; + QString key; + QString name2 = name; + if (name2.isNull()) + name2.detach(); + + QString owner = d->getNameOwner(service); // we don't care of owner is empty + hook.signature = signature; + if (!d->prepareHook(hook, key, service, owner, path, interface, name, receiver, slot, 0, false)) + return false; // don't disconnect + + // avoid duplicating: + QDBusWriteLocker locker(DisconnectAction, d); + QDBusConnectionPrivate::SignalHookHash::Iterator it = d->signalHooks.find(key); + QDBusConnectionPrivate::SignalHookHash::Iterator end = d->signalHooks.end(); + for ( ; it != end && it.key() == key; ++it) { + const QDBusConnectionPrivate::SignalHook &entry = it.value(); + if (entry.service == hook.service && + entry.owner == hook.owner && + entry.path == hook.path && + entry.signature == hook.signature && + entry.obj == hook.obj && + entry.midx == hook.midx) { + // no need to compare the parameters if it's the same slot + d->disconnectSignal(it); + return true; // it was there + } + } + + // the slot was not found + return false; +} + +/*! + Registers the object \a object at path \a path and returns true if + the registration was successful. The \a options parameter + specifies how much of the object \a object will be exposed through + D-Bus. + + This function does not replace existing objects: if there is already an object registered at + path \a path, this function will return false. Use unregisterObject() to unregister it first. + + You cannot register an object as a child object of an object that + was registered with QDBusConnection::ExportChildObjects. +*/ +bool QDBusConnection::registerObject(const QString &path, QObject *object, RegisterOptions options) +{ + Q_ASSERT_X(QDBusUtil::isValidObjectPath(path), "QDBusConnection::registerObject", + "Invalid object path given"); + if (!d || !d->connection || !object || !options || !QDBusUtil::isValidObjectPath(path)) + return false; + + QStringList pathComponents = path.split(QLatin1Char('/')); + if (pathComponents.last().isEmpty()) + pathComponents.removeLast(); + QDBusWriteLocker locker(RegisterObjectAction, d); + + // lower-bound search for where this object should enter in the tree + QDBusConnectionPrivate::ObjectTreeNode *node = &d->rootNode; + int i = 1; + while (node) { + if (pathComponents.count() == i) { + // this node exists + // consider it free if there's no object here and the user is not trying to + // replace the object sub-tree + if ((options & ExportChildObjects && !node->children.isEmpty()) || node->obj) + return false; + + // we can add the object here + node->obj = object; + node->flags = options; + + d->registerObject(node); + //qDebug("REGISTERED FOR %s", path.toLocal8Bit().constData()); + return true; + } + + // find the position where we'd insert the node + QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator it = + qLowerBound(node->children.begin(), node->children.end(), pathComponents.at(i)); + if (it != node->children.end() && it->name == pathComponents.at(i)) { + // match: this node exists + node = it; + + // are we allowed to go deeper? + if (node->flags & ExportChildObjects) { + // we're not + qDebug("Cannot register object at %s because %s exports its own child objects", + qPrintable(path), qPrintable(pathComponents.at(i))); + return false; + } + } else { + // add entry + node = node->children.insert(it, pathComponents.at(i)); + } + + // iterate + ++i; + } + + Q_ASSERT_X(false, "QDBusConnection::registerObject", "The impossible happened"); + return false; +} + +/*! + Unregisters an object that was registered with the registerObject() at the object path given by + \a path and, if \a mode is QDBusConnection::UnregisterTree, all of its sub-objects too. + + Note that you cannot unregister objects that were not registered with registerObject(). +*/ +void QDBusConnection::unregisterObject(const QString &path, UnregisterMode mode) +{ + if (!d || !d->connection || !QDBusUtil::isValidObjectPath(path)) + return; + + QStringList pathComponents = path.split(QLatin1Char('/')); + QDBusWriteLocker locker(UnregisterObjectAction, d); + QDBusConnectionPrivate::ObjectTreeNode *node = &d->rootNode; + int i = 1; + + // find the object + while (node) { + if (pathComponents.count() == i) { + // found it + node->obj = 0; + node->flags = 0; + + if (mode == UnregisterTree) { + // clear the sub-tree as well + node->children.clear(); // can't disconnect the objects because we really don't know if they can + // be found somewhere else in the path too + } + + return; + } + + QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator it = + qLowerBound(node->children.begin(), node->children.end(), pathComponents.at(i)); + if (it == node->children.end() || it->name != pathComponents.at(i)) + break; // node not found + + node = it; + ++i; + } +} + +/*! + Return the object that was registered with the registerObject() at the object path given by + \a path. +*/ +QObject *QDBusConnection::objectRegisteredAt(const QString &path) const +{ + Q_ASSERT_X(QDBusUtil::isValidObjectPath(path), "QDBusConnection::registeredObject", + "Invalid object path given"); + if (!d || !d->connection || !QDBusUtil::isValidObjectPath(path)) + return false; + + QStringList pathComponents = path.split(QLatin1Char('/')); + if (pathComponents.last().isEmpty()) + pathComponents.removeLast(); + + // lower-bound search for where this object should enter in the tree + QDBusReadLocker lock(ObjectRegisteredAtAction, d); + const QDBusConnectionPrivate::ObjectTreeNode *node = &d->rootNode; + + int i = 1; + while (node) { + if (pathComponents.count() == i) + return node->obj; + + QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator it = + qLowerBound(node->children.constBegin(), node->children.constEnd(), pathComponents.at(i)); + if (it == node->children.constEnd() || it->name != pathComponents.at(i)) + break; // node not found + + node = it; + ++i; + } + return 0; +} + +/*! + Returns a QDBusConnectionInterface object that represents the + D-Bus server interface on this connection. +*/ +QDBusConnectionInterface *QDBusConnection::interface() const +{ + if (!d) + return 0; + return d->busService; +} + +/*! + Returns true if this QDBusConnection object is connected. + + If it isn't connected, calling connectToBus() on the same + connection name will not make be connected. You need to call the + QDBusConnection constructor again. +*/ +bool QDBusConnection::isConnected() const +{ + return d && d->connection && q_dbus_connection_get_is_connected(d->connection); +} + +/*! + Returns the last error that happened in this connection. + + This function is provided for low-level code. If you're using + QDBusInterface::call(), error codes are reported by its return + value. + + \sa QDBusInterface, QDBusMessage +*/ +QDBusError QDBusConnection::lastError() const +{ + return d ? d->lastError : QDBusError(); +} + +/*! + Returns the unique connection name for this connection, if this QDBusConnection object is + connected, or an empty QString otherwise. + + A Unique Connection Name is a string in the form ":x.xxx" (where x + are decimal digits) that is assigned by the D-Bus server daemon + upon connection. It uniquely identifies this client in the bus. + + This function returns an empty QString for peer-to-peer connections. +*/ +QString QDBusConnection::baseService() const +{ + return d ? d->baseService : QString(); +} + +/*! + \since 4.5 + + Returns the connection name for this connection, as given as the + name parameter to connectToBus(). + + The connection name can be used to uniquely identify actual + underlying connections to buses. Copies made from a single + connection will always implicitly share the underlying connection, + and hence will have the same connection name. + + Inversely, two connections having different connection names will + always either be connected to different buses, or have a different + unique name (as returned by baseService()) on that bus. + + \sa connectToBus(), disconnectFromBus() +*/ +QString QDBusConnection::name() const +{ + return d ? d->name : QString(); +} + +/*! + Attempts to register the \a serviceName on the D-Bus server and + returns true if the registration succeded. The registration will + fail if the name is already registered by another application. + + \sa unregisterService(), QDBusConnectionInterface::registerService() +*/ +bool QDBusConnection::registerService(const QString &serviceName) +{ + if (interface() && interface()->registerService(serviceName)) { + if (d) d->registerService(serviceName); + return true; + } + return false; +} + +/*! + Unregisters the service \a serviceName that was previously + registered with registerService() and returns true if it + succeeded. + + \sa registerService(), QDBusConnectionInterface::unregisterService() +*/ +bool QDBusConnection::unregisterService(const QString &serviceName) +{ + if (interface()->unregisterService(serviceName)) { + if (d) d->unregisterService(serviceName); + return true; + } + return false; +} + +static const char _q_sessionBusName[] = "qt_default_session_bus"; +static const char _q_systemBusName[] = "qt_default_system_bus"; + +class QDBusDefaultConnection: public QDBusConnection +{ + const char *ownName; +public: + inline QDBusDefaultConnection(BusType type, const char *name) + : QDBusConnection(connectToBus(type, QString::fromLatin1(name))), ownName(name) + { } + + inline ~QDBusDefaultConnection() + { disconnectFromBus(QString::fromLatin1(ownName)); } +}; + +Q_GLOBAL_STATIC_WITH_ARGS(QDBusDefaultConnection, _q_sessionBus, + (QDBusConnection::SessionBus, _q_sessionBusName)) +Q_GLOBAL_STATIC_WITH_ARGS(QDBusDefaultConnection, _q_systemBus, + (QDBusConnection::SystemBus, _q_systemBusName)) + +QDBusConnection QDBusConnection::sessionBus() +{ + return *_q_sessionBus(); +} + +QDBusConnection QDBusConnection::systemBus() +{ + return *_q_systemBus(); +} + +/*! + Returns the connection that sent the signal, if called in a slot activated + by QDBus; otherwise it returns 0. +*/ +QDBusConnection QDBusConnection::sender() +{ + return QDBusConnection(_q_manager()->sender()); +} + +/*! + \internal +*/ +void QDBusConnectionPrivate::setSender(const QDBusConnectionPrivate *s) +{ + _q_manager()->setSender(s); +} + +/*! + \internal +*/ +void QDBusConnectionPrivate::setConnection(const QString &name, QDBusConnectionPrivate *c) +{ + _q_manager()->setConnection(name, c); +} + +/*! + \internal +*/ +void QDBusConnectionPrivate::setBusService(const QDBusConnection &connection) +{ + busService = new QDBusConnectionInterface(connection, this); + ref.deref(); // busService has increased the refcounting to us + // avoid cyclic refcounting +// if (mode != PeerMode) + QObject::connect(busService, SIGNAL(serviceOwnerChanged(QString,QString,QString)), + this, SIGNAL(serviceOwnerChanged(QString,QString,QString))); + + QObject::connect(this, SIGNAL(callWithCallbackFailed(QDBusError,QDBusMessage)), + busService, SIGNAL(callWithCallbackFailed(QDBusError,QDBusMessage)), + Qt::QueuedConnection); + +} + +/*! + \namespace QDBus + \inmodule QtDBus + + \brief The QDBus namespace contains miscellaneous identifiers used + throughout the QtDBus library. +*/ + +/*! + \enum QDBus::CallMode + + This enum describes the various ways of placing a function call. The valid modes are: + + \value NoBlock Place the call but don't wait for the reply (the reply's contents + will be discarded). + \value Block Don't use an event loop to wait for a reply, but instead block on + network operations while waiting. This means the + user-interface may not be updated until the function returns. + \value BlockWithGui Use the Qt event loop to wait for a reply. This means that the + user-interface will stay responsive (processing input events), + but it also means other events may happen, like signal delivery + and other D-Bus method calls. + \value AutoDetect Automatically detect if the called function has a reply. + + When using BlockWithGui, applications must be prepared for reentrancy in any function. +*/ + +QT_END_NAMESPACE diff --git a/src/dbus/qdbusconnection.h b/src/dbus/qdbusconnection.h new file mode 100644 index 0000000..862cad4 --- /dev/null +++ b/src/dbus/qdbusconnection.h @@ -0,0 +1,178 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDBUSCONNECTION_H +#define QDBUSCONNECTION_H + +#include <QtDBus/qdbusmacros.h> +#include <QtCore/qstring.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(DBus) + +namespace QDBus +{ + enum CallMode { + NoBlock, + Block, + BlockWithGui, + AutoDetect + }; +} + +class QDBusAbstractInterfacePrivate; +class QDBusInterface; +class QDBusError; +class QDBusMessage; +class QDBusPendingCall; +class QDBusConnectionInterface; +class QObject; + +class QDBusConnectionPrivate; +class QDBUS_EXPORT QDBusConnection +{ + Q_GADGET + Q_ENUMS(BusType UnregisterMode) + Q_FLAGS(RegisterOptions) +public: + enum BusType { SessionBus, SystemBus, ActivationBus }; + enum RegisterOption { + ExportAdaptors = 0x01, + + ExportScriptableSlots = 0x10, + ExportScriptableSignals = 0x20, + ExportScriptableProperties = 0x40, + ExportScriptableContents = 0xf0, + + ExportNonScriptableSlots = 0x100, + ExportNonScriptableSignals = 0x200, + ExportNonScriptableProperties = 0x400, + ExportNonScriptableContents = 0xf00, + + ExportAllSlots = ExportScriptableSlots|ExportNonScriptableSlots, + ExportAllSignals = ExportScriptableSignals|ExportNonScriptableSignals, + ExportAllProperties = ExportScriptableProperties|ExportNonScriptableProperties, + ExportAllContents = ExportScriptableContents|ExportNonScriptableContents, + +#ifndef Q_QDOC + // Qt 4.2 had a misspelling here + ExportAllSignal = ExportAllSignals, +#endif + + ExportChildObjects = 0x1000 + }; + enum UnregisterMode { + UnregisterNode, + UnregisterTree + }; + + Q_DECLARE_FLAGS(RegisterOptions, RegisterOption) + + QDBusConnection(const QString &name); + QDBusConnection(const QDBusConnection &other); + ~QDBusConnection(); + + QDBusConnection &operator=(const QDBusConnection &other); + + bool isConnected() const; + QString baseService() const; + QDBusError lastError() const; + QString name() const; + + bool send(const QDBusMessage &message) const; + bool callWithCallback(const QDBusMessage &message, QObject *receiver, + const char *returnMethod, const char *errorMethod, + int timeout = -1) const; + bool callWithCallback(const QDBusMessage &message, QObject *receiver, + const char *slot, int timeout = -1) const; + QDBusMessage call(const QDBusMessage &message, QDBus::CallMode mode = QDBus::Block, + int timeout = -1) const; + QDBusPendingCall asyncCall(const QDBusMessage &message, int timeout = -1) const; + + bool connect(const QString &service, const QString &path, const QString &interface, + const QString &name, QObject *receiver, const char *slot); + bool disconnect(const QString &service, const QString &path, const QString &interface, + const QString &name, QObject *receiver, const char *slot); + + bool connect(const QString &service, const QString &path, const QString &interface, + const QString &name, const QString& signature, + QObject *receiver, const char *slot); + bool disconnect(const QString &service, const QString &path, const QString &interface, + const QString &name, const QString& signature, + QObject *receiver, const char *slot); + + bool registerObject(const QString &path, QObject *object, + RegisterOptions options = ExportAdaptors); + void unregisterObject(const QString &path, UnregisterMode mode = UnregisterNode); + QObject *objectRegisteredAt(const QString &path) const; + + bool registerService(const QString &serviceName); + bool unregisterService(const QString &serviceName); + + QDBusConnectionInterface *interface() const; + + static QDBusConnection connectToBus(BusType type, const QString &name); + static QDBusConnection connectToBus(const QString &address, const QString &name); + static void disconnectFromBus(const QString &name); + + static QDBusConnection sessionBus(); + static QDBusConnection systemBus(); + + static QDBusConnection sender(); + +protected: + explicit QDBusConnection(QDBusConnectionPrivate *dd); + +private: + friend class QDBusConnectionPrivate; + QDBusConnectionPrivate *d; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QDBusConnection::RegisterOptions) + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/dbus/qdbusconnection_p.h b/src/dbus/qdbusconnection_p.h new file mode 100644 index 0000000..9ff5b6f --- /dev/null +++ b/src/dbus/qdbusconnection_p.h @@ -0,0 +1,318 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the public API. This header file may +// change from version to version without notice, or even be +// removed. +// +// We mean it. +// +// + +#ifndef QDBUSCONNECTION_P_H +#define QDBUSCONNECTION_P_H + +#include <qdbuserror.h> +#include <qdbusconnection.h> + +#include <QtCore/qatomic.h> +#include <QtCore/qhash.h> +#include <QtCore/qmutex.h> +#include <QtCore/qobject.h> +#include <QtCore/qpointer.h> +#include <QtCore/qreadwritelock.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qvector.h> + +#include <qdbus_symbols_p.h> + +#include <qdbusmessage.h> + +QT_BEGIN_NAMESPACE + +class QDBusMessage; +class QSocketNotifier; +class QTimerEvent; +class QDBusObjectPrivate; +class QDBusCallDeliveryEvent; +class QDBusActivateObjectEvent; +class QMetaMethod; +class QDBusInterfacePrivate; +struct QDBusMetaObject; +class QDBusAbstractInterface; +class QDBusConnectionInterface; +class QDBusPendingCallPrivate; + +class QDBusErrorInternal +{ + mutable DBusError error; + Q_DISABLE_COPY(QDBusErrorInternal) +public: + inline QDBusErrorInternal() { q_dbus_error_init(&error); } + inline ~QDBusErrorInternal() { q_dbus_error_free(&error); } + inline bool operator !() const { return !q_dbus_error_is_set(&error); } + inline operator DBusError *() { q_dbus_error_free(&error); return &error; } + inline operator QDBusError() const { QDBusError err(&error); q_dbus_error_free(&error); return err; } +}; + +// QDBusConnectionPrivate holds the DBusConnection and +// can have many QDBusConnection objects referring to it + +class QDBusConnectionPrivate: public QObject +{ + Q_OBJECT +public: + // structs and enums + enum ConnectionMode { InvalidMode, ServerMode, ClientMode, PeerMode }; // LocalMode + + struct Watcher + { + Watcher(): watch(0), read(0), write(0) {} + DBusWatch *watch; + QSocketNotifier *read; + QSocketNotifier *write; + }; + + struct SignalHook + { + inline SignalHook() : obj(0), midx(-1) { } + QString owner, service, path, signature; + QObject* obj; + int midx; + QList<int> params; + QByteArray matchRule; + }; + + struct ObjectTreeNode + { + typedef QVector<ObjectTreeNode> DataList; + + inline ObjectTreeNode() : obj(0), flags(0) { } + inline ObjectTreeNode(const QString &n) // intentionally implicit + : name(n), obj(0), flags(0) { } + inline ~ObjectTreeNode() { } + inline bool operator<(const QString &other) const + { return name < other; } + inline bool operator<(const QStringRef &other) const + { return QStringRef(&name) < other; } + + QString name; + QObject* obj; + int flags; + DataList children; + }; + +public: + // typedefs + typedef QMultiHash<int, Watcher> WatcherHash; + typedef QHash<int, DBusTimeout *> TimeoutHash; + typedef QList<QPair<DBusTimeout *, int> > PendingTimeoutList; + + typedef QMultiHash<QString, SignalHook> SignalHookHash; + typedef QHash<QString, QDBusMetaObject* > MetaObjectHash; + typedef QHash<QByteArray, int> MatchRefCountHash; + +public: + // public methods are entry points from other objects + explicit QDBusConnectionPrivate(QObject *parent = 0); + ~QDBusConnectionPrivate(); + void deleteYourself(); + + void setBusService(const QDBusConnection &connection); + void setPeer(DBusConnection *connection, const QDBusErrorInternal &error); + void setConnection(DBusConnection *connection, const QDBusErrorInternal &error); + void setServer(DBusServer *server, const QDBusErrorInternal &error); + void closeConnection(); + + QString getNameOwner(const QString &service); + + int send(const QDBusMessage &message); + QDBusMessage sendWithReply(const QDBusMessage &message, int mode, int timeout = -1); + QDBusMessage sendWithReplyLocal(const QDBusMessage &message); + QDBusPendingCallPrivate *sendWithReplyAsync(const QDBusMessage &message, int timeout = -1); + int sendWithReplyAsync(const QDBusMessage &message, QObject *receiver, + const char *returnMethod, const char *errorMethod, int timeout = -1); + void connectSignal(const QString &key, const SignalHook &hook); + SignalHookHash::Iterator disconnectSignal(SignalHookHash::Iterator &it); + void registerObject(const ObjectTreeNode *node); + void connectRelay(const QString &service, const QString ¤tOwner, + const QString &path, const QString &interface, + QDBusAbstractInterface *receiver, const char *signal); + void disconnectRelay(const QString &service, const QString ¤tOwner, + const QString &path, const QString &interface, + QDBusAbstractInterface *receiver, const char *signal); + + bool handleMessage(const QDBusMessage &msg); + void waitForFinished(QDBusPendingCallPrivate *pcall); + + QDBusMetaObject *findMetaObject(const QString &service, const QString &path, + const QString &interface, QDBusError &error); + + void registerService(const QString &serviceName); + void unregisterService(const QString &serviceName); + + void postEventToThread(int action, QObject *target, QEvent *event); + + inline void serverConnection(const QDBusConnection &connection) + { emit newServerConnection(connection); } + +private: + void checkThread(); + bool handleError(const QDBusErrorInternal &error); + + void handleSignal(const QString &key, const QDBusMessage &msg); + void handleSignal(const QDBusMessage &msg); + void handleObjectCall(const QDBusMessage &message); + + void activateSignal(const SignalHook& hook, const QDBusMessage &msg); + void activateObject(ObjectTreeNode &node, const QDBusMessage &msg, int pathStartPos); + bool activateInternalFilters(const ObjectTreeNode &node, const QDBusMessage &msg); + bool activateCall(QObject *object, int flags, const QDBusMessage &msg); + + void sendError(const QDBusMessage &msg, QDBusError::ErrorType code); + void deliverCall(QObject *object, int flags, const QDBusMessage &msg, + const QList<int> &metaTypes, int slotIdx); + + bool isServiceRegisteredByThread(const QString &serviceName) const; + +protected: + void customEvent(QEvent *e); + void timerEvent(QTimerEvent *e); + +public slots: + // public slots + void doDispatch(); + void socketRead(int); + void socketWrite(int); + void objectDestroyed(QObject *o); + void relaySignal(QObject *obj, const QMetaObject *, int signalId, const QVariantList &args); + void _q_serviceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner); + +signals: + void serviceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner); + void callWithCallbackFailed(const QDBusError &error, const QDBusMessage &message); + void newServerConnection(const QDBusConnection &connection); + +public: + QAtomicInt ref; + QString name; // this connection's name + QString baseService; // this connection's base service + + ConnectionMode mode; + + // members accessed in unlocked mode (except for deletion) + // connection and server provide their own locking mechanisms + // busService doesn't have state to be changed + DBusConnection *connection; + DBusServer *server; + QDBusConnectionInterface *busService; + + // watchers and timeouts are accessed from any thread + // but the corresponding timer and QSocketNotifier must be handled + // only in the object's thread + QMutex watchAndTimeoutLock; + WatcherHash watchers; + TimeoutHash timeouts; + PendingTimeoutList timeoutsPendingAdd; + + // members accessed through a lock + QMutex dispatchLock; + QReadWriteLock lock; + QDBusError lastError; + + QStringList serviceNames; + SignalHookHash signalHooks; + MatchRefCountHash matchRefCounts; + ObjectTreeNode rootNode; + MetaObjectHash cachedMetaObjects; + + QMutex callDeliveryMutex; + QDBusCallDeliveryEvent *callDeliveryState; // protected by the callDeliveryMutex mutex + +public: + // static methods + static int findSlot(QObject *obj, const QByteArray &normalizedName, QList<int>& params); + static bool prepareHook(QDBusConnectionPrivate::SignalHook &hook, QString &key, + const QString &service, const QString &owner, + const QString &path, const QString &interface, const QString &name, + QObject *receiver, const char *signal, int minMIdx, + bool buildSignature); + static DBusHandlerResult messageFilter(DBusConnection *, DBusMessage *, void *); + static QDBusCallDeliveryEvent *prepareReply(QDBusConnectionPrivate *target, QObject *object, + int idx, const QList<int> &metaTypes, + const QDBusMessage &msg); + static void processFinishedCall(QDBusPendingCallPrivate *call); + + static QDBusConnectionPrivate *d(const QDBusConnection& q) { return q.d; } + static QDBusConnection q(QDBusConnectionPrivate *connection) { return QDBusConnection(connection); } + + static void setSender(const QDBusConnectionPrivate *s); + static void setConnection(const QString &name, QDBusConnectionPrivate *c); + + friend class QDBusActivateObjectEvent; + friend class QDBusCallDeliveryEvent; +}; + +// in qdbusmisc.cpp +extern int qDBusParametersForMethod(const QMetaMethod &mm, QList<int>& metaTypes); +extern int qDBusNameToTypeId(const char *name); +extern bool qDBusCheckAsyncTag(const char *tag); + +// in qdbusinternalfilters.cpp +extern QString qDBusIntrospectObject(const QDBusConnectionPrivate::ObjectTreeNode &node); +extern QDBusMessage qDBusPropertyGet(const QDBusConnectionPrivate::ObjectTreeNode &node, + const QDBusMessage &msg); +extern QDBusMessage qDBusPropertySet(const QDBusConnectionPrivate::ObjectTreeNode &node, + const QDBusMessage &msg); +extern QDBusMessage qDBusPropertyGetAll(const QDBusConnectionPrivate::ObjectTreeNode &node, + const QDBusMessage &msg); + +// in qdbusxmlgenerator.cpp +extern QString qDBusInterfaceFromMetaObject(const QMetaObject *mo); + +QT_END_NAMESPACE + +#endif diff --git a/src/dbus/qdbusconnectioninterface.cpp b/src/dbus/qdbusconnectioninterface.cpp new file mode 100644 index 0000000..1d38761 --- /dev/null +++ b/src/dbus/qdbusconnectioninterface.cpp @@ -0,0 +1,403 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdbusconnectioninterface.h" + +#include <QtCore/QByteArray> +#include <QtCore/QList> +#include <QtCore/QMap> +#include <QtCore/QString> +#include <QtCore/QStringList> +#include <QtCore/QVariant> +#include <QtCore/QDebug> + +#include <qdbus_symbols_p.h> // for the DBUS_* constants + +QT_BEGIN_NAMESPACE + +/* + * Implementation of interface class QDBusConnectionInterface + */ + +/*! + \class QDBusConnectionInterface + \inmodule QtDBus + \since 4.2 + + \brief The QDBusConnectionInterface class provides access to the D-Bus bus daemon service. + + The D-Bus bus server daemon provides one special interface \c + org.freedesktop.DBus that allows clients to access certain + properties of the bus, such as the current list of clients + connected. The QDBusConnectionInterface class provides access to that + interface. + + The most common uses of this class are to register and unregister + service names on the bus using the registerService() and + unregisterService() functions, query about existing names using + the isServiceRegistered(), registeredServiceNames() and + serviceOwner() functions, and to receive notification that a + client has registered or de-registered through the + serviceRegistered(), serviceUnregistered() and serviceOwnerChanged() + signals. +*/ + +/*! + \enum QDBusConnectionInterface::ServiceQueueOptions + + Flags for determining how a service registration should behave, in + case the service name is already registered. + + \value DontQueueService If an application requests a name that + is already owned, no queueing will be + performed. The registeredService() + call will simply fail. + This is the default. + + \value QueueService Attempts to register the requested + service, but do not try to replace it + if another application already has it + registered. Instead, simply put this + application in queue, until it is + given up. The serviceRegistered() + signal will be emitted when that + happens. + + \value ReplaceExistingService If another application already has + the service name registered, attempt + to replace it. + + \sa ServiceReplacementOptions +*/ + +/*! + \enum QDBusConnectionInterface::ServiceReplacementOptions + + Flags for determining if the D-Bus server should allow another + application to replace a name that this application has registered + with the ReplaceExistingService option. + + The possible values are: + + \value DontAllowReplacement Do not allow another application to + replace us. The service must be + explicitly unregistered with + unregisterService() for another + application to acquire it. + This is the default. + + \value AllowReplacement Allow other applications to replace us + with the ReplaceExistingService option + to registerService() without + intervention. If that happens, the + serviceUnregistered() signal will be + emitted. + + \sa ServiceQueueOptions +*/ + +/*! + \enum QDBusConnectionInterface::RegisterServiceReply + + The possible return values from registerService(): + + \value ServiceNotRegistered The call failed and the service name was not registered. + \value ServiceRegistered The caller is now the owner of the service name. + \value ServiceQueued The caller specified the QueueService flag and the + service was already registered, so we are in queue. + + The serviceRegistered() signal will be emitted when the service is + acquired by this application. +*/ + +/*! + \internal +*/ +const char *QDBusConnectionInterface::staticInterfaceName() +{ return "org.freedesktop.DBus"; } + +/*! + \internal +*/ +QDBusConnectionInterface::QDBusConnectionInterface(const QDBusConnection &connection, + QObject *parent) + : QDBusAbstractInterface(QLatin1String(DBUS_SERVICE_DBUS), + QLatin1String(DBUS_PATH_DBUS), + DBUS_INTERFACE_DBUS, connection, parent) +{ + connect(this, SIGNAL(NameAcquired(QString)), this, SIGNAL(serviceRegistered(QString))); + connect(this, SIGNAL(NameLost(QString)), this, SIGNAL(serviceUnregistered(QString))); + connect(this, SIGNAL(NameOwnerChanged(QString,QString,QString)), + this, SIGNAL(serviceOwnerChanged(QString,QString,QString))); +} + +/*! + \internal +*/ +QDBusConnectionInterface::~QDBusConnectionInterface() +{ +} + +/*! + Returns the unique connection name of the primary owner of the + name \a name. If the requested name doesn't have an owner, returns + a \c org.freedesktop.DBus.Error.NameHasNoOwner error. +*/ +QDBusReply<QString> QDBusConnectionInterface::serviceOwner(const QString &name) const +{ + return internalConstCall(QDBus::AutoDetect, QLatin1String("GetNameOwner"), QList<QVariant>() << name); +} + +/*! + \property QDBusConnectionInterface::registeredServiceNames + \brief holds the registered service names + + Lists all names currently registered on the bus. +*/ +QDBusReply<QStringList> QDBusConnectionInterface::registeredServiceNames() const +{ + return internalConstCall(QDBus::AutoDetect, QLatin1String("ListNames")); +} + +/*! + Returns true if the service name \a serviceName has is currently + registered. +*/ +QDBusReply<bool> QDBusConnectionInterface::isServiceRegistered(const QString &serviceName) const +{ + return internalConstCall(QDBus::AutoDetect, QLatin1String("NameHasOwner"), + QList<QVariant>() << serviceName); +} + +/*! + Returns the Unix Process ID (PID) for the process currently + holding the bus service \a serviceName. +*/ +QDBusReply<uint> QDBusConnectionInterface::servicePid(const QString &serviceName) const +{ + return internalConstCall(QDBus::AutoDetect, QLatin1String("GetConnectionUnixProcessID"), + QList<QVariant>() << serviceName); +} + +/*! + Returns the Unix User ID (UID) for the process currently holding + the bus service \a serviceName. +*/ +QDBusReply<uint> QDBusConnectionInterface::serviceUid(const QString &serviceName) const +{ + return internalConstCall(QDBus::AutoDetect, QLatin1String("GetConnectionUnixUser"), + QList<QVariant>() << serviceName); +} + +/*! + Requests that the bus start the service given by the name \a name. +*/ +QDBusReply<void> QDBusConnectionInterface::startService(const QString &name) +{ + return call(QLatin1String("StartServiceByName"), name, uint(0)); +} + +/*! + Requests to register the service name \a serviceName on the + bus. The \a qoption flag specifies how the D-Bus server should behave + if \a serviceName is already registered. The \a roption flag + specifies if the server should allow another application to + replace our registered name. + + If the service registration succeeds, the serviceRegistered() + signal will be emitted. If we are placed in queue, the signal will + be emitted when we obtain the name. If \a roption is + AllowReplacement, the serviceUnregistered() signal will be emitted + if another application replaces this one. + + \sa unregisterService() +*/ +QDBusReply<QDBusConnectionInterface::RegisterServiceReply> +QDBusConnectionInterface::registerService(const QString &serviceName, + ServiceQueueOptions qoption, + ServiceReplacementOptions roption) +{ + // reconstruct the low-level flags + uint flags = 0; + switch (qoption) { + case DontQueueService: + flags = DBUS_NAME_FLAG_DO_NOT_QUEUE; + break; + case QueueService: + flags = 0; + break; + case ReplaceExistingService: + flags = DBUS_NAME_FLAG_DO_NOT_QUEUE | DBUS_NAME_FLAG_REPLACE_EXISTING; + break; + } + + switch (roption) { + case DontAllowReplacement: + break; + case AllowReplacement: + flags |= DBUS_NAME_FLAG_ALLOW_REPLACEMENT; + break; + } + + QDBusMessage reply = call(QLatin1String("RequestName"), serviceName, flags); +// qDebug() << "QDBusConnectionInterface::registerService" << serviceName << "Reply:" << reply; + + // convert the low-level flags to something that we can use + if (reply.type() == QDBusMessage::ReplyMessage) { + uint code = 0; + + switch (reply.arguments().at(0).toUInt()) { + case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER: + case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER: + code = uint(ServiceRegistered); + break; + + case DBUS_REQUEST_NAME_REPLY_EXISTS: + code = uint(ServiceNotRegistered); + break; + + case DBUS_REQUEST_NAME_REPLY_IN_QUEUE: + code = uint(ServiceQueued); + break; + } + + reply.setArguments(QVariantList() << code); + } + + return reply; +} + +/*! + Releases the claim on the bus service name \a serviceName, that + had been previously registered with registerService(). If this + application had ownership of the name, it will be released for + other applications to claim. If it only had the name queued, it + gives up its position in the queue. +*/ +QDBusReply<bool> +QDBusConnectionInterface::unregisterService(const QString &serviceName) +{ + QDBusMessage reply = call(QLatin1String("ReleaseName"), serviceName); + if (reply.type() == QDBusMessage::ReplyMessage) { + bool success = reply.arguments().at(0).toUInt() == DBUS_RELEASE_NAME_REPLY_RELEASED; + reply.setArguments(QVariantList() << success); + } + return reply; +} + +/*! + \internal +*/ +void QDBusConnectionInterface::connectNotify(const char *signalName) +{ + // translate the signal names to what we really want + // this avoids setting hooks for signals that don't exist on the bus + if (qstrcmp(signalName, SIGNAL(serviceRegistered(QString))) == 0) + QDBusAbstractInterface::connectNotify(SIGNAL(NameAcquired(QString))); + + else if (qstrcmp(signalName, SIGNAL(serviceUnregistered(QString))) == 0) + QDBusAbstractInterface::connectNotify(SIGNAL(NameLost(QString))); + + else if (qstrcmp(signalName, SIGNAL(serviceOwnerChanged(QString,QString,QString))) == 0) + QDBusAbstractInterface::connectNotify(SIGNAL(NameOwnerChanged(QString,QString,QString))); +} + +/*! + \internal +*/ +void QDBusConnectionInterface::disconnectNotify(const char *signalName) +{ + // translate the signal names to what we really want + // this avoids setting hooks for signals that don't exist on the bus + if (qstrcmp(signalName, SIGNAL(serviceRegistered(QString))) == 0) + QDBusAbstractInterface::disconnectNotify(SIGNAL(NameAcquired(QString))); + + else if (qstrcmp(signalName, SIGNAL(serviceUnregistered(QString))) == 0) + QDBusAbstractInterface::disconnectNotify(SIGNAL(NameLost(QString))); + + else if (qstrcmp(signalName, SIGNAL(serviceOwnerChanged(QString,QString,QString))) == 0) + QDBusAbstractInterface::disconnectNotify(SIGNAL(NameOwnerChanged(QString,QString,QString))); +} + +// signals +/*! + \fn QDBusConnectionInterface::serviceRegistered(const QString &serviceName) + + This signal is emitted by the D-Bus server when the bus service + name (unique connection name or well-known service name) given by + \a serviceName is acquired by this application. + + Acquisition happens after this application has requested a name using + registerService(). +*/ + +/*! + \fn QDBusConnectionInterface::serviceUnregistered(const QString &serviceName) + + This signal is emitted by the D-Bus server when this application + loses ownership of the bus service name given by \a serviceName. +*/ + +/*! + \fn QDBusConnectionInterface::serviceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner) + + This signal is emitted by the D-Bus server whenever a service + ownership change happens in the bus, including apparition and + disparition of names. + + This signal means the application \a oldOwner lost ownership of + bus name \a name to application \a newOwner. If \a oldOwner is an + empty string, it means the name \a name has just been created; if + \a newOwner is empty, the name \a name has no current owner and is + no longer available. +*/ + +/*! + \fn void QDBusConnectionInterface::callWithCallbackFailed(const QDBusError &error, const QDBusMessage &call) + + This signal is emitted when there is an error during a + QDBusConnection::callWithCallback(). \a error specifies the error. + \a call is the message that couldn't be delivered. + + \sa QDBusConnection::callWithCallback() + */ + +QT_END_NAMESPACE diff --git a/src/dbus/qdbusconnectioninterface.h b/src/dbus/qdbusconnectioninterface.h new file mode 100644 index 0000000..77a4add --- /dev/null +++ b/src/dbus/qdbusconnectioninterface.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDBUSBUS_H +#define QDBUSBUS_H + +#include <QtCore/qstringlist.h> + +#include <QtDBus/qdbusabstractinterface.h> +#include <QtDBus/qdbusreply.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(DBus) + +class QDBusConnection; +class QString; +class QByteArray; + +/* + * Proxy class for interface org.freedesktop.DBus + */ +class QDBUS_EXPORT QDBusConnectionInterface: public QDBusAbstractInterface +{ + Q_OBJECT + Q_ENUMS(ServiceQueueOptions ServiceReplacementOptions RegisterServiceReply) + friend class QDBusConnectionPrivate; + static inline const char *staticInterfaceName(); + + explicit QDBusConnectionInterface(const QDBusConnection &connection, QObject *parent); + ~QDBusConnectionInterface(); + + Q_PROPERTY(QDBusReply<QStringList> registeredServiceNames READ registeredServiceNames) + +public: + enum ServiceQueueOptions { + DontQueueService, + QueueService, + ReplaceExistingService + }; + enum ServiceReplacementOptions { + DontAllowReplacement, + AllowReplacement + }; + enum RegisterServiceReply { + ServiceNotRegistered = 0, + ServiceRegistered, + ServiceQueued + }; + +public Q_SLOTS: + QDBusReply<QStringList> registeredServiceNames() const; + QDBusReply<bool> isServiceRegistered(const QString &serviceName) const; + QDBusReply<QString> serviceOwner(const QString &name) const; + QDBusReply<bool> unregisterService(const QString &serviceName); + QDBusReply<QDBusConnectionInterface::RegisterServiceReply> registerService(const QString &serviceName, + ServiceQueueOptions qoption = DontQueueService, + ServiceReplacementOptions roption = DontAllowReplacement); + + QDBusReply<uint> servicePid(const QString &serviceName) const; + QDBusReply<uint> serviceUid(const QString &serviceName) const; + + QDBusReply<void> startService(const QString &name); + +Q_SIGNALS: + void serviceRegistered(const QString &service); + void serviceUnregistered(const QString &service); + void serviceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner); + void callWithCallbackFailed(const QDBusError &error, const QDBusMessage &call); + +#ifndef Q_QDOC + // internal signals + // do not use + void NameAcquired(const QString &); + void NameLost(const QString &); + void NameOwnerChanged(const QString &, const QString &, const QString &); +protected: + void connectNotify(const char *); + void disconnectNotify(const char *); +#endif +}; + +QT_END_NAMESPACE + +Q_DECLARE_BUILTIN_METATYPE(QDBusConnectionInterface::RegisterServiceReply, UInt) + +QT_END_HEADER + +#endif diff --git a/src/dbus/qdbuscontext.cpp b/src/dbus/qdbuscontext.cpp new file mode 100644 index 0000000..8cffa81 --- /dev/null +++ b/src/dbus/qdbuscontext.cpp @@ -0,0 +1,204 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdbusmessage.h" +#include "qdbusconnection.h" +#include "qdbusabstractadaptor.h" + +#include "qdbuscontext.h" +#include "qdbuscontext_p.h" + +QT_BEGIN_NAMESPACE + +QDBusContextPrivate *QDBusContextPrivate::set(QObject *obj, QDBusContextPrivate *newContext) +{ + // determine if this is an adaptor or not + if (qobject_cast<QDBusAbstractAdaptor *>(obj)) + obj = obj->parent(); + + Q_ASSERT(obj); + + void *ptr = obj->qt_metacast("QDBusContext"); + QDBusContext *q_ptr = reinterpret_cast<QDBusContext *>(ptr); + if (q_ptr) { + QDBusContextPrivate *old = q_ptr->d_ptr; + q_ptr->d_ptr = newContext; + return old; + } + + return 0; +} + +/*! + \since 4.3 + \class QDBusContext + \inmodule QtDBus + + \brief The QDBusContext class allows slots to determine the D-Bus context of the calls. + + When a slot is called in an object due to a signal delivery or due + to a remote method call, it is sometimes necessary to know the + context in which that happened. In particular, if the slot + determines that it wants to send the reply at a later opportunity + or if it wants to reply with an error, the context is needed. + + The QDBusContext class is an alternative to accessing the context + that doesn't involve modifying the code generated by the \l + {QtDBus XML Compiler (qdbusxml2cpp)}. + + QDBusContext is used by subclassing it from the objects being + exported using QDBusConnection::registerObject(). The following + example illustrates the usage: + + \snippet doc/src/snippets/code/src_qdbus_qdbuscontext.cpp 0 + + The example illustrates the two typical uses, that of sending + error replies and that of delayed replies. + + Note: do not subclass QDBusContext and QDBusAbstractAdaptor at the + same time. QDBusContext should appear in the real object, not the + adaptor. If it's necessary from the adaptor code to determine the + context, use a public inheritance and access the functions via + QObject::parent(). +*/ + +/*! + Constructs an empty QDBusContext. + */ +QDBusContext::QDBusContext() + : d_ptr(0) +{ +} + +/*! + An empty destructor. + */ +QDBusContext::~QDBusContext() +{ +} + +/*! + Returns true if we are processing a D-Bus call. If this function + returns true, the rest of the functions in this class are + available. + + Accessing those functions when this function returns false is + undefined and may lead to crashes. +*/ +bool QDBusContext::calledFromDBus() const +{ + return d_ptr; +} + +/*! + Returns the connection from which this call was received. +*/ +QDBusConnection QDBusContext::connection() const +{ + return d_ptr->connection; +} + +/*! + Returns the message that generated this call. +*/ +const QDBusMessage &QDBusContext::message() const +{ + return d_ptr->message; +} + +/*! + Returns true if this call will have a delayed reply. + + \sa setDelayedReply() +*/ +bool QDBusContext::isDelayedReply() const +{ + return message().isDelayedReply(); +} + +/*! + Sets whether this call will have a delayed reply or not. + + If \a enable is false, QtDBus will automatically generate a reply + back to the caller, if needed, as soon as the called slot returns. + + If \a enable is true, QtDBus will not generate automatic + replies. It will also ignore the return value from the slot and + any output parameters. Instead, the called object is responsible + for storing the incoming message and send a reply or error at a + later time. + + Failing to send a reply will result in an automatic timeout error + being generated by D-Bus. +*/ +void QDBusContext::setDelayedReply(bool enable) const +{ + message().setDelayedReply(enable); +} + +/*! + Sends an error \a name as a reply to the caller. The optional \a + msg parameter is a human-readable text explaining the failure. + + If an error is sent, the return value and any output parameters + from the called slot will be ignored by QtDBus. +*/ +void QDBusContext::sendErrorReply(const QString &name, const QString &msg) const +{ + setDelayedReply(true); + connection().send(message().createErrorReply(name, msg)); +} + +/*! + \overload + Sends an error \a type as a reply to the caller. The optional \a + msg parameter is a human-readable text explaining the failure. + + If an error is sent, the return value and any output parameters + from the called slot will be ignored by QtDBus. +*/ +void QDBusContext::sendErrorReply(QDBusError::ErrorType type, const QString &msg) const +{ + setDelayedReply(true); + connection().send(message().createErrorReply(type, msg)); +} + +QT_END_NAMESPACE diff --git a/src/dbus/qdbuscontext.h b/src/dbus/qdbuscontext.h new file mode 100644 index 0000000..3d043cf --- /dev/null +++ b/src/dbus/qdbuscontext.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDBUSCONTEXT_H +#define QDBUSCONTEXT_H + +#include <QtCore/qstring.h> +#include <QtDBus/qdbuserror.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(DBus) + +class QDBusConnection; +class QDBusMessage; + +class QDBusContextPrivate; +class QDBUS_EXPORT QDBusContext +{ +public: + QDBusContext(); + ~QDBusContext(); + + bool calledFromDBus() const; + QDBusConnection connection() const; + const QDBusMessage &message() const; + + // convenience methods + bool isDelayedReply() const; + // yes, they are const, so that you can use them even from const methods + void setDelayedReply(bool enable) const; + void sendErrorReply(const QString &name, const QString &msg = QString()) const; + void sendErrorReply(QDBusError::ErrorType type, const QString &msg = QString()) const; + +private: + QDBusContextPrivate *d_ptr; + Q_DECLARE_PRIVATE(QDBusContext) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/dbus/qdbuscontext_p.h b/src/dbus/qdbuscontext_p.h new file mode 100644 index 0000000..ade1e7d --- /dev/null +++ b/src/dbus/qdbuscontext_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the public API. This header file may +// change from version to version without notice, or even be +// removed. +// +// We mean it. +// +// + +#ifndef QDBUSCONTEXT_P_H +#define QDBUSCONTEXT_P_H + +QT_BEGIN_NAMESPACE + +class QDBusMessage; +class QDBusConnection; + +class QDBusContext; +class QDBusContextPrivate +{ +public: + inline QDBusContextPrivate(const QDBusConnection &conn, const QDBusMessage &msg) + : connection(conn), message(msg) {} + + QDBusConnection connection; + const QDBusMessage &message; + + static QDBusContextPrivate *set(QObject *obj, QDBusContextPrivate *newContext); +}; + +QT_END_NAMESPACE + +#endif + diff --git a/src/dbus/qdbusdemarshaller.cpp b/src/dbus/qdbusdemarshaller.cpp new file mode 100644 index 0000000..7130324 --- /dev/null +++ b/src/dbus/qdbusdemarshaller.cpp @@ -0,0 +1,342 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdbusargument_p.h" +#include <stdlib.h> + +QT_BEGIN_NAMESPACE + +template <typename T> +static inline T qIterGet(DBusMessageIter *it) +{ + T t; + q_dbus_message_iter_get_basic(it, &t); + q_dbus_message_iter_next(it); + return t; +} + +QDBusDemarshaller::~QDBusDemarshaller() +{ +} + +inline QString QDBusDemarshaller::currentSignature() +{ + char *sig = q_dbus_message_iter_get_signature(&iterator); + QString retval = QString::fromUtf8(sig); + q_dbus_free(sig); + + return retval; +} + +inline uchar QDBusDemarshaller::toByte() +{ + return qIterGet<uchar>(&iterator); +} + +inline bool QDBusDemarshaller::toBool() +{ + return bool(qIterGet<dbus_bool_t>(&iterator)); +} + +inline ushort QDBusDemarshaller::toUShort() +{ + return qIterGet<dbus_uint16_t>(&iterator); +} + +inline short QDBusDemarshaller::toShort() +{ + return qIterGet<dbus_int16_t>(&iterator); +} + +inline int QDBusDemarshaller::toInt() +{ + return qIterGet<dbus_int32_t>(&iterator); +} + +inline uint QDBusDemarshaller::toUInt() +{ + return qIterGet<dbus_uint32_t>(&iterator); +} + +inline qlonglong QDBusDemarshaller::toLongLong() +{ + return qIterGet<qlonglong>(&iterator); +} + +inline qulonglong QDBusDemarshaller::toULongLong() +{ + return qIterGet<qulonglong>(&iterator); +} + +inline double QDBusDemarshaller::toDouble() +{ + return qIterGet<double>(&iterator); +} + +inline QString QDBusDemarshaller::toString() +{ + return QString::fromUtf8(qIterGet<char *>(&iterator)); +} + +inline QDBusObjectPath QDBusDemarshaller::toObjectPath() +{ + return QDBusObjectPath(QString::fromUtf8(qIterGet<char *>(&iterator))); +} + +inline QDBusSignature QDBusDemarshaller::toSignature() +{ + return QDBusSignature(QString::fromUtf8(qIterGet<char *>(&iterator))); +} + +inline QDBusVariant QDBusDemarshaller::toVariant() +{ + QDBusDemarshaller sub; + sub.message = q_dbus_message_ref(message); + q_dbus_message_iter_recurse(&iterator, &sub.iterator); + q_dbus_message_iter_next(&iterator); + + return QDBusVariant( sub.toVariantInternal() ); +} + +QDBusArgument::ElementType QDBusDemarshaller::currentType() +{ + switch (q_dbus_message_iter_get_arg_type(&iterator)) { + 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_BOOLEAN: + case DBUS_TYPE_DOUBLE: + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + case DBUS_TYPE_SIGNATURE: + return QDBusArgument::BasicType; + + case DBUS_TYPE_VARIANT: + return QDBusArgument::VariantType; + + case DBUS_TYPE_ARRAY: + switch (q_dbus_message_iter_get_element_type(&iterator)) { + case DBUS_TYPE_BYTE: + case DBUS_TYPE_STRING: + // QByteArray and QStringList + return QDBusArgument::BasicType; + case DBUS_TYPE_DICT_ENTRY: + return QDBusArgument::MapType; + default: + return QDBusArgument::ArrayType; + } + + case DBUS_TYPE_STRUCT: + return QDBusArgument::StructureType; + case DBUS_TYPE_DICT_ENTRY: + return QDBusArgument::MapEntryType; + + case DBUS_TYPE_INVALID: + return QDBusArgument::UnknownType; + + default: + qWarning("QDBusDemarshaller: Found unknown D-Bus type %d '%c'", + q_dbus_message_iter_get_arg_type(&iterator), + q_dbus_message_iter_get_arg_type(&iterator)); + } + return QDBusArgument::UnknownType; +} + +QVariant QDBusDemarshaller::toVariantInternal() +{ + switch (q_dbus_message_iter_get_arg_type(&iterator)) { + case DBUS_TYPE_BYTE: + return qVariantFromValue(toByte()); + case DBUS_TYPE_INT16: + return qVariantFromValue(toShort()); + case DBUS_TYPE_UINT16: + return qVariantFromValue(toUShort()); + case DBUS_TYPE_INT32: + return toInt(); + case DBUS_TYPE_UINT32: + return toUInt(); + case DBUS_TYPE_DOUBLE: + return toDouble(); + case DBUS_TYPE_BOOLEAN: + return toBool(); + case DBUS_TYPE_INT64: + return toLongLong(); + case DBUS_TYPE_UINT64: + return toULongLong(); + case DBUS_TYPE_STRING: + return toString(); + case DBUS_TYPE_OBJECT_PATH: + return qVariantFromValue(toObjectPath()); + case DBUS_TYPE_SIGNATURE: + return qVariantFromValue(toSignature()); + case DBUS_TYPE_VARIANT: + return qVariantFromValue(toVariant()); + + case DBUS_TYPE_ARRAY: + switch (q_dbus_message_iter_get_element_type(&iterator)) { + case DBUS_TYPE_BYTE: + // QByteArray + return toByteArray(); + case DBUS_TYPE_STRING: + return toStringList(); + case DBUS_TYPE_DICT_ENTRY: + return qVariantFromValue(duplicate()); + + default: + return qVariantFromValue(duplicate()); + } + + case DBUS_TYPE_STRUCT: + return qVariantFromValue(duplicate()); + + default: + qWarning("QDBusDemarshaller: Found unknown D-Bus type %d '%c'", + q_dbus_message_iter_get_arg_type(&iterator), + q_dbus_message_iter_get_arg_type(&iterator)); + return QVariant(); + break; + }; +} + +QStringList QDBusDemarshaller::toStringList() +{ + QStringList list; + + QDBusDemarshaller sub; + q_dbus_message_iter_recurse(&iterator, &sub.iterator); + q_dbus_message_iter_next(&iterator); + while (!sub.atEnd()) + list.append(sub.toString()); + + return list; +} + +QByteArray QDBusDemarshaller::toByteArray() +{ + DBusMessageIter sub; + q_dbus_message_iter_recurse(&iterator, &sub); + q_dbus_message_iter_next(&iterator); + int len; + char* data; + q_dbus_message_iter_get_fixed_array(&sub,&data,&len); + return QByteArray(data,len); +} + +bool QDBusDemarshaller::atEnd() +{ + // dbus_message_iter_has_next is broken if the list has one single element + return q_dbus_message_iter_get_arg_type(&iterator) == DBUS_TYPE_INVALID; +} + +inline QDBusDemarshaller *QDBusDemarshaller::beginStructure() +{ + return beginCommon(); +} + +inline QDBusDemarshaller *QDBusDemarshaller::beginArray() +{ + return beginCommon(); +} + +inline QDBusDemarshaller *QDBusDemarshaller::beginMap() +{ + return beginCommon(); +} + +inline QDBusDemarshaller *QDBusDemarshaller::beginMapEntry() +{ + return beginCommon(); +} + +QDBusDemarshaller *QDBusDemarshaller::beginCommon() +{ + QDBusDemarshaller *d = new QDBusDemarshaller; + d->parent = this; + d->message = q_dbus_message_ref(message); + + // recurse + q_dbus_message_iter_recurse(&iterator, &d->iterator); + q_dbus_message_iter_next(&iterator); + return d; +} + +inline QDBusDemarshaller *QDBusDemarshaller::endStructure() +{ + return endCommon(); +} + +inline QDBusDemarshaller *QDBusDemarshaller::endArray() +{ + return endCommon(); +} + +inline QDBusDemarshaller *QDBusDemarshaller::endMap() +{ + return endCommon(); +} + +inline QDBusDemarshaller *QDBusDemarshaller::endMapEntry() +{ + return endCommon(); +} + +QDBusDemarshaller *QDBusDemarshaller::endCommon() +{ + QDBusDemarshaller *retval = parent; + delete this; + return retval; +} + +QDBusArgument QDBusDemarshaller::duplicate() +{ + QDBusDemarshaller *d = new QDBusDemarshaller; + d->iterator = iterator; + d->message = q_dbus_message_ref(message); + + q_dbus_message_iter_next(&iterator); + return QDBusArgumentPrivate::create(d); +} + +QT_END_NAMESPACE diff --git a/src/dbus/qdbuserror.cpp b/src/dbus/qdbuserror.cpp new file mode 100644 index 0000000..955a31c --- /dev/null +++ b/src/dbus/qdbuserror.cpp @@ -0,0 +1,349 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdbuserror.h" + +#include <qdebug.h> +#include <qvarlengtharray.h> + +#include <qdbus_symbols_p.h> +#include "qdbusmessage.h" +#include "qdbusmessage_p.h" + +QT_BEGIN_NAMESPACE + +/* + * Use the following Perl script to generate the error string index list: +===== PERL SCRIPT ==== +print "static const char errorMessages_string[] =\n"; +$counter = 0; +$i = 0; +while (<STDIN>) { + chomp; + print " \"$_\\0\"\n"; + $sizes[$i++] = $counter; + $counter += 1 + length $_; +} +print " \"\\0\";\n\nstatic const int errorMessages_indices[] = {\n "; +for ($j = 0; $j < $i; ++$j) { + printf "$sizes[$j], "; +} +print "0\n};\n"; +===== PERL SCRIPT ==== + + * The input data is as follows: +other +org.freedesktop.DBus.Error.Failed +org.freedesktop.DBus.Error.NoMemory +org.freedesktop.DBus.Error.ServiceUnknown +org.freedesktop.DBus.Error.NoReply +org.freedesktop.DBus.Error.BadAddress +org.freedesktop.DBus.Error.NotSupported +org.freedesktop.DBus.Error.LimitsExceeded +org.freedesktop.DBus.Error.AccessDenied +org.freedesktop.DBus.Error.NoServer +org.freedesktop.DBus.Error.Timeout +org.freedesktop.DBus.Error.NoNetwork +org.freedesktop.DBus.Error.AddressInUse +org.freedesktop.DBus.Error.Disconnected +org.freedesktop.DBus.Error.InvalidArgs +org.freedesktop.DBus.Error.UnknownMethod +org.freedesktop.DBus.Error.TimedOut +org.freedesktop.DBus.Error.InvalidSignature +org.freedesktop.DBus.Error.UnknownInterface +com.trolltech.QtDBus.Error.InternalError +org.freedesktop.DBus.Error.UnknownObject +*/ + +// in the same order as KnownErrors! +static const char errorMessages_string[] = + "other\0" + "org.freedesktop.DBus.Error.Failed\0" + "org.freedesktop.DBus.Error.NoMemory\0" + "org.freedesktop.DBus.Error.ServiceUnknown\0" + "org.freedesktop.DBus.Error.NoReply\0" + "org.freedesktop.DBus.Error.BadAddress\0" + "org.freedesktop.DBus.Error.NotSupported\0" + "org.freedesktop.DBus.Error.LimitsExceeded\0" + "org.freedesktop.DBus.Error.AccessDenied\0" + "org.freedesktop.DBus.Error.NoServer\0" + "org.freedesktop.DBus.Error.Timeout\0" + "org.freedesktop.DBus.Error.NoNetwork\0" + "org.freedesktop.DBus.Error.AddressInUse\0" + "org.freedesktop.DBus.Error.Disconnected\0" + "org.freedesktop.DBus.Error.InvalidArgs\0" + "org.freedesktop.DBus.Error.UnknownMethod\0" + "org.freedesktop.DBus.Error.TimedOut\0" + "org.freedesktop.DBus.Error.InvalidSignature\0" + "org.freedesktop.DBus.Error.UnknownInterface\0" + "com.trolltech.QtDBus.Error.InternalError\0" + "org.freedesktop.DBus.Error.UnknownObject\0" + "\0"; + +static const int errorMessages_indices[] = { + 0, 6, 40, 76, 118, 153, 191, 231, + 273, 313, 349, 384, 421, 461, 501, 540, + 581, 617, 661, 705, 746, 0 +}; + +static const int errorMessages_count = sizeof errorMessages_indices / + sizeof errorMessages_indices[0]; + +static inline const char *get(QDBusError::ErrorType code) +{ + int intcode = qBound(0, int(code) - int(QDBusError::Other), errorMessages_count); + return errorMessages_string + errorMessages_indices[intcode]; +} + +static inline QDBusError::ErrorType get(const char *name) +{ + if (!name || !*name) + return QDBusError::NoError; + for (int i = 0; i < errorMessages_count; ++i) + if (strcmp(name, errorMessages_string + errorMessages_indices[i]) == 0) + return QDBusError::ErrorType(i + int(QDBusError::Other)); + return QDBusError::Other; +} + +/*! + \class QDBusError + \inmodule QtDBus + \since 4.2 + + \brief The QDBusError class represents an error received from the + D-Bus bus or from remote applications found in the bus. + + When dealing with the D-Bus bus service or with remote + applications over D-Bus, a number of error conditions can + happen. This error conditions are sometimes signalled by a + returned error value or by a QDBusError. + + C++ and Java exceptions are a valid analogy for D-Bus errors: + instead of returning normally with a return value, remote + applications and the bus may decide to throw an error + condition. However, the QtDBus implementation does not use the C++ + exception-throwing mechanism, so you will receive QDBusErrors in + the return reply (see QDBusReply::error()). + + QDBusError objects are used to inspect the error name and message + as received from the bus and remote applications. You should not + create such objects yourself to signal error conditions when + called from D-Bus: instead, use QDBusMessage::createError() and + QDBusConnection::send(). + + \sa QDBusConnection::send(), QDBusMessage, QDBusReply +*/ + +/*! + \enum QDBusError::ErrorType + + In order to facilitate verification of the most common D-Bus errors generated by the D-Bus + implementation and by the bus daemon itself, QDBusError can be compared to a set of pre-defined + values: + + \value NoError QDBusError is invalid (i.e., the call succeeded) + \value Other QDBusError contains an error that is one of the well-known ones + \value Failed The call failed (\c org.freedesktop.DBus.Error.Failed) + \value NoMemory Out of memory (\c org.freedesktop.DBus.Error.NoMemory) + \value ServiceUnknown The called service is not known + (\c org.freedesktop.DBus.Error.ServiceUnknown) + \value NoReply The called method did not reply within the specified timeout + (\c org.freedesktop.DBus.Error.NoReply) + \value BadAddress The address given is not valid + (\c org.freedesktop.DBus.Error.BadAddress) + \value NotSupported The call/operation is not supported + (\c org.freedesktop.DBus.Error.NotSupported) + \value LimitsExceeded The limits allocated to this process/call/connection exceeded the + pre-defined values (\c org.freedesktop.DBus.Error.LimitsExceeded) + \value AccessDenied The call/operation tried to access a resource it isn't allowed to + (\c org.freedesktop.DBus.Error.AccessDenied) + \value NoServer \e {Documentation doesn't say what this is for} + (\c org.freedesktop.DBus.Error.NoServer) + \value Timeout \e {Documentation doesn't say what this is for or how it's used} + (\c org.freedesktop.DBus.Error.Timeout) + \value NoNetwork \e {Documentation doesn't say what this is for} + (\c org.freedesktop.DBus.Error.NoNetwork) + \value AddressInUse QDBusServer tried to bind to an address that is already in use + (\c org.freedesktop.DBus.Error.AddressInUse) + \value Disconnected The call/process/message was sent after QDBusConnection disconnected + (\c org.freedesktop.DBus.Error.Disconnected) + \value InvalidArgs The arguments passed to this call/operation are not valid + (\c org.freedesktop.DBus.Error.InvalidArgs) + \value UnknownMethod The method called was not found in this object/interface with the + given parameters (\c org.freedesktop.DBus.Error.UnknownMethod) + \value TimedOut \e {Documentation doesn't say...} + (\c org.freedesktop.DBus.Error.TimedOut) + \value InvalidSignature The type signature is not valid or compatible + (\c org.freedesktop.DBus.Error.InvalidSignature) + \value UnknownInterface The interface is not known + \value InternalError An internal error occurred + (\c com.trolltech.QtDBus.Error.InternalError) + \value UnknownObject The remote object could not be found. + +*/ + +/*! + \internal + Constructs a QDBusError from a DBusError structure. +*/ +QDBusError::QDBusError(const DBusError *error) + : code(NoError) +{ + if (!error || !q_dbus_error_is_set(error)) + return; + + code = ::get(error->name); + msg = QString::fromUtf8(error->message); + nm = QString::fromUtf8(error->name); +} + +/*! + \internal + Constructs a QDBusError from a QDBusMessage. +*/ +QDBusError::QDBusError(const QDBusMessage &qdmsg) + : code(NoError) +{ + if (qdmsg.type() != QDBusMessage::ErrorMessage) + return; + + code = ::get(qdmsg.errorName().toUtf8().constData()); + nm = qdmsg.errorName(); + msg = qdmsg.errorMessage(); +} + +/*! + \internal + Constructs a QDBusError from a well-known error code +*/ +QDBusError::QDBusError(ErrorType error, const QString &mess) + : code(error) +{ + nm = QLatin1String(::get(error)); + msg = mess; +} + +/*! + \internal + Constructs a QDBusError from another QDBusError object +*/ +QDBusError::QDBusError(const QDBusError &other) + : code(other.code), msg(other.msg), nm(other.nm) +{ +} + +/*! + \internal + Assignment operator +*/ + +QDBusError &QDBusError::operator=(const QDBusError &other) +{ + code = other.code; + msg = other.msg; + nm = other.nm; + return *this; +} + +/*! + Returns this error's ErrorType. + + \sa ErrorType +*/ + +QDBusError::ErrorType QDBusError::type() const +{ + return code; +} + +/*! + Returns this error's name. Error names are similar to D-Bus Interface names, like + \c org.freedesktop.DBus.InvalidArgs. + + \sa type() +*/ + +QString QDBusError::name() const +{ + return nm; +} + +/*! + Returns the message that the callee associated with this error. Error messages are + implementation defined and usually contain a human-readable error code, though this does not + mean it is suitable for your end-users. +*/ + +QString QDBusError::message() const +{ + return msg; +} + +/*! + Returns true if this is a valid error condition (i.e., if there was an error), + otherwise false. +*/ + +bool QDBusError::isValid() const +{ + return (code != NoError); +} + +/*! + \since 4.3 + Returns the error name associated with error condition \a error. +*/ +QString QDBusError::errorString(ErrorType error) +{ + return QLatin1String(::get(error)); +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QDBusError &msg) +{ + dbg.nospace() << "QDBusError(" << msg.name() << ", " << msg.message() << ")"; + return dbg.space(); +} +#endif + +QT_END_NAMESPACE + + diff --git a/src/dbus/qdbuserror.h b/src/dbus/qdbuserror.h new file mode 100644 index 0000000..f3de690 --- /dev/null +++ b/src/dbus/qdbuserror.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDBUSERROR_H +#define QDBUSERROR_H + +#include <QtDBus/qdbusmacros.h> +#include <QtCore/qstring.h> + +QT_BEGIN_HEADER + +struct DBusError; + +QT_BEGIN_NAMESPACE + +QT_MODULE(DBus) + +class QDBusMessage; + +class QDBUS_EXPORT QDBusError +{ +public: + enum ErrorType { + NoError = 0, + Other = 1, + Failed, + NoMemory, + ServiceUnknown, + NoReply, + BadAddress, + NotSupported, + LimitsExceeded, + AccessDenied, + NoServer, + Timeout, + NoNetwork, + AddressInUse, + Disconnected, + InvalidArgs, + UnknownMethod, + TimedOut, + InvalidSignature, + UnknownInterface, + InternalError, + UnknownObject, + +#ifndef Q_QDOC + // don't use this one! + LastErrorType = UnknownObject +#endif + }; + + QDBusError(const DBusError *error = 0); + QDBusError(const QDBusMessage& msg); + QDBusError(ErrorType error, const QString &message); + QDBusError(const QDBusError &other); + QDBusError &operator=(const QDBusError &other); + + ErrorType type() const; + QString name() const; + QString message() const; + bool isValid() const; + + static QString errorString(ErrorType error); + +private: + ErrorType code; + QString msg; + QString nm; + void *unused; +}; + +#ifndef QT_NO_DEBUG_STREAM +QDBUS_EXPORT QDebug operator<<(QDebug, const QDBusError &); +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/dbus/qdbusextratypes.cpp b/src/dbus/qdbusextratypes.cpp new file mode 100644 index 0000000..7a11480 --- /dev/null +++ b/src/dbus/qdbusextratypes.cpp @@ -0,0 +1,239 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdbusextratypes.h" +#include "qdbusutil_p.h" + +QT_BEGIN_NAMESPACE + +void QDBusObjectPath::check() +{ + if (!QDBusUtil::isValidObjectPath(*this)) { + qWarning("QDBusObjectPath: invalid path \"%s\"", qPrintable(*this)); + clear(); + } +} + +void QDBusSignature::check() +{ + if (!QDBusUtil::isValidSignature(*this)) { + qWarning("QDBusSignature: invalid signature \"%s\"", qPrintable(*this)); + clear(); + } +} + +/*! + \class QDBusVariant + \inmodule QtDBus + \since 4.2 + + \brief The QDBusVariant class enables the programmer to identify + the variant type provided by the D-Bus typesystem. + + A D-Bus function that takes an integer, a D-Bus variant and a string as parameters + can be called with the following argument list (see QDBusMessage::setArguments()): + + \snippet doc/src/snippets/qdbusextratypes/qdbusextratypes.cpp 0 + + When a D-Bus function returns a D-Bus variant, it can be retrieved as follows: + + \snippet doc/src/snippets/qdbusextratypes/qdbusextratypes.cpp 1 + + The QVariant within a QDBusVariant is required to distinguish between a normal + D-Bus value and a value within a D-Bus variant. + + \sa {The QtDBus type system} +*/ + +/*! + \fn QDBusVariant::QDBusVariant() + + Constructs a new D-Bus variant. +*/ + +/*! + \fn QDBusVariant::QDBusVariant(const QVariant &variant) + + Constructs a new D-Bus variant from the given Qt \a variant. + + \sa setVariant() +*/ + +/*! + \fn QVariant QDBusVariant::variant() const + + Returns this D-Bus variant as a QVariant object. + + \sa setVariant() +*/ + +/*! + \fn void QDBusVariant::setVariant(const QVariant &variant) + + Assigns the value of the given Qt \a variant to this D-Bus variant. + + \sa variant() +*/ + +/*! + \class QDBusObjectPath + \inmodule QtDBus + \since 4.2 + + \brief The QDBusObjectPath class enables the programmer to + identify the OBJECT_PATH type provided by the D-Bus typesystem. + + \sa {The QtDBus type system} +*/ + +/*! + \fn QDBusObjectPath::QDBusObjectPath() + + Constructs a new object path. +*/ + +/*! + \fn QDBusObjectPath::QDBusObjectPath(const char *path) + + Constructs a new object path from the given \a path. + + \sa setPath() +*/ + +/*! + \fn QDBusObjectPath::QDBusObjectPath(const QLatin1String &path) + + Constructs a new object path from the given \a path. +*/ + +/*! + \fn QDBusObjectPath::QDBusObjectPath(const QString &path) + + Constructs a new object path from the given \a path. +*/ + +/*! + \fn QString QDBusObjectPath::path() const + + Returns this object path. + + \sa setPath() +*/ + +/*! + \fn void QDBusObjectPath::setPath(const QString &path) + + Assigns the value of the given \a path to this object path. + + \sa path() +*/ + +/*! + \fn QDBusObjectPath &QDBusObjectPath::operator=(const QDBusObjectPath &path) + + Assigns the value of the given \a path to this object path. + + \sa setPath() +*/ + + +/*! + \class QDBusSignature + \inmodule QtDBus + \since 4.2 + + \brief The QDBusSignature class enables the programmer to + identify the SIGNATURE type provided by the D-Bus typesystem. + + \sa {The QtDBus type system} +*/ + +/*! + \fn QDBusSignature::QDBusSignature() + + Constructs a new signature. + + \sa setSignature() +*/ + +/*! + \fn QDBusSignature::QDBusSignature(const char *signature) + + Constructs a new signature from the given \a signature. +*/ + +/*! + \fn QDBusSignature::QDBusSignature(const QLatin1String &signature) + + Constructs a new signature from the given \a signature. +*/ + +/*! + \fn QDBusSignature::QDBusSignature(const QString &signature) + + Constructs a new signature from the given \a signature. +*/ + +/*! + \fn QString QDBusSignature::signature() const + + Returns this signature. + + \sa setSignature() +*/ + +/*! + \fn void QDBusSignature::setSignature(const QString &signature) + + Assigns the value of the given \a signature to this signature. + \sa signature() +*/ + +/*! + \fn QDBusSignature &QDBusSignature::operator=(const QDBusSignature &signature) + + Assigns the value of the given \a signature to this signature. + + \sa setSignature() +*/ + +QT_END_NAMESPACE + diff --git a/src/dbus/qdbusextratypes.h b/src/dbus/qdbusextratypes.h new file mode 100644 index 0000000..873ab51 --- /dev/null +++ b/src/dbus/qdbusextratypes.h @@ -0,0 +1,187 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDBUSEXTRATYPES_H +#define QDBUSEXTRATYPES_H + +// define some useful types for D-BUS + +#include <QtCore/qvariant.h> +#include <QtCore/qstring.h> +#include <QtDBus/qdbusmacros.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(DBus) + +// defined in qhash.cpp +Q_CORE_EXPORT uint qHash(const QString &key); + +class QDBUS_EXPORT QDBusObjectPath : private QString +{ +public: + inline QDBusObjectPath() { } + + inline explicit QDBusObjectPath(const char *path); + inline explicit QDBusObjectPath(const QLatin1String &path); + inline explicit QDBusObjectPath(const QString &path); + inline QDBusObjectPath &operator=(const QDBusObjectPath &path); + + inline void setPath(const QString &path); + + inline QString path() const + { return *this; } + +private: + void check(); +}; + +inline QDBusObjectPath::QDBusObjectPath(const char *objectPath) + : QString(QString::fromLatin1(objectPath)) +{ check(); } + +inline QDBusObjectPath::QDBusObjectPath(const QLatin1String &objectPath) + : QString(objectPath) +{ check(); } + +inline QDBusObjectPath::QDBusObjectPath(const QString &objectPath) + : QString(objectPath) +{ check(); } + +inline QDBusObjectPath &QDBusObjectPath::operator=(const QDBusObjectPath &_path) +{ QString::operator=(_path); check(); return *this; } + +inline void QDBusObjectPath::setPath(const QString &objectPath) +{ QString::operator=(objectPath); check(); } + +inline bool operator==(const QDBusObjectPath &lhs, const QDBusObjectPath &rhs) +{ return lhs.path() == rhs.path(); } + +inline bool operator!=(const QDBusObjectPath &lhs, const QDBusObjectPath &rhs) +{ return lhs.path() != rhs.path(); } + +inline bool operator<(const QDBusObjectPath &lhs, const QDBusObjectPath &rhs) +{ return lhs.path() < rhs.path(); } + +inline uint qHash(const QDBusObjectPath &objectPath) +{ return qHash(objectPath.path()); } + + +class QDBUS_EXPORT QDBusSignature : private QString +{ +public: + inline QDBusSignature() { } + + inline explicit QDBusSignature(const char *signature); + inline explicit QDBusSignature(const QLatin1String &signature); + inline explicit QDBusSignature(const QString &signature); + inline QDBusSignature &operator=(const QDBusSignature &signature); + + inline void setSignature(const QString &signature); + + inline QString signature() const + { return *this; } + +private: + void check(); +}; + +inline QDBusSignature::QDBusSignature(const char *dBusSignature) + : QString(QString::fromAscii(dBusSignature)) +{ check(); } + +inline QDBusSignature::QDBusSignature(const QLatin1String &dBusSignature) + : QString(dBusSignature) +{ check(); } + +inline QDBusSignature::QDBusSignature(const QString &dBusSignature) + : QString(dBusSignature) +{ check(); } + +inline QDBusSignature &QDBusSignature::operator=(const QDBusSignature &dbusSignature) +{ QString::operator=(dbusSignature); check(); return *this; } + +inline void QDBusSignature::setSignature(const QString &dBusSignature) +{ QString::operator=(dBusSignature); check(); } + +inline bool operator==(const QDBusSignature &lhs, const QDBusSignature &rhs) +{ return lhs.signature() == rhs.signature(); } + +inline bool operator!=(const QDBusSignature &lhs, const QDBusSignature &rhs) +{ return lhs.signature() != rhs.signature(); } + +inline bool operator<(const QDBusSignature &lhs, const QDBusSignature &rhs) +{ return lhs.signature() < rhs.signature(); } + +inline uint qHash(const QDBusSignature &signature) +{ return qHash(signature.signature()); } + +class QDBusVariant : private QVariant +{ +public: + inline QDBusVariant() { } + inline explicit QDBusVariant(const QVariant &variant); + + inline void setVariant(const QVariant &variant); + + inline QVariant variant() const + { return *this; } +}; + +inline QDBusVariant::QDBusVariant(const QVariant &dBusVariant) + : QVariant(dBusVariant) { } + +inline void QDBusVariant::setVariant(const QVariant &dBusVariant) +{ QVariant::operator=(dBusVariant); } + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QDBusVariant) +Q_DECLARE_METATYPE(QDBusObjectPath) +Q_DECLARE_METATYPE(QList<QDBusObjectPath>) +Q_DECLARE_METATYPE(QDBusSignature) +Q_DECLARE_METATYPE(QList<QDBusSignature>) + +QT_END_HEADER + +#endif diff --git a/src/dbus/qdbusintegrator.cpp b/src/dbus/qdbusintegrator.cpp new file mode 100644 index 0000000..91b9cb1 --- /dev/null +++ b/src/dbus/qdbusintegrator.cpp @@ -0,0 +1,2170 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qcoreapplication.h> +#include <qdebug.h> +#include <qmetaobject.h> +#include <qobject.h> +#include <qsocketnotifier.h> +#include <qstringlist.h> +#include <qtimer.h> +#include <qthread.h> + +#include "qdbusargument.h" +#include "qdbusconnection_p.h" +#include "qdbusinterface_p.h" +#include "qdbusmessage.h" +#include "qdbusmetatype.h" +#include "qdbusmetatype_p.h" +#include "qdbusabstractadaptor.h" +#include "qdbusabstractadaptor_p.h" +#include "qdbusutil_p.h" +#include "qdbusmessage_p.h" +#include "qdbuscontext_p.h" +#include "qdbuspendingcall_p.h" +#include "qdbusintegrator_p.h" + +#include "qdbusthreaddebug_p.h" + +QT_BEGIN_NAMESPACE + +static bool isDebugging; +#define qDBusDebug if (!::isDebugging); else qDebug + +static inline QDebug operator<<(QDebug dbg, const QThread *th) +{ + dbg.nospace() << "QThread(ptr=" << (void*)th; + if (th && !th->objectName().isEmpty()) + dbg.nospace() << ", name=" << th->objectName(); + dbg.nospace() << ")"; + return dbg.space(); +} + +#if QDBUS_THREAD_DEBUG +static inline QDebug operator<<(QDebug dbg, const QDBusConnectionPrivate *conn) +{ + dbg.nospace() << "QDBusConnection(" + << "ptr=" << (void*)conn + << ", name=" << conn->name + << ", baseService=" << conn->baseService + << ", thread="; + if (conn->thread() == QThread::currentThread()) + dbg.nospace() << "same thread"; + else + dbg.nospace() << conn->thread(); + dbg.nospace() << ")"; + return dbg.space(); +} + +Q_AUTOTEST_EXPORT void qdbusDefaultThreadDebug(int action, int condition, QDBusConnectionPrivate *conn) +{ + qDBusDebug() << QThread::currentThread() + << "QtDBus threading action" << action + << (condition == QDBusLockerBase::BeforeLock ? "before lock" : + condition == QDBusLockerBase::AfterLock ? "after lock" : + condition == QDBusLockerBase::BeforeUnlock ? "before unlock" : + condition == QDBusLockerBase::AfterUnlock ? "after unlock" : + condition == QDBusLockerBase::BeforePost ? "before event posting" : + condition == QDBusLockerBase::AfterPost ? "after event posting" : + condition == QDBusLockerBase::BeforeDeliver ? "before event delivery" : + condition == QDBusLockerBase::AfterDeliver ? "after event delivery" : + condition == QDBusLockerBase::BeforeAcquire ? "before acquire" : + condition == QDBusLockerBase::AfterAcquire ? "after acquire" : + condition == QDBusLockerBase::BeforeRelease ? "before release" : + condition == QDBusLockerBase::AfterRelease ? "after release" : + "condition unknown") + << "in connection" << conn; +} +Q_AUTOTEST_EXPORT qdbusThreadDebugFunc qdbusThreadDebug = 0; +#endif + +typedef void (*QDBusSpyHook)(const QDBusMessage&); +typedef QVarLengthArray<QDBusSpyHook, 4> QDBusSpyHookList; +Q_GLOBAL_STATIC(QDBusSpyHookList, qDBusSpyHookList) + +extern "C" { + + // libdbus-1 callbacks + +static bool qDBusRealAddTimeout(QDBusConnectionPrivate *d, DBusTimeout *timeout, int ms); +static dbus_bool_t qDBusAddTimeout(DBusTimeout *timeout, void *data) +{ + Q_ASSERT(timeout); + Q_ASSERT(data); + + // qDebug("addTimeout %d", q_dbus_timeout_get_interval(timeout)); + + QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); + + if (!q_dbus_timeout_get_enabled(timeout)) + return true; + + if (QCoreApplication::instance() && QThread::currentThread() == d->thread()) { + // correct thread + return qDBusRealAddTimeout(d, timeout, q_dbus_timeout_get_interval(timeout)); + } else { + // wrong thread: sync back + QDBusConnectionCallbackEvent *ev = new QDBusConnectionCallbackEvent; + ev->subtype = QDBusConnectionCallbackEvent::AddTimeout; + d->timeoutsPendingAdd.append(qMakePair(timeout, q_dbus_timeout_get_interval(timeout))); + d->postEventToThread(AddTimeoutAction, d, ev); + return true; + } +} + +static bool qDBusRealAddTimeout(QDBusConnectionPrivate *d, DBusTimeout *timeout, int ms) +{ + QDBusWatchAndTimeoutLocker locker(AddTimeoutAction, d); + Q_ASSERT(d->timeouts.keys(timeout).isEmpty()); + + int timerId = d->startTimer(ms); + if (!timerId) + return false; + + d->timeouts[timerId] = timeout; + return true; +} + +static void qDBusRemoveTimeout(DBusTimeout *timeout, void *data) +{ + Q_ASSERT(timeout); + Q_ASSERT(data); + + // qDebug("removeTimeout"); + + QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); + + QDBusWatchAndTimeoutLocker locker(RemoveTimeoutAction, d); + + // is it pending addition? + QDBusConnectionPrivate::PendingTimeoutList::iterator pit = d->timeoutsPendingAdd.begin(); + while (pit != d->timeoutsPendingAdd.end()) { + if (pit->first == timeout) + pit = d->timeoutsPendingAdd.erase(pit); + else + ++pit; + } + + // is it a running timer? + bool correctThread = QCoreApplication::instance() && QThread::currentThread() == d->thread(); + QDBusConnectionPrivate::TimeoutHash::iterator it = d->timeouts.begin(); + while (it != d->timeouts.end()) { + if (it.value() == timeout) { + if (correctThread) { + // correct thread + d->killTimer(it.key()); + } else { + // incorrect thread or no application, post an event for later + QDBusConnectionCallbackEvent *ev = new QDBusConnectionCallbackEvent; + ev->subtype = QDBusConnectionCallbackEvent::KillTimer; + ev->timerId = it.key(); + d->postEventToThread(KillTimerAction, d, ev); + } + it = d->timeouts.erase(it); + break; + } else { + ++it; + } + } +} + +static void qDBusToggleTimeout(DBusTimeout *timeout, void *data) +{ + Q_ASSERT(timeout); + Q_ASSERT(data); + + //qDebug("ToggleTimeout"); + + qDBusRemoveTimeout(timeout, data); + qDBusAddTimeout(timeout, data); +} + +static bool qDBusRealAddWatch(QDBusConnectionPrivate *d, DBusWatch *watch, int flags, int fd); +static dbus_bool_t qDBusAddWatch(DBusWatch *watch, void *data) +{ + Q_ASSERT(watch); + Q_ASSERT(data); + + QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); + + int flags = q_dbus_watch_get_flags(watch); + int fd = q_dbus_watch_get_fd(watch); + + if (QCoreApplication::instance() && QThread::currentThread() == d->thread()) { + return qDBusRealAddWatch(d, watch, flags, fd); + } else { + QDBusConnectionCallbackEvent *ev = new QDBusConnectionCallbackEvent; + ev->subtype = QDBusConnectionCallbackEvent::AddWatch; + ev->watch = watch; + ev->fd = fd; + ev->extra = flags; + d->postEventToThread(AddWatchAction, d, ev); + return true; + } +} + +static bool qDBusRealAddWatch(QDBusConnectionPrivate *d, DBusWatch *watch, int flags, int fd) +{ + QDBusConnectionPrivate::Watcher watcher; + + QDBusWatchAndTimeoutLocker locker(AddWatchAction, d); + if (flags & DBUS_WATCH_READABLE) { + //qDebug("addReadWatch %d", fd); + watcher.watch = watch; + if (QCoreApplication::instance()) { + watcher.read = new QSocketNotifier(fd, QSocketNotifier::Read, d); + watcher.read->setEnabled(q_dbus_watch_get_enabled(watch)); + d->connect(watcher.read, SIGNAL(activated(int)), SLOT(socketRead(int))); + } + } + if (flags & DBUS_WATCH_WRITABLE) { + //qDebug("addWriteWatch %d", fd); + watcher.watch = watch; + if (QCoreApplication::instance()) { + watcher.write = new QSocketNotifier(fd, QSocketNotifier::Write, d); + watcher.write->setEnabled(q_dbus_watch_get_enabled(watch)); + d->connect(watcher.write, SIGNAL(activated(int)), SLOT(socketWrite(int))); + } + } + d->watchers.insertMulti(fd, watcher); + + return true; +} + +static void qDBusRemoveWatch(DBusWatch *watch, void *data) +{ + Q_ASSERT(watch); + Q_ASSERT(data); + + //qDebug("remove watch"); + + QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); + int fd = q_dbus_watch_get_fd(watch); + + QDBusWatchAndTimeoutLocker locker(RemoveWatchAction, d); + QDBusConnectionPrivate::WatcherHash::iterator i = d->watchers.find(fd); + while (i != d->watchers.end() && i.key() == fd) { + if (i.value().watch == watch) { + if (QCoreApplication::instance() && QThread::currentThread() == d->thread()) { + // correct thread, delete the socket notifiers + delete i.value().read; + delete i.value().write; + } else { + // incorrect thread or no application, use delete later + if (i->read) + i->read->deleteLater(); + if (i->write) + i->write->deleteLater(); + } + i = d->watchers.erase(i); + } else { + ++i; + } + } +} + +static void qDBusRealToggleWatch(QDBusConnectionPrivate *d, DBusWatch *watch, int fd); +static void qDBusToggleWatch(DBusWatch *watch, void *data) +{ + Q_ASSERT(watch); + Q_ASSERT(data); + + QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); + int fd = q_dbus_watch_get_fd(watch); + + if (QCoreApplication::instance() && QThread::currentThread() == d->thread()) { + qDBusRealToggleWatch(d, watch, fd); + } else { + QDBusConnectionCallbackEvent *ev = new QDBusConnectionCallbackEvent; + ev->subtype = QDBusConnectionCallbackEvent::ToggleWatch; + ev->watch = watch; + ev->fd = fd; + d->postEventToThread(ToggleWatchAction, d, ev); + } +} + +static void qDBusRealToggleWatch(QDBusConnectionPrivate *d, DBusWatch *watch, int fd) +{ + QDBusWatchAndTimeoutLocker locker(ToggleWatchAction, d); + + QDBusConnectionPrivate::WatcherHash::iterator i = d->watchers.find(fd); + while (i != d->watchers.end() && i.key() == fd) { + if (i.value().watch == watch) { + bool enabled = q_dbus_watch_get_enabled(watch); + int flags = q_dbus_watch_get_flags(watch); + + //qDebug("toggle watch %d to %d (write: %d, read: %d)", q_dbus_watch_get_fd(watch), enabled, flags & DBUS_WATCH_WRITABLE, flags & DBUS_WATCH_READABLE); + + if (flags & DBUS_WATCH_READABLE && i.value().read) + i.value().read->setEnabled(enabled); + if (flags & DBUS_WATCH_WRITABLE && i.value().write) + i.value().write->setEnabled(enabled); + return; + } + ++i; + } +} + +static void qDBusUpdateDispatchStatus(DBusConnection *connection, DBusDispatchStatus new_status, void *data) +{ + Q_ASSERT(connection); + Q_UNUSED(connection); + QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); + + static int slotId; // 0 is QObject::deleteLater() + if (!slotId) { + // it's ok to do this: there's no race condition because the store is atomic + // and we always set to the same value + slotId = QDBusConnectionPrivate::staticMetaObject.indexOfSlot("doDispatch()"); + } + + //qDBusDebug() << "Updating dispatcher status" << slotId; + if (new_status == DBUS_DISPATCH_DATA_REMAINS) + QDBusConnectionPrivate::staticMetaObject.method(slotId). + invoke(d, Qt::QueuedConnection); +} + +static void qDBusNewConnection(DBusServer *server, DBusConnection *connection, void *data) +{ + // ### We may want to separate the server from the QDBusConnectionPrivate + Q_ASSERT(server); Q_UNUSED(server); + Q_ASSERT(connection); + Q_ASSERT(data); + + // keep the connection alive + q_dbus_connection_ref(connection); + QDBusConnectionPrivate *d = new QDBusConnectionPrivate; + + // setConnection does the error handling for us + QDBusErrorInternal error; + d->setPeer(connection, error); + + QDBusConnection retval = QDBusConnectionPrivate::q(d); + d->setBusService(retval); + + //d->name = QString::number(reinterpret_cast<int>(d)); + //d->setConnection(d->name, d); + + // make QDBusServer emit the newConnection signal + QDBusConnectionPrivate *server_d = static_cast<QDBusConnectionPrivate *>(data); + server_d->serverConnection(retval); +} + +} // extern "C" + +static QByteArray buildMatchRule(const QString &service, const QString & /*owner*/, + const QString &objectPath, const QString &interface, + const QString &member, const QString & /*signature*/) +{ + QString result = QLatin1String("type='signal',"); + QString keyValue = QLatin1String("%1='%2',"); + + if (!service.isEmpty()) + result += keyValue.arg(QLatin1String("sender"), service); + if (!objectPath.isEmpty()) + result += keyValue.arg(QLatin1String("path"), objectPath); + if (!interface.isEmpty()) + result += keyValue.arg(QLatin1String("interface"), interface); + if (!member.isEmpty()) + result += keyValue.arg(QLatin1String("member"), member); + + result.chop(1); // remove ending comma + return result.toLatin1(); +} + +static bool findObject(const QDBusConnectionPrivate::ObjectTreeNode *root, + const QString &fullpath, int &usedLength, + QDBusConnectionPrivate::ObjectTreeNode &result) +{ + int start = 0; + int length = fullpath.length(); + if (fullpath.at(0) == QLatin1Char('/')) + start = 1; + + // walk the object tree + const QDBusConnectionPrivate::ObjectTreeNode *node = root; + while (start < length && node && !(node->flags & QDBusConnection::ExportChildObjects)) { + int end = fullpath.indexOf(QLatin1Char('/'), start); + end = (end == -1 ? length : end); + QStringRef pathComponent(&fullpath, start, end - start); + + QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator it = + qLowerBound(node->children.constBegin(), node->children.constEnd(), pathComponent); + if (it != node->children.constEnd() && it->name == pathComponent) + // match + node = it; + else + node = 0; + + start = end + 1; + } + + // found our object + usedLength = (start > length ? length : start); + if (node) { + if (node->obj || !node->children.isEmpty()) + result = *node; + else + // there really is no object here + // we're just looking at an unused space in the QVector + node = 0; + } + return node; +} + +static QObject *findChildObject(const QDBusConnectionPrivate::ObjectTreeNode *root, + const QString &fullpath, int start) +{ + int length = fullpath.length(); + + // any object in the tree can tell us to switch to its own object tree: + const QDBusConnectionPrivate::ObjectTreeNode *node = root; + if (node && node->flags & QDBusConnection::ExportChildObjects) { + QObject *obj = node->obj; + + while (obj) { + if (start >= length) + // we're at the correct level + return obj; + + int pos = fullpath.indexOf(QLatin1Char('/'), start); + pos = (pos == -1 ? length : pos); + QStringRef pathComponent(&fullpath, start, pos - start); + + const QObjectList children = obj->children(); + + // find a child with the proper name + QObject *next = 0; + QObjectList::ConstIterator it = children.constBegin(); + QObjectList::ConstIterator end = children.constEnd(); + for ( ; it != end; ++it) + if ((*it)->objectName() == pathComponent) { + next = *it; + break; + } + + if (!next) + break; + + obj = next; + start = pos + 1; + } + } + + // object not found + return 0; +} + +extern QDBUS_EXPORT void qDBusAddSpyHook(QDBusSpyHook); +void qDBusAddSpyHook(QDBusSpyHook hook) +{ + qDBusSpyHookList()->append(hook); +} + +extern "C" { +static DBusHandlerResult +qDBusSignalFilter(DBusConnection *connection, DBusMessage *message, void *data) +{ + Q_ASSERT(data); + Q_UNUSED(connection); + QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); + if (d->mode == QDBusConnectionPrivate::InvalidMode) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + QDBusMessage amsg = QDBusMessagePrivate::fromDBusMessage(message); + qDBusDebug() << QThread::currentThread() << "got message:" << amsg; + + return d->handleMessage(amsg) ? + DBUS_HANDLER_RESULT_HANDLED : + DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} +} + +bool QDBusConnectionPrivate::handleMessage(const QDBusMessage &amsg) +{ + const QDBusSpyHookList *list = qDBusSpyHookList(); + for (int i = 0; i < list->size(); ++i) { + qDBusDebug() << "calling the message spy hook"; + (*(*list)[i])(amsg); + } + + switch (amsg.type()) { + case QDBusMessage::SignalMessage: + handleSignal(amsg); + return true; + break; + case QDBusMessage::MethodCallMessage: + handleObjectCall(amsg); + return true; + case QDBusMessage::ReplyMessage: + case QDBusMessage::ErrorMessage: + return false; // we don't handle those here + case QDBusMessage::InvalidMessage: + Q_ASSERT_X(false, "QDBusConnection", "Invalid message found when processing"); + break; + } + + return false; +} + +static void huntAndDestroy(QObject *needle, QDBusConnectionPrivate::ObjectTreeNode &haystack) +{ + QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator it = haystack.children.begin(); + QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator end = haystack.children.end(); + for ( ; it != end; ++it) + huntAndDestroy(needle, *it); + + if (needle == haystack.obj) { + haystack.obj = 0; + haystack.flags = 0; + } +} + +static void huntAndEmit(DBusConnection *connection, DBusMessage *msg, + QObject *needle, const QDBusConnectionPrivate::ObjectTreeNode &haystack, + bool isScriptable, bool isAdaptor, const QString &path = QString()) +{ + QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator it = haystack.children.constBegin(); + QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator end = haystack.children.constEnd(); + for ( ; it != end; ++it) + huntAndEmit(connection, msg, needle, *it, isScriptable, isAdaptor, path + QLatin1String("/") + it->name); + + if (needle == haystack.obj) { + // is this a signal we should relay? + if (isAdaptor && (haystack.flags & QDBusConnection::ExportAdaptors) == 0) + return; // no: it comes from an adaptor and we're not exporting adaptors + else if (!isAdaptor) { + int mask = isScriptable + ? QDBusConnection::ExportScriptableSignals + : QDBusConnection::ExportNonScriptableSignals; + if ((haystack.flags & mask) == 0) + return; // signal was not exported + } + + QByteArray p = path.toLatin1(); + if (p.isEmpty()) + p = "/"; + qDBusDebug() << QThread::currentThread() << "emitting signal at" << p; + DBusMessage *msg2 = q_dbus_message_copy(msg); + q_dbus_message_set_path(msg2, p); + q_dbus_connection_send(connection, msg2, 0); + q_dbus_message_unref(msg2); + } +} + +static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags, + const QString &signature_, QList<int>& metaTypes) +{ + QByteArray msgSignature = signature_.toLatin1(); + + for (int idx = mo->methodCount() - 1 ; idx >= QObject::staticMetaObject.methodCount(); --idx) { + QMetaMethod mm = mo->method(idx); + + // check access: + if (mm.access() != QMetaMethod::Public) + continue; + + // check type: + if (mm.methodType() != QMetaMethod::Slot) + continue; + + // check name: + QByteArray slotname = mm.signature(); + int paren = slotname.indexOf('('); + if (paren != name.length() || !slotname.startsWith(name)) + continue; + + int returnType = qDBusNameToTypeId(mm.typeName()); + bool isAsync = qDBusCheckAsyncTag(mm.tag()); + bool isScriptable = mm.attributes() & QMetaMethod::Scriptable; + + // consistency check: + if (isAsync && returnType != QMetaType::Void) + continue; + + int inputCount = qDBusParametersForMethod(mm, metaTypes); + if (inputCount == -1) + continue; // problem parsing + + metaTypes[0] = returnType; + bool hasMessage = false; + if (inputCount > 0 && + metaTypes.at(inputCount) == QDBusMetaTypeId::message) { + // "no input parameters" is allowed as long as the message meta type is there + hasMessage = true; + --inputCount; + } + + // try to match the parameters + int i; + QByteArray reconstructedSignature; + for (i = 1; i <= inputCount; ++i) { + const char *typeSignature = QDBusMetaType::typeToSignature( metaTypes.at(i) ); + if (!typeSignature) + break; // invalid + + reconstructedSignature += typeSignature; + if (!msgSignature.startsWith(reconstructedSignature)) + break; + } + + if (reconstructedSignature != msgSignature) + continue; // we didn't match them all + + if (hasMessage) + ++i; + + // make sure that the output parameters have signatures too + if (returnType != 0 && QDBusMetaType::typeToSignature(returnType) == 0) + continue; + + bool ok = true; + for (int j = i; ok && j < metaTypes.count(); ++j) + if (QDBusMetaType::typeToSignature(metaTypes.at(i)) == 0) + ok = false; + if (!ok) + continue; + + // consistency check: + if (isAsync && metaTypes.count() > i + 1) + continue; + + if (isScriptable && (flags & QDBusConnection::ExportScriptableSlots) == 0) + continue; // not exported + if (!isScriptable && (flags & QDBusConnection::ExportNonScriptableSlots) == 0) + continue; // not exported + + // if we got here, this slot matched + return idx; + } + + // no slot matched + return -1; +} + +QDBusCallDeliveryEvent* QDBusConnectionPrivate::prepareReply(QDBusConnectionPrivate *target, + QObject *object, int idx, + const QList<int> &metaTypes, + const QDBusMessage &msg) +{ + Q_ASSERT(object); + Q_UNUSED(object); + + int n = metaTypes.count() - 1; + if (metaTypes[n] == QDBusMetaTypeId::message) + --n; + + // check that types match + for (int i = 0; i < n; ++i) + if (metaTypes.at(i + 1) != msg.arguments().at(i).userType() && + msg.arguments().at(i).userType() != qMetaTypeId<QDBusArgument>()) + return 0; // no match + + // we can deliver + // prepare for the call + return new QDBusCallDeliveryEvent(QDBusConnection(target), idx, target, msg, metaTypes); +} + +void QDBusConnectionPrivate::activateSignal(const QDBusConnectionPrivate::SignalHook& hook, + const QDBusMessage &msg) +{ + // This is called by QDBusConnectionPrivate::handleSignal to deliver a signal + // that was received from D-Bus + // + // Signals are delivered to slots if the parameters match + // Slots can have less parameters than there are on the message + // Slots can optionally have one final parameter that is a QDBusMessage + // Slots receive read-only copies of the message (i.e., pass by value or by const-ref) + QDBusCallDeliveryEvent *call = prepareReply(this, hook.obj, hook.midx, hook.params, msg); + if (call) + postEventToThread(ActivateSignalAction, hook.obj, call); +} + +bool QDBusConnectionPrivate::activateCall(QObject* object, int flags, const QDBusMessage &msg) +{ + // This is called by QDBusConnectionPrivate::handleObjectCall to place a call + // to a slot on the object. + // + // The call is delivered to the first slot that matches the following conditions: + // - has the same name as the message's target member + // - ALL of the message's types are found in slot's parameter list + // - optionally has one more parameter of type QDBusMessage + // If none match, then the slot of the same name as the message target and with + // the first type of QDBusMessage is delivered. + // + // The D-Bus specification requires that all MethodCall messages be replied to, unless the + // caller specifically waived this requirement. This means that we inspect if the user slot + // generated a reply and, if it didn't, we will. Obviously, if the user slot doesn't take a + // QDBusMessage parameter, it cannot generate a reply. + // + // When a return message is generated, the slot's return type, if any, will be placed + // in the message's first position. If there are non-const reference parameters to the + // slot, they must appear at the end and will be placed in the subsequent message + // positions. + + static const char cachePropertyName[] = "_qdbus_slotCache"; + + if (!object) + return false; + + Q_ASSERT_X(QThread::currentThread() == object->thread(), + "QDBusConnection: internal threading error", + "function called for an object that is in another thread!!"); + + QDBusSlotCache slotCache = + qvariant_cast<QDBusSlotCache>(object->property(cachePropertyName)); + QString cacheKey = msg.member(), signature = msg.signature(); + if (!signature.isEmpty()) { + cacheKey.reserve(cacheKey.length() + 1 + signature.length()); + cacheKey += QLatin1Char('.'); + cacheKey += signature; + } + + QDBusSlotCache::Hash::ConstIterator cacheIt = slotCache.hash.constFind(cacheKey); + while (cacheIt != slotCache.hash.constEnd() && cacheIt->flags != flags && + cacheIt.key() == cacheKey) + ++cacheIt; + if (cacheIt == slotCache.hash.constEnd() || cacheIt.key() != cacheKey) + { + // not cached, analyse the meta object + const QMetaObject *mo = object->metaObject(); + QByteArray memberName = msg.member().toUtf8(); + + // find a slot that matches according to the rules above + QDBusSlotCache::Data slotData; + slotData.flags = flags; + slotData.slotIdx = ::findSlot(mo, memberName, flags, msg.signature(), slotData.metaTypes); + if (slotData.slotIdx == -1) { + // ### this is where we want to add the connection as an arg too + // try with no parameters, but with a QDBusMessage + slotData.slotIdx = ::findSlot(mo, memberName, flags, QString(), slotData.metaTypes); + if (slotData.metaTypes.count() != 2 || + slotData.metaTypes.at(1) != QDBusMetaTypeId::message) { + // not found + // save the negative lookup + slotData.slotIdx = -1; + slotData.metaTypes.clear(); + slotCache.hash.insert(cacheKey, slotData); + object->setProperty(cachePropertyName, qVariantFromValue(slotCache)); + return false; + } + } + + // save to the cache + slotCache.hash.insert(cacheKey, slotData); + object->setProperty(cachePropertyName, qVariantFromValue(slotCache)); + + // found the slot to be called + deliverCall(object, flags, msg, slotData.metaTypes, slotData.slotIdx); + return true; + } else if (cacheIt->slotIdx == -1) { + // negative cache + return false; + } else { + // use the cache + deliverCall(object, flags, msg, cacheIt->metaTypes, cacheIt->slotIdx); + return true; + } +} + +void QDBusConnectionPrivate::deliverCall(QObject *object, int /*flags*/, const QDBusMessage &msg, + const QList<int> &metaTypes, int slotIdx) +{ + Q_ASSERT_X(!object || QThread::currentThread() == object->thread(), + "QDBusConnection: internal threading error", + "function called for an object that is in another thread!!"); + + QVarLengthArray<void *, 10> params; + params.reserve(metaTypes.count()); + + QVariantList auxParameters; + // let's create the parameter list + + // first one is the return type -- add it below + params.append(0); + + // add the input parameters + int i; + int pCount = qMin(msg.arguments().count(), metaTypes.count() - 1); + for (i = 1; i <= pCount; ++i) { + int id = metaTypes[i]; + if (id == QDBusMetaTypeId::message) + break; + + const QVariant &arg = msg.arguments().at(i - 1); + if (arg.userType() == id) + // no conversion needed + params.append(const_cast<void *>(arg.constData())); + else if (arg.userType() == qMetaTypeId<QDBusArgument>()) { + // convert to what the function expects + void *null = 0; + auxParameters.append(QVariant(id, null)); + + const QDBusArgument &in = + *reinterpret_cast<const QDBusArgument *>(arg.constData()); + QVariant &out = auxParameters[auxParameters.count() - 1]; + + if (!QDBusMetaType::demarshall(in, out.userType(), out.data())) + qFatal("Internal error: demarshalling function for type '%s' (%d) failed!", + out.typeName(), out.userType()); + + params.append(const_cast<void *>(out.constData())); + } else { + qFatal("Internal error: got invalid meta type %d (%s) " + "when trying to convert to meta type %d (%s)", + arg.userType(), QMetaType::typeName(arg.userType()), + id, QMetaType::typeName(id)); + } + } + + bool takesMessage = false; + if (metaTypes.count() > i && metaTypes[i] == QDBusMetaTypeId::message) { + params.append(const_cast<void*>(static_cast<const void*>(&msg))); + takesMessage = true; + ++i; + } + + // output arguments + QVariantList outputArgs; + void *null = 0; + if (metaTypes[0] != QMetaType::Void) { + QVariant arg(metaTypes[0], null); + outputArgs.append( arg ); + params[0] = const_cast<void*>(outputArgs.at( outputArgs.count() - 1 ).constData()); + } + for ( ; i < metaTypes.count(); ++i) { + QVariant arg(metaTypes[i], null); + outputArgs.append( arg ); + params.append(const_cast<void*>(outputArgs.at( outputArgs.count() - 1 ).constData())); + } + + // make call: + bool fail; + if (!object) { + fail = true; + } else { + // FIXME: save the old sender! + QDBusContextPrivate context(QDBusConnection(this), msg); + QDBusContextPrivate *old = QDBusContextPrivate::set(object, &context); + QDBusConnectionPrivate::setSender(this); + + QPointer<QObject> ptr = object; + fail = object->qt_metacall(QMetaObject::InvokeMetaMethod, + slotIdx, params.data()) >= 0; + QDBusConnectionPrivate::setSender(0); + // the object might be deleted in the slot + if (!ptr.isNull()) + QDBusContextPrivate::set(object, old); + } + + // do we create a reply? Only if the caller is waiting for a reply and one hasn't been sent + // yet. + if (msg.isReplyRequired() && !msg.isDelayedReply()) { + if (!fail) { + // normal reply + qDBusDebug() << QThread::currentThread() << "Automatically sending reply:" << outputArgs; + send(msg.createReply(outputArgs)); + } else { + // generate internal error + qWarning("Internal error: Failed to deliver message"); + send(msg.createErrorReply(QDBusError::InternalError, + QLatin1String("Failed to deliver message"))); + } + } + + return; +} + +extern bool qDBusInitThreads(); + +QDBusConnectionPrivate::QDBusConnectionPrivate(QObject *p) + : QObject(p), ref(1), mode(InvalidMode), connection(0), server(0), busService(0), + watchAndTimeoutLock(QMutex::Recursive), + rootNode(QString(QLatin1Char('/'))) +{ + static const bool threads = qDBusInitThreads(); + static const int debugging = qgetenv("QDBUS_DEBUG").toInt(); + Q_UNUSED(threads) + + ::isDebugging = debugging; +#ifdef QDBUS_THREAD_DEBUG + if (debugging > 1) + qdbusThreadDebug = qdbusDefaultThreadDebug; +#endif + + QDBusMetaTypeId::init(); + + rootNode.flags = 0; + + connect(this, SIGNAL(serviceOwnerChanged(QString,QString,QString)), + this, SLOT(_q_serviceOwnerChanged(QString,QString,QString))); +} + +QDBusConnectionPrivate::~QDBusConnectionPrivate() +{ + if (thread() && thread() != QThread::currentThread()) + qWarning("QDBusConnection(name=\"%s\")'s last reference in not in its creation thread! " + "Timer and socket errors will follow and the program will probably crash", + qPrintable(name)); + + closeConnection(); + rootNode.children.clear(); // free resources + qDeleteAll(cachedMetaObjects); + + if (server) + q_dbus_server_unref(server); + if (connection) + q_dbus_connection_unref(connection); + + connection = 0; + server = 0; +} + +void QDBusConnectionPrivate::deleteYourself() +{ + if (thread() && thread() != QThread::currentThread()) { + // last reference dropped while not in the correct thread + // ask the correct thread to delete + + // note: since we're posting an event to another thread, we + // must consider deleteLater() to take effect immediately + deleteLater(); + } else { + delete this; + } +} + +void QDBusConnectionPrivate::closeConnection() +{ + QDBusWriteLocker locker(CloseConnectionAction, this); + ConnectionMode oldMode = mode; + mode = InvalidMode; // prevent reentrancy + baseService.clear(); + + if (oldMode == ServerMode) { + if (server) { + q_dbus_server_disconnect(server); + } + } else if (oldMode == ClientMode || oldMode == PeerMode) { + if (connection) { + q_dbus_connection_close(connection); + // send the "close" message + while (q_dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS) + ; + } + } +} + +void QDBusConnectionPrivate::checkThread() +{ + if (!thread()) { + if (QCoreApplication::instance()) + moveToThread(QCoreApplication::instance()->thread()); + else + qWarning("The thread that had QDBusConnection('%s') has died and there is no main thread", + qPrintable(name)); + } +} + +bool QDBusConnectionPrivate::handleError(const QDBusErrorInternal &error) +{ + if (!error) + return false; // no error + + //lock.lockForWrite(); + lastError = error; + //lock.unlock(); + return true; +} + +void QDBusConnectionPrivate::timerEvent(QTimerEvent *e) +{ + { + QDBusWatchAndTimeoutLocker locker(TimerEventAction, this); + DBusTimeout *timeout = timeouts.value(e->timerId(), 0); + if (timeout) + q_dbus_timeout_handle(timeout); + } + + doDispatch(); +} + +void QDBusConnectionPrivate::customEvent(QEvent *e) +{ + Q_ASSERT(e->type() == QEvent::User); + + QDBusConnectionCallbackEvent *ev = static_cast<QDBusConnectionCallbackEvent *>(e); + QDBusLockerBase::reportThreadAction(int(AddTimeoutAction) + int(ev->subtype), + QDBusLockerBase::BeforeDeliver, this); + switch (ev->subtype) + { + case QDBusConnectionCallbackEvent::AddTimeout: + while (!timeoutsPendingAdd.isEmpty()) { + QPair<DBusTimeout *, int> entry = timeoutsPendingAdd.takeFirst(); + qDBusRealAddTimeout(this, entry.first, entry.second); + } + break; + + case QDBusConnectionCallbackEvent::KillTimer: + qDebug() << QThread::currentThread() << "RemoveTimeout: killing timer" << (ev->timerId & 0xffffff); + killTimer(ev->timerId); + break; + + case QDBusConnectionCallbackEvent::AddWatch: + qDBusRealAddWatch(this, ev->watch, ev->extra, ev->fd); + break; + + case QDBusConnectionCallbackEvent::ToggleWatch: + qDBusRealToggleWatch(this, ev->watch, ev->fd); + break; + } + QDBusLockerBase::reportThreadAction(int(AddTimeoutAction) + int(ev->subtype), + QDBusLockerBase::AfterDeliver, this); +} + +void QDBusConnectionPrivate::doDispatch() +{ + QDBusDispatchLocker locker(DoDispatchAction, this); + if (mode == ClientMode || mode == PeerMode) + while (q_dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS) ; +} + +void QDBusConnectionPrivate::socketRead(int fd) +{ + QVarLengthArray<DBusWatch *, 2> pendingWatches; + + { + QDBusWatchAndTimeoutLocker locker(SocketReadAction, this); + WatcherHash::ConstIterator it = watchers.constFind(fd); + while (it != watchers.constEnd() && it.key() == fd) { + if (it->watch && it->read && it->read->isEnabled()) + pendingWatches.append(it.value().watch); + ++it; + } + } + + for (int i = 0; i < pendingWatches.size(); ++i) + if (!q_dbus_watch_handle(pendingWatches[i], DBUS_WATCH_READABLE)) + qDebug("OUT OF MEM"); + doDispatch(); +} + +void QDBusConnectionPrivate::socketWrite(int fd) +{ + QVarLengthArray<DBusWatch *, 2> pendingWatches; + + { + QDBusWatchAndTimeoutLocker locker(SocketWriteAction, this); + WatcherHash::ConstIterator it = watchers.constFind(fd); + while (it != watchers.constEnd() && it.key() == fd) { + if (it->watch && it->write && it->write->isEnabled()) + pendingWatches.append(it.value().watch); + ++it; + } + } + + for (int i = 0; i < pendingWatches.size(); ++i) + if (!q_dbus_watch_handle(pendingWatches[i], DBUS_WATCH_READABLE)) + qDebug("OUT OF MEM"); +} + +void QDBusConnectionPrivate::objectDestroyed(QObject *obj) +{ + QDBusWriteLocker locker(ObjectDestroyedAction, this); + huntAndDestroy(obj, rootNode); + + SignalHookHash::iterator sit = signalHooks.begin(); + while (sit != signalHooks.end()) { + if (static_cast<QObject *>(sit.value().obj) == obj) + sit = disconnectSignal(sit); + else + ++sit; + } + + obj->disconnect(this); +} + +void QDBusConnectionPrivate::relaySignal(QObject *obj, const QMetaObject *mo, int signalId, + const QVariantList &args) +{ + int mciid = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTERFACE); + Q_ASSERT(mciid != -1); + + QMetaClassInfo mci = mo->classInfo(mciid); + Q_ASSERT(mci.value()); + const char *interface = mci.value(); + + QMetaMethod mm = mo->method(signalId); + QByteArray memberName = mm.signature(); + memberName.truncate(memberName.indexOf('(')); + + // check if it's scriptable + bool isScriptable = mm.attributes() & QMetaMethod::Scriptable; + bool isAdaptor = false; + for ( ; mo; mo = mo->superClass()) + if (mo == &QDBusAbstractAdaptor::staticMetaObject) { + isAdaptor = true; + break; + } + + QDBusReadLocker locker(RelaySignalAction, this); + QDBusMessage message = QDBusMessage::createSignal(QLatin1String("/"), QLatin1String(interface), + QLatin1String(memberName)); + message.setArguments(args); + DBusMessage *msg = QDBusMessagePrivate::toDBusMessage(message); + if (!msg) { + qWarning("QDBusConnection: Could not emit signal %s.%s", interface, memberName.constData()); + return; + } + + //qDBusDebug() << "Emitting signal" << message; + //qDBusDebug() << "for paths:"; + q_dbus_message_set_no_reply(msg, true); // the reply would not be delivered to anything + huntAndEmit(connection, msg, obj, rootNode, isScriptable, isAdaptor); + q_dbus_message_unref(msg); +} + +void QDBusConnectionPrivate::_q_serviceOwnerChanged(const QString &name, + const QString &oldOwner, const QString &newOwner) +{ + if (oldOwner == baseService) + unregisterService(name); + if (newOwner == baseService) + registerService(name); + + QDBusWriteLocker locker(UpdateSignalHookOwnerAction, this); + QMutableHashIterator<QString, SignalHook> it(signalHooks); + it.toFront(); + while (it.hasNext()) + if (it.next().value().service == name) + it.value().owner = newOwner; +} + +int QDBusConnectionPrivate::findSlot(QObject* obj, const QByteArray &normalizedName, + QList<int> ¶ms) +{ + int midx = obj->metaObject()->indexOfMethod(normalizedName); + if (midx == -1) + return -1; + + int inputCount = qDBusParametersForMethod(obj->metaObject()->method(midx), params); + if ( inputCount == -1 || inputCount + 1 != params.count() ) + return -1; // failed to parse or invalid arguments or output arguments + + return midx; +} + +bool QDBusConnectionPrivate::prepareHook(QDBusConnectionPrivate::SignalHook &hook, QString &key, + const QString &service, const QString &owner, + const QString &path, const QString &interface, const QString &name, + QObject *receiver, const char *signal, int minMIdx, + bool buildSignature) +{ + QByteArray normalizedName = signal + 1; + hook.midx = findSlot(receiver, signal + 1, hook.params); + if (hook.midx == -1) { + normalizedName = QMetaObject::normalizedSignature(signal + 1); + hook.midx = findSlot(receiver, normalizedName, hook.params); + } + if (hook.midx < minMIdx) { + if (hook.midx == -1) + {} + return false; + } + + hook.service = service; + hook.owner = owner; // we don't care if the service has an owner yet + hook.path = path; + hook.obj = receiver; + + // build the D-Bus signal name and signature + // This should not happen for QDBusConnection::connect, use buildSignature here, since + // QDBusConnection::connect passes false and everything else uses true + QString mname = name; + if (buildSignature && mname.isNull()) { + normalizedName.truncate(normalizedName.indexOf('(')); + mname = QString::fromUtf8(normalizedName); + } + key = mname; + key.reserve(interface.length() + 1 + mname.length()); + key += QLatin1Char(':'); + key += interface; + + if (buildSignature) { + hook.signature.clear(); + for (int i = 1; i < hook.params.count(); ++i) + if (hook.params.at(i) != QDBusMetaTypeId::message) + hook.signature += QLatin1String( QDBusMetaType::typeToSignature( hook.params.at(i) ) ); + } + + hook.matchRule = buildMatchRule(service, owner, path, interface, mname, hook.signature); + return true; // connect to this signal +} + +void QDBusConnectionPrivate::sendError(const QDBusMessage &msg, QDBusError::ErrorType code) +{ + if (code == QDBusError::UnknownMethod) { + QString interfaceMsg; + if (msg.interface().isEmpty()) + interfaceMsg = QLatin1String("any interface"); + else + interfaceMsg = QString::fromLatin1("interface '%1'").arg(msg.interface()); + + send(msg.createErrorReply(code, + QString::fromLatin1("No such method '%1' in %2 at object path '%3' " + "(signature '%4')") + .arg(msg.member(), interfaceMsg, msg.path(), msg.signature()))); + } else if (code == QDBusError::UnknownInterface) { + send(msg.createErrorReply(QDBusError::UnknownInterface, + QString::fromLatin1("No such interface '%1' at object path '%2'") + .arg(msg.interface(), msg.path()))); + } else if (code == QDBusError::UnknownObject) { + send(msg.createErrorReply(QDBusError::UnknownObject, + QString::fromLatin1("No such object path '%1'").arg(msg.path()))); + } +} + +bool QDBusConnectionPrivate::activateInternalFilters(const ObjectTreeNode &node, + const QDBusMessage &msg) +{ + // object may be null + const QString interface = msg.interface(); + + if (interface.isEmpty() || interface == QLatin1String(DBUS_INTERFACE_INTROSPECTABLE)) { + if (msg.member() == QLatin1String("Introspect") && msg.signature().isEmpty()) { + //qDebug() << "QDBusConnectionPrivate::activateInternalFilters introspect" << msg.d_ptr->msg; + QDBusMessage reply = msg.createReply(qDBusIntrospectObject(node)); + send(reply); + return true; + } + + if (!interface.isEmpty()) { + sendError(msg, QDBusError::UnknownMethod); + return true; + } + } + + if (node.obj && (interface.isEmpty() || + interface == QLatin1String(DBUS_INTERFACE_PROPERTIES))) { + //qDebug() << "QDBusConnectionPrivate::activateInternalFilters properties" << msg.d_ptr->msg; + if (msg.member() == QLatin1String("Get") && msg.signature() == QLatin1String("ss")) { + QDBusMessage reply = qDBusPropertyGet(node, msg); + send(reply); + return true; + } else if (msg.member() == QLatin1String("Set") && msg.signature() == QLatin1String("ssv")) { + QDBusMessage reply = qDBusPropertySet(node, msg); + send(reply); + return true; + } else if (msg.member() == QLatin1String("GetAll") && msg.signature() == QLatin1String("s")) { + QDBusMessage reply = qDBusPropertyGetAll(node, msg); + send(reply); + return true; + } + + if (!interface.isEmpty()) { + sendError(msg, QDBusError::UnknownMethod); + return true; + } + } + + return false; +} + +void QDBusConnectionPrivate::activateObject(ObjectTreeNode &node, const QDBusMessage &msg, + int pathStartPos) +{ + // This is called by QDBusConnectionPrivate::handleObjectCall to place a call to a slot + // on the object. + // + // The call is routed through the adaptor sub-objects if we have any + + // object may be null + + if (pathStartPos != msg.path().length()) { + node.flags &= ~QDBusConnection::ExportAllSignals; + node.obj = findChildObject(&node, msg.path(), pathStartPos); + if (!node.obj) { + sendError(msg, QDBusError::UnknownObject); + return; + } + } + + QDBusAdaptorConnector *connector; + if (node.flags & QDBusConnection::ExportAdaptors && + (connector = qDBusFindAdaptorConnector(node.obj))) { + int newflags = node.flags | QDBusConnection::ExportAllSlots; + + if (msg.interface().isEmpty()) { + // place the call in all interfaces + // let the first one that handles it to work + QDBusAdaptorConnector::AdaptorMap::ConstIterator it = + connector->adaptors.constBegin(); + QDBusAdaptorConnector::AdaptorMap::ConstIterator end = + connector->adaptors.constEnd(); + + for ( ; it != end; ++it) + if (activateCall(it->adaptor, newflags, msg)) + return; + } else { + // check if we have an interface matching the name that was asked: + QDBusAdaptorConnector::AdaptorMap::ConstIterator it; + it = qLowerBound(connector->adaptors.constBegin(), connector->adaptors.constEnd(), + msg.interface()); + if (it != connector->adaptors.constEnd() && msg.interface() == QLatin1String(it->interface)) { + if (!activateCall(it->adaptor, newflags, msg)) + sendError(msg, QDBusError::UnknownMethod); + return; + } + } + } + + // no adaptors matched or were exported + // try our standard filters + if (activateInternalFilters(node, msg)) + return; // internal filters have already run or an error has been sent + + // try the object itself: + if (node.flags & (QDBusConnection::ExportScriptableSlots|QDBusConnection::ExportNonScriptableSlots)) { + bool interfaceFound = true; + if (!msg.interface().isEmpty()) { + // check if the interface name matches anything in the class hierarchy + const QMetaObject *mo = node.obj->metaObject(); + for ( ; !interfaceFound && mo != &QObject::staticMetaObject; mo = mo->superClass()) + interfaceFound = msg.interface() == qDBusInterfaceFromMetaObject(mo); + } + + if (interfaceFound) { + if (!activateCall(node.obj, node.flags, msg)) + sendError(msg, QDBusError::UnknownMethod); + return; + } + } + + // nothing matched, send an error code + if (msg.interface().isEmpty()) + sendError(msg, QDBusError::UnknownMethod); + else + sendError(msg, QDBusError::UnknownInterface); +} + +void QDBusConnectionPrivate::handleObjectCall(const QDBusMessage &msg) +{ + // if the msg is external, we were called from inside doDispatch + // that means the dispatchLock mutex is locked + // must not call out to user code in that case + // + // however, if the message is internal, handleMessage was called + // directly and no lock is in place. We can therefore call out to + // user code, if necessary + ObjectTreeNode result; + int usedLength; + QThread *objThread = 0; + QSemaphore sem; + bool semWait; + + { + QDBusReadLocker locker(HandleObjectCallAction, this); + if (!findObject(&rootNode, msg.path(), usedLength, result)) { + // qDebug("Call failed: no object found at %s", qPrintable(msg.path())); + sendError(msg, QDBusError::UnknownObject); + return; + } + + if (!result.obj) { + // no object -> no threading issues + // it's either going to be an error, or an internal filter + activateObject(result, msg, usedLength); + return; + } + + objThread = result.obj->thread(); + if (!objThread) { + send(msg.createErrorReply(QDBusError::InternalError, + QString::fromLatin1("Object '%1' (at path '%2')" + " has no thread. Cannot deliver message.") + .arg(result.obj->objectName(), msg.path()))); + return; + } + + if (!QDBusMessagePrivate::isLocal(msg)) { + // external incoming message + // post it and forget + postEventToThread(HandleObjectCallPostEventAction, result.obj, + new QDBusActivateObjectEvent(QDBusConnection(this), this, result, + usedLength, msg)); + return; + } else if (objThread != QThread::currentThread()) { + // synchronize with other thread + postEventToThread(HandleObjectCallPostEventAction, result.obj, + new QDBusActivateObjectEvent(QDBusConnection(this), this, result, + usedLength, msg, &sem)); + semWait = true; + } else { + semWait = false; + } + } // release the lock + + if (semWait) + SEM_ACQUIRE(HandleObjectCallSemaphoreAction, sem); + else + activateObject(result, msg, usedLength); +} + +QDBusActivateObjectEvent::~QDBusActivateObjectEvent() +{ + if (!handled) { + // we're being destroyed without delivering + // it means the object was deleted between posting and delivering + QDBusConnectionPrivate *that = QDBusConnectionPrivate::d(connection); + that->sendError(message, QDBusError::UnknownObject); + } + + // semaphore releasing happens in ~QMetaCallEvent +} + +int QDBusActivateObjectEvent::placeMetaCall(QObject *) +{ + QDBusConnectionPrivate *that = QDBusConnectionPrivate::d(connection); + + QDBusLockerBase::reportThreadAction(HandleObjectCallPostEventAction, + QDBusLockerBase::BeforeDeliver, that); + that->activateObject(node, message, pathStartPos); + QDBusLockerBase::reportThreadAction(HandleObjectCallPostEventAction, + QDBusLockerBase::AfterDeliver, that); + + handled = true; + return -1; +} + +void QDBusConnectionPrivate::handleSignal(const QString &key, const QDBusMessage& msg) +{ + SignalHookHash::const_iterator it = signalHooks.find(key); + SignalHookHash::const_iterator end = signalHooks.constEnd(); + //qDebug("looking for: %s", path.toLocal8Bit().constData()); + //qDBusDebug() << signalHooks.keys(); + for ( ; it != end && it.key() == key; ++it) { + const SignalHook &hook = it.value(); + if (!hook.owner.isEmpty() && hook.owner != msg.service()) + continue; + if (!hook.path.isEmpty() && hook.path != msg.path()) + continue; + if (!hook.signature.isEmpty() && hook.signature != msg.signature()) + continue; + if (hook.signature.isEmpty() && !hook.signature.isNull() && !msg.signature().isEmpty()) + continue; + + activateSignal(hook, msg); + } +} + +void QDBusConnectionPrivate::handleSignal(const QDBusMessage& msg) +{ + // We call handlesignal(QString, QDBusMessage) three times: + // one with member:interface + // one with member: + // one with :interface + // This allows us to match signals with wildcards on member or interface + // (but not both) + + QString key = msg.member(); + key.reserve(key.length() + 1 + msg.interface().length()); + key += QLatin1Char(':'); + key += msg.interface(); + + QDBusReadLocker locker(HandleSignalAction, this); + handleSignal(key, msg); // one try + + key.truncate(msg.member().length() + 1); // keep the ':' + handleSignal(key, msg); // second try + + key = QLatin1Char(':'); + key += msg.interface(); + handleSignal(key, msg); // third try +} + +static dbus_int32_t server_slot = -1; + +void QDBusConnectionPrivate::setServer(DBusServer *s, const QDBusErrorInternal &error) +{ + if (!s) { + handleError(error); + return; + } + + server = s; + mode = ServerMode; + + dbus_bool_t data_allocated = q_dbus_server_allocate_data_slot(&server_slot); + if (data_allocated && server_slot < 0) + return; + + dbus_bool_t watch_functions_set = q_dbus_server_set_watch_functions(server, + qDBusAddWatch, + qDBusRemoveWatch, + qDBusToggleWatch, + this, 0); + //qDebug() << "watch_functions_set" << watch_functions_set; + Q_UNUSED(watch_functions_set); + + dbus_bool_t time_functions_set = q_dbus_server_set_timeout_functions(server, + qDBusAddTimeout, + qDBusRemoveTimeout, + qDBusToggleTimeout, + this, 0); + //qDebug() << "time_functions_set" << time_functions_set; + Q_UNUSED(time_functions_set); + + q_dbus_server_set_new_connection_function(server, qDBusNewConnection, this, 0); + + dbus_bool_t data_set = q_dbus_server_set_data(server, server_slot, this, 0); + //qDebug() << "data_set" << data_set; + Q_UNUSED(data_set); +} + +void QDBusConnectionPrivate::setPeer(DBusConnection *c, const QDBusErrorInternal &error) +{ + if (!c) { + handleError(error); + return; + } + + connection = c; + mode = PeerMode; + + q_dbus_connection_set_exit_on_disconnect(connection, false); + q_dbus_connection_set_watch_functions(connection, + qDBusAddWatch, + qDBusRemoveWatch, + qDBusToggleWatch, + this, 0); + q_dbus_connection_set_timeout_functions(connection, + qDBusAddTimeout, + qDBusRemoveTimeout, + qDBusToggleTimeout, + this, 0); + q_dbus_connection_set_dispatch_status_function(connection, qDBusUpdateDispatchStatus, this, 0); + q_dbus_connection_add_filter(connection, + qDBusSignalFilter, + this, 0); + + QMetaObject::invokeMethod(this, "doDispatch", Qt::QueuedConnection); +} + +void QDBusConnectionPrivate::setConnection(DBusConnection *dbc, const QDBusErrorInternal &error) +{ + if (!dbc) { + handleError(error); + return; + } + + connection = dbc; + mode = ClientMode; + + q_dbus_connection_set_exit_on_disconnect(connection, false); + q_dbus_connection_set_watch_functions(connection, qDBusAddWatch, qDBusRemoveWatch, + qDBusToggleWatch, this, 0); + q_dbus_connection_set_timeout_functions(connection, qDBusAddTimeout, qDBusRemoveTimeout, + qDBusToggleTimeout, this, 0); + q_dbus_connection_set_dispatch_status_function(connection, qDBusUpdateDispatchStatus, this, 0); + + // Initialize the match rules + // We want all messages that have us as destination + // signals don't have destinations, but connectSignal() takes care of them + const char *service = q_dbus_bus_get_unique_name(connection); + if (service) { + QVarLengthArray<char, 56> filter; + filter.append("destination='", 13); + filter.append(service, qstrlen(service)); + filter.append("\'\0", 2); + + QDBusErrorInternal error; + q_dbus_bus_add_match(connection, filter.constData(), error); + if (handleError(error)) { + closeConnection(); + return; + } + + baseService = QString::fromUtf8(service); + } else { + qWarning("QDBusConnectionPrivate::SetConnection: Unable to get base service"); + } + + q_dbus_connection_add_filter(connection, qDBusSignalFilter, this, 0); + + //qDebug("base service: %s", service); + + // schedule a dispatch: + QMetaObject::invokeMethod(this, "doDispatch", Qt::QueuedConnection); +} + +extern "C"{ +static void qDBusResultReceived(DBusPendingCall *pending, void *user_data) +{ + QDBusPendingCallPrivate *call = reinterpret_cast<QDBusPendingCallPrivate *>(user_data); + Q_ASSERT(call->pending == pending); + Q_UNUSED(pending); + QDBusConnectionPrivate::processFinishedCall(call); +} +} + +void QDBusConnectionPrivate::waitForFinished(QDBusPendingCallPrivate *pcall) +{ + Q_ASSERT(pcall->pending); + QDBusDispatchLocker locker(PendingCallBlockAction, this); + q_dbus_pending_call_block(pcall->pending); + // QDBusConnectionPrivate::processFinishedCall() is called automatically +} + +void QDBusConnectionPrivate::processFinishedCall(QDBusPendingCallPrivate *call) +{ + QDBusConnectionPrivate *connection = const_cast<QDBusConnectionPrivate *>(call->connection); + + QDBusMessage &msg = call->replyMessage; + if (call->pending) { + // decode the message + DBusMessage *reply = q_dbus_pending_call_steal_reply(call->pending); + msg = QDBusMessagePrivate::fromDBusMessage(reply); + q_dbus_message_unref(reply); + } + qDBusDebug() << QThread::currentThread() << "got message reply (async):" << msg; + + // Check if the reply has the expected signature + call->checkReceivedSignature(); + + if (!call->receiver.isNull() && call->methodIdx != -1 && msg.type() == QDBusMessage::ReplyMessage) { + // Deliver the return values of a remote function call. + // + // There is only one connection and it is specified by idx + // The slot must have the same parameter types that the message does + // The slot may have less parameters than the message + // The slot may optionally have one final parameter that is QDBusMessage + // The slot receives read-only copies of the message (i.e., pass by value or by const-ref) + + QDBusCallDeliveryEvent *e = prepareReply(connection, call->receiver, call->methodIdx, + call->metaTypes, msg); + if (e) + connection->postEventToThread(MessageResultReceivedAction, call->receiver, e); + else + qDBusDebug() << "Deliver failed!"; + } + + // Are there any watchers? + if (call->watcherHelper) + call->watcherHelper->emitSignals(msg, call->sentMessage); + + if (msg.type() == QDBusMessage::ErrorMessage) + emit connection->callWithCallbackFailed(QDBusError(msg), call->sentMessage); + + if (call->pending) + q_dbus_pending_call_unref(call->pending); + call->pending = 0; + + if (call->autoDelete) + delete call; +} + +int QDBusConnectionPrivate::send(const QDBusMessage& message) +{ + if (QDBusMessagePrivate::isLocal(message)) + return -1; // don't send; the reply will be retrieved by the caller + // through the d_ptr->localReply link + + DBusMessage *msg = QDBusMessagePrivate::toDBusMessage(message); + if (!msg) { + if (message.type() == QDBusMessage::MethodCallMessage) + qWarning("QDBusConnection: error: could not send message to service \"%s\" path \"%s\" interface \"%s\" member \"%s\"", + qPrintable(message.service()), qPrintable(message.path()), + qPrintable(message.interface()), qPrintable(message.member())); + else if (message.type() == QDBusMessage::SignalMessage) + qWarning("QDBusConnection: error: could not send signal path \"%s\" interface \"%s\" member \"%s\"", + qPrintable(message.path()), qPrintable(message.interface()), + qPrintable(message.member())); + else + qWarning("QDBusConnection: error: could not send %s message to service \"%s\"", + message.type() == QDBusMessage::ReplyMessage ? "reply" : + message.type() == QDBusMessage::ErrorMessage ? "error" : + "invalid", qPrintable(message.service())); + return 0; + } + + q_dbus_message_set_no_reply(msg, true); // the reply would not be delivered to anything + + qDBusDebug() << QThread::currentThread() << "sending message (no reply):" << message; + checkThread(); + bool isOk = q_dbus_connection_send(connection, msg, 0); + int serial = 0; + if (isOk) + serial = q_dbus_message_get_serial(msg); + + q_dbus_message_unref(msg); + return serial; +} + +QDBusMessage QDBusConnectionPrivate::sendWithReply(const QDBusMessage &message, + int sendMode, int timeout) +{ + checkThread(); + if ((sendMode == QDBus::BlockWithGui || sendMode == QDBus::Block) + && isServiceRegisteredByThread(message.service())) + // special case for synchronous local calls + return sendWithReplyLocal(message); + + if (!QCoreApplication::instance() || sendMode == QDBus::Block) { + DBusMessage *msg = QDBusMessagePrivate::toDBusMessage(message); + if (!msg) { + qWarning("QDBusConnection: error: could not send message to service \"%s\" path \"%s\" interface \"%s\" member \"%s\"", + qPrintable(message.service()), qPrintable(message.path()), + qPrintable(message.interface()), qPrintable(message.member())); + return QDBusMessage(); + } + + qDBusDebug() << QThread::currentThread() << "sending message (blocking):" << message; + QDBusErrorInternal error; + DBusMessage *reply = q_dbus_connection_send_with_reply_and_block(connection, msg, timeout, error); + + q_dbus_message_unref(msg); + + if (!!error) { + QDBusError qe = error; + lastError = qe; + return QDBusMessage::createError(qe); + } + + QDBusMessage amsg = QDBusMessagePrivate::fromDBusMessage(reply); + q_dbus_message_unref(reply); + qDBusDebug() << QThread::currentThread() << "got message reply (blocking):" << amsg; + + return amsg; + } else { // use the event loop + QDBusPendingCallPrivate *pcall = sendWithReplyAsync(message, timeout); + if (!pcall) + return QDBusMessage(); + + pcall->watcherHelper = new QDBusPendingCallWatcherHelper; + QEventLoop loop; + loop.connect(pcall->watcherHelper, SIGNAL(reply(QDBusMessage)), SLOT(quit())); + loop.connect(pcall->watcherHelper, SIGNAL(error(QDBusError,QDBusMessage)), SLOT(quit())); + + // enter the event loop and wait for a reply + loop.exec(QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents); + + QDBusMessage reply = pcall->replyMessage; + lastError = reply; // set or clear error + + delete pcall; + return reply; + } +} + +QDBusMessage QDBusConnectionPrivate::sendWithReplyLocal(const QDBusMessage &message) +{ + qDBusDebug() << QThread::currentThread() << "sending message via local-loop:" << message; + + QDBusMessage localCallMsg = QDBusMessagePrivate::makeLocal(*this, message); + bool handled = handleMessage(localCallMsg); + + if (!handled) { + QString interface = message.interface(); + if (interface.isEmpty()) + interface = QLatin1String("<no-interface>"); + return QDBusMessage::createError(QDBusError::InternalError, + QString::fromLatin1("Internal error trying to call %1.%2 at %3 (signature '%4'") + .arg(interface, message.member(), + message.path(), message.signature())); + } + + // if the message was handled, there might be a reply + QDBusMessage localReplyMsg = QDBusMessagePrivate::makeLocalReply(*this, localCallMsg); + if (localReplyMsg.type() == QDBusMessage::InvalidMessage) { + qWarning("QDBusConnection: cannot call local method '%s' at object %s (with signature '%s') " + "on blocking mode", qPrintable(message.member()), qPrintable(message.path()), + qPrintable(message.signature())); + return QDBusMessage::createError( + QDBusError(QDBusError::InternalError, + QLatin1String("local-loop message cannot have delayed replies"))); + } + + // there is a reply + qDBusDebug() << QThread::currentThread() << "got message via local-loop:" << localReplyMsg; + return localReplyMsg; +} + +QDBusPendingCallPrivate *QDBusConnectionPrivate::sendWithReplyAsync(const QDBusMessage &message, + int timeout) +{ + if (isServiceRegisteredByThread(message.service())) { + // special case for local calls + QDBusPendingCallPrivate *pcall = new QDBusPendingCallPrivate; + pcall->sentMessage = message; + pcall->replyMessage = sendWithReplyLocal(message); + pcall->connection = this; + + return pcall; + } + + DBusMessage *msg = QDBusMessagePrivate::toDBusMessage(message); + if (!msg) { + qWarning("QDBusConnection: error: could not send message to service \"%s\" path \"%s\" interface \"%s\" member \"%s\"", + qPrintable(message.service()), qPrintable(message.path()), + qPrintable(message.interface()), qPrintable(message.member())); + return 0; + } + + checkThread(); + qDBusDebug() << QThread::currentThread() << "sending message (async):" << message; + DBusPendingCall *pending = 0; + QDBusPendingCallPrivate *pcall = new QDBusPendingCallPrivate; + pcall->sentMessage = message; + pcall->ref = 0; + + QDBusDispatchLocker locker(SendWithReplyAsyncAction, this); + if (q_dbus_connection_send_with_reply(connection, msg, &pending, timeout)) { + if (pending) { + q_dbus_message_unref(msg); + + pcall->pending = pending; + pcall->connection = this; + q_dbus_pending_call_set_notify(pending, qDBusResultReceived, pcall, 0); + + return pcall; + } else { + // we're probably disconnected at this point + lastError = QDBusError(QDBusError::Disconnected, QLatin1String("Not connected to server")); + } + } else { + lastError = QDBusError(QDBusError::NoMemory, QLatin1String("Out of memory")); + } + + q_dbus_message_unref(msg); + pcall->replyMessage = QDBusMessage::createError(lastError); + return pcall; +} + +int QDBusConnectionPrivate::sendWithReplyAsync(const QDBusMessage &message, QObject *receiver, + const char *returnMethod, const char *errorMethod, + int timeout) +{ + QDBusPendingCallPrivate *pcall = sendWithReplyAsync(message, timeout); + if (!pcall) + return 0; + + // has it already finished (dispatched locally)? + if (pcall->replyMessage.type() == QDBusMessage::ReplyMessage) { + pcall->setReplyCallback(receiver, returnMethod); + processFinishedCall(pcall); + delete pcall; + return 1; + } + + // has it already finished and is an error reply message? + if (pcall->replyMessage.type() == QDBusMessage::ErrorMessage) { + if (errorMethod) { + pcall->watcherHelper = new QDBusPendingCallWatcherHelper; + connect(pcall->watcherHelper, SIGNAL(error(QDBusError,QDBusMessage)), receiver, errorMethod); + pcall->watcherHelper->moveToThread(thread()); + } + processFinishedCall(pcall); + delete pcall; + return 1; + } + + // has it already finished with error? + if (pcall->replyMessage.type() != QDBusMessage::InvalidMessage) { + delete pcall; + return 0; + } + + pcall->autoDelete = true; + pcall->ref.ref(); + + pcall->setReplyCallback(receiver, returnMethod); + if (errorMethod) { + pcall->watcherHelper = new QDBusPendingCallWatcherHelper; + connect(pcall->watcherHelper, SIGNAL(error(QDBusError,QDBusMessage)), receiver, errorMethod); + pcall->watcherHelper->moveToThread(thread()); + } + + return 1; +} + +void QDBusConnectionPrivate::connectSignal(const QString &key, const SignalHook &hook) +{ + signalHooks.insertMulti(key, hook); + connect(hook.obj, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*)), + Qt::DirectConnection); + + MatchRefCountHash::iterator it = matchRefCounts.find(hook.matchRule); + + if (it != matchRefCounts.end()) { // Match already present + it.value() = it.value() + 1; + return; + } + + matchRefCounts.insert(hook.matchRule, 1); + + if (connection) { + qDBusDebug("Adding rule: %s", hook.matchRule.constData()); + QDBusErrorInternal error; + q_dbus_bus_add_match(connection, hook.matchRule, error); + if (!!error) { + QDBusError qerror = error; + qWarning("QDBusConnectionPrivate::connectSignal: received error from D-Bus server " + "while connecting signal to %s::%s: %s (%s)", + hook.obj->metaObject()->className(), + hook.obj->metaObject()->method(hook.midx).signature(), + qPrintable(qerror.name()), qPrintable(qerror.message())); + Q_ASSERT(false); + } + } +} + +QDBusConnectionPrivate::SignalHookHash::Iterator +QDBusConnectionPrivate::disconnectSignal(SignalHookHash::Iterator &it) +{ + const SignalHook &hook = it.value(); + + bool erase = false; + MatchRefCountHash::iterator i = matchRefCounts.find(hook.matchRule); + if (i == matchRefCounts.end()) { + qWarning("QDBusConnectionPrivate::disconnectSignal: MatchRule not found in matchRefCounts!!"); + } else { + if (i.value() == 1) { + erase = true; + matchRefCounts.erase(i); + } + else { + i.value() = i.value() - 1; + } + } + + // we don't care about errors here + if (connection && erase) { + qDBusDebug("Removing rule: %s", hook.matchRule.constData()); + q_dbus_bus_remove_match(connection, hook.matchRule, NULL); + } + + return signalHooks.erase(it); +} + +void QDBusConnectionPrivate::registerObject(const ObjectTreeNode *node) +{ + connect(node->obj, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*)), + Qt::DirectConnection); + + if (node->flags & (QDBusConnection::ExportAdaptors + | QDBusConnection::ExportScriptableSignals + | QDBusConnection::ExportNonScriptableSignals)) { + QDBusAdaptorConnector *connector = qDBusCreateAdaptorConnector(node->obj); + + if (node->flags & (QDBusConnection::ExportScriptableSignals + | QDBusConnection::ExportNonScriptableSignals)) { + connector->disconnectAllSignals(node->obj); + connector->connectAllSignals(node->obj); + } + + // disconnect and reconnect to avoid duplicates + connector->disconnect(SIGNAL(relaySignal(QObject*,const QMetaObject*,int,QVariantList)), + this, SLOT(relaySignal(QObject*,const QMetaObject*,int,QVariantList))); + connect(connector, SIGNAL(relaySignal(QObject*,const QMetaObject*,int,QVariantList)), + this, SLOT(relaySignal(QObject*,const QMetaObject*,int,QVariantList)), + Qt::DirectConnection); + } +} + +void QDBusConnectionPrivate::connectRelay(const QString &service, const QString &owner, + const QString &path, const QString &interface, + QDBusAbstractInterface *receiver, + const char *signal) +{ + // this function is called by QDBusAbstractInterface when one of its signals is connected + // we set up a relay from D-Bus into it + SignalHook hook; + QString key; + + if (!prepareHook(hook, key, service, owner, path, interface, QString(), receiver, signal, + QDBusAbstractInterface::staticMetaObject.methodCount(), true)) + return; // don't connect + + // add it to our list: + QDBusWriteLocker locker(ConnectRelayAction, this); + SignalHookHash::ConstIterator it = signalHooks.find(key); + SignalHookHash::ConstIterator end = signalHooks.constEnd(); + for ( ; it != end && it.key() == key; ++it) { + const SignalHook &entry = it.value(); + if (entry.service == hook.service && + entry.owner == hook.owner && + entry.path == hook.path && + entry.signature == hook.signature && + entry.obj == hook.obj && + entry.midx == hook.midx) + return; // already there, no need to re-add + } + + connectSignal(key, hook); +} + +void QDBusConnectionPrivate::disconnectRelay(const QString &service, const QString &owner, + const QString &path, const QString &interface, + QDBusAbstractInterface *receiver, + const char *signal) +{ + // this function is called by QDBusAbstractInterface when one of its signals is disconnected + // we remove relay from D-Bus into it + SignalHook hook; + QString key; + + if (!prepareHook(hook, key, service, owner, path, interface, QString(), receiver, signal, + QDBusAbstractInterface::staticMetaObject.methodCount(), true)) + return; // don't connect + + // remove it from our list: + QDBusWriteLocker locker(DisconnectRelayAction, this); + SignalHookHash::Iterator it = signalHooks.find(key); + SignalHookHash::Iterator end = signalHooks.end(); + for ( ; it != end && it.key() == key; ++it) { + const SignalHook &entry = it.value(); + if (entry.service == hook.service && + entry.owner == hook.owner && + entry.path == hook.path && + entry.signature == hook.signature && + entry.obj == hook.obj && + entry.midx == hook.midx) { + // found it + disconnectSignal(it); + return; + } + } + + qWarning("QDBusConnectionPrivate::disconnectRelay called for a signal that was not found"); +} + +QString QDBusConnectionPrivate::getNameOwner(const QString& serviceName) +{ + if (QDBusUtil::isValidUniqueConnectionName(serviceName)) + return serviceName; + if (!connection || !QDBusUtil::isValidBusName(serviceName)) + return QString(); + + QDBusMessage msg = QDBusMessage::createMethodCall(QLatin1String(DBUS_SERVICE_DBUS), + QLatin1String(DBUS_PATH_DBUS), QLatin1String(DBUS_INTERFACE_DBUS), + QLatin1String("GetNameOwner")); + msg << serviceName; + QDBusMessage reply = sendWithReply(msg, QDBus::Block); + if (reply.type() == QDBusMessage::ReplyMessage) + return reply.arguments().at(0).toString(); + return QString(); +} + +QDBusMetaObject * +QDBusConnectionPrivate::findMetaObject(const QString &service, const QString &path, + const QString &interface, QDBusError &error) +{ + // service must be a unique connection name + if (!interface.isEmpty()) { + QDBusReadLocker locker(FindMetaObject1Action, this); + QDBusMetaObject *mo = cachedMetaObjects.value(interface, 0); + if (mo) + return mo; + } + + // introspect the target object + QDBusMessage msg = QDBusMessage::createMethodCall(service, path, + QLatin1String(DBUS_INTERFACE_INTROSPECTABLE), + QLatin1String("Introspect")); + + QDBusMessage reply = sendWithReply(msg, QDBus::Block); + + // it doesn't exist yet, we have to create it + QDBusWriteLocker locker(FindMetaObject2Action, this); + QDBusMetaObject *mo = 0; + if (!interface.isEmpty()) + mo = cachedMetaObjects.value(interface, 0); + if (mo) + // maybe it got created when we switched from read to write lock + return mo; + + QString xml; + if (reply.type() == QDBusMessage::ReplyMessage) { + if (reply.signature() == QLatin1String("s")) + // fetch the XML description + xml = reply.arguments().at(0).toString(); + } else { + error = reply; + lastError = error; + if (reply.type() != QDBusMessage::ErrorMessage || error.type() != QDBusError::UnknownMethod) + return 0; // error + } + + // release the lock and return + QDBusMetaObject *result = QDBusMetaObject::createMetaObject(interface, xml, + cachedMetaObjects, error); + lastError = error; + return result; +} + +void QDBusConnectionPrivate::registerService(const QString &serviceName) +{ + QDBusWriteLocker locker(RegisterServiceAction, this); + serviceNames.append(serviceName); +} + +void QDBusConnectionPrivate::unregisterService(const QString &serviceName) +{ + QDBusWriteLocker locker(UnregisterServiceAction, this); + serviceNames.removeAll(serviceName); +} + +bool QDBusConnectionPrivate::isServiceRegisteredByThread(const QString &serviceName) const +{ + if (serviceName == baseService) + return true; + QStringList copy = serviceNames; + return copy.contains(serviceName); +} + +void QDBusConnectionPrivate::postEventToThread(int action, QObject *object, QEvent *ev) +{ + QDBusLockerBase::reportThreadAction(action, QDBusLockerBase::BeforePost, this); + QCoreApplication::postEvent(object, ev); + QDBusLockerBase::reportThreadAction(action, QDBusLockerBase::AfterPost, this); +} + +QT_END_NAMESPACE diff --git a/src/dbus/qdbusintegrator_p.h b/src/dbus/qdbusintegrator_p.h new file mode 100644 index 0000000..9528019 --- /dev/null +++ b/src/dbus/qdbusintegrator_p.h @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the public API. This header file may +// change from version to version without notice, or even be +// removed. +// +// We mean it. +// +// + +#ifndef QDBUSINTEGRATOR_P_H +#define QDBUSINTEGRATOR_P_H + +#include <qdbus_symbols_p.h> + +#include "qcoreevent.h" +#include "qeventloop.h" +#include "qhash.h" +#include "qobject.h" +#include "private/qobject_p.h" +#include "qlist.h" +#include "qpointer.h" +#include "qsemaphore.h" + +#include "qdbusconnection.h" +#include "qdbusmessage.h" +#include "qdbusconnection_p.h" + +QT_BEGIN_NAMESPACE + +class QDBusConnectionPrivate; + +// Really private structs used by qdbusintegrator.cpp +// Things that aren't used by any other file + +struct QDBusSlotCache +{ + struct Data + { + int flags; + int slotIdx; + QList<int> metaTypes; + }; + typedef QMultiHash<QString, Data> Hash; + Hash hash; +}; + +class QDBusCallDeliveryEvent: public QMetaCallEvent +{ +public: + QDBusCallDeliveryEvent(const QDBusConnection &c, int id, QObject *sender, + const QDBusMessage &msg, const QList<int> &types, int f = 0) + : QMetaCallEvent(id, sender, -1), connection(c), message(msg), metaTypes(types), flags(f) + { } + + int placeMetaCall(QObject *object) + { + QDBusConnectionPrivate::d(connection)->deliverCall(object, flags, message, metaTypes, id()); + return -1; + } + +private: + QDBusConnection connection; // just for refcounting + QDBusMessage message; + QList<int> metaTypes; + int flags; +}; + +class QDBusActivateObjectEvent: public QMetaCallEvent +{ +public: + QDBusActivateObjectEvent(const QDBusConnection &c, QObject *sender, + const QDBusConnectionPrivate::ObjectTreeNode &n, + int p, const QDBusMessage &m, QSemaphore *s = 0) + : QMetaCallEvent(-1, sender, -1, 0, 0, 0, s), connection(c), node(n), + pathStartPos(p), message(m), handled(false) + { } + ~QDBusActivateObjectEvent(); + + int placeMetaCall(QObject *); + +private: + QDBusConnection connection; // just for refcounting + QDBusConnectionPrivate::ObjectTreeNode node; + int pathStartPos; + QDBusMessage message; + bool handled; +}; + +class QDBusConnectionCallbackEvent : public QEvent +{ +public: + QDBusConnectionCallbackEvent() + : QEvent(User), subtype(Subtype(0)) + { } + + DBusWatch *watch; + union { + int timerId; + int fd; + }; + int extra; + + enum Subtype { + AddTimeout = 0, + KillTimer, + AddWatch, + //RemoveWatch, + ToggleWatch + } subtype; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QDBusSlotCache) + +#endif diff --git a/src/dbus/qdbusinterface.cpp b/src/dbus/qdbusinterface.cpp new file mode 100644 index 0000000..e6290f3 --- /dev/null +++ b/src/dbus/qdbusinterface.cpp @@ -0,0 +1,232 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdbusinterface.h" + +#include <qdbus_symbols_p.h> +#include <QtCore/qpointer.h> +#include <QtCore/qstringlist.h> + +#include "qdbusmetatype_p.h" +#include "qdbusinterface_p.h" +#include "qdbusconnection_p.h" + +QT_BEGIN_NAMESPACE + +QDBusInterfacePrivate::QDBusInterfacePrivate(const QString &serv, const QString &p, + const QString &iface, const QDBusConnection &con) + : QDBusAbstractInterfacePrivate(serv, p, iface, con, true), metaObject(0) +{ + // QDBusAbstractInterfacePrivate's constructor checked the parameters for us + if (connection.isConnected()) { + metaObject = connectionPrivate()->findMetaObject(service, path, interface, lastError); + + if (!metaObject) { + // creation failed, somehow + isValid = false; + if (!lastError.isValid()) + lastError = QDBusError(QDBusError::InternalError, QLatin1String("Unknown error")); + } + } +} + +QDBusInterfacePrivate::~QDBusInterfacePrivate() +{ + if (metaObject && !metaObject->cached) + delete metaObject; +} + + +/*! + \class QDBusInterface + \inmodule QtDBus + \since 4.2 + + \brief The QDBusInterface class is a proxy for interfaces on remote objects. + + QDBusInterface is a generic accessor class that is used to place calls to remote objects, + connect to signals exported by remote objects and get/set the value of remote properties. This + class is useful for dynamic access to remote objects: that is, when you do not have a generated + code that represents the remote interface. + + Calls are usually placed by using the call() function, which constructs the message, sends it + over the bus, waits for the reply and decodes the reply. Signals are connected to by using the + normal QObject::connect() function. Finally, properties are accessed using the + QObject::property() and QObject::setProperty() functions. + + The following code snippet demonstrates how to perform a + mathematical operation of \tt{"2 + 2"} in a remote application + called \c com.example.Calculator, accessed via the session bus. + + \snippet doc/src/snippets/code/src_qdbus_qdbusinterface.cpp 0 + + \sa {QtDBus XML compiler (qdbusxml2cpp)} +*/ + +/*! + Creates a dynamic QDBusInterface object associated with the + interface \a interface on object at path \a path on service \a + service, using the given \a connection. If \a interface is an + empty string, the object created will refer to the merging of all + interfaces found in that object. + + \a parent is passed to the base class constructor. + + If the remote service \a service is not present or if an error + occurs trying to obtain the description of the remote interface + \a interface, the object created will not be valid (see + isValid()). +*/ +QDBusInterface::QDBusInterface(const QString &service, const QString &path, const QString &interface, + const QDBusConnection &connection, QObject *parent) + : QDBusAbstractInterface(*new QDBusInterfacePrivate(service, path, interface, connection), + parent) +{ +} + +/*! + Destroy the object interface and frees up any resource used. +*/ +QDBusInterface::~QDBusInterface() +{ + // resources are freed in QDBusInterfacePrivate::~QDBusInterfacePrivate() +} + +/*! + \internal + Overrides QObject::metaObject to return our own copy. +*/ +const QMetaObject *QDBusInterface::metaObject() const +{ + return d_func()->isValid ? d_func()->metaObject : &QDBusAbstractInterface::staticMetaObject; +} + +/*! + \internal + Override QObject::qt_metacast to catch the interface name too. +*/ +void *QDBusInterface::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, "QDBusInterface")) + return static_cast<void*>(const_cast<QDBusInterface*>(this)); + if (d_func()->interface.toLatin1() == _clname) + return static_cast<void*>(const_cast<QDBusInterface*>(this)); + return QDBusAbstractInterface::qt_metacast(_clname); +} + +/*! + \internal + Dispatch the call through the private. +*/ +int QDBusInterface::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QDBusAbstractInterface::qt_metacall(_c, _id, _a); + if (_id < 0 || !d_func()->isValid) + return _id; + return d_func()->metacall(_c, _id, _a); +} + +int QDBusInterfacePrivate::metacall(QMetaObject::Call c, int id, void **argv) +{ + Q_Q(QDBusInterface); + + if (c == QMetaObject::InvokeMetaMethod) { + int offset = metaObject->methodOffset(); + QMetaMethod mm = metaObject->method(id + offset); + + if (mm.methodType() == QMetaMethod::Signal) { + // signal relay from D-Bus world to Qt world + QMetaObject::activate(q, metaObject, id, argv); + + } else if (mm.methodType() == QMetaMethod::Slot) { + // method call relay from Qt world to D-Bus world + // get D-Bus equivalent signature + QString methodName = QLatin1String(metaObject->dbusNameForMethod(id)); + const int *inputTypes = metaObject->inputTypesForMethod(id); + int inputTypesCount = *inputTypes; + + // we will assume that the input arguments were passed correctly + QVariantList args; + for (int i = 1; i <= inputTypesCount; ++i) + args << QVariant(inputTypes[i], argv[i]); + + // make the call + QPointer<QDBusInterface> qq = q; + QDBusMessage reply = q->callWithArgumentList(QDBus::Block, methodName, args); + args.clear(); + + // we ignore return values + + // access to "this" or to "q" below this point must check for "qq" + // we may have been deleted! + + if (!qq.isNull()) + lastError = reply; + + // done + return -1; + } + } else if (c == QMetaObject::ReadProperty) { + // Qt doesn't support non-readable properties + // we have to re-check + QMetaProperty mp = metaObject->property(id + metaObject->propertyOffset()); + if (!mp.isReadable()) + return -1; // don't read + + QVariant *value = reinterpret_cast<QVariant*>(argv[1]); + argv[1] = 0; + *value = property(mp); + + return -1; // handled, error or not + } else if (c == QMetaObject::WriteProperty) { + // QMetaProperty::write has already checked that we're writable + // it has also checked that the type is right + QVariant *value = reinterpret_cast<QVariant *>(argv[1]); + QMetaProperty mp = metaObject->property(id + metaObject->propertyOffset()); + + setProperty(mp, *value); + return -1; + } + return id; +} + +QT_END_NAMESPACE diff --git a/src/dbus/qdbusinterface.h b/src/dbus/qdbusinterface.h new file mode 100644 index 0000000..3776cd0 --- /dev/null +++ b/src/dbus/qdbusinterface.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDBUSINTERFACE_H +#define QDBUSINTERFACE_H + +#include <QtDBus/qdbusabstractinterface.h> +#include <QtDBus/qdbusconnection.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(DBus) + +class QDBusInterfacePrivate; +class QDBUS_EXPORT QDBusInterface: public QDBusAbstractInterface +{ + friend class QDBusConnection; +private: + QDBusInterface(QDBusInterfacePrivate *p); + +public: + QDBusInterface(const QString &service, const QString &path, const QString &interface = QString(), + const QDBusConnection &connection = QDBusConnection::sessionBus(), + QObject *parent = 0); + ~QDBusInterface(); + + virtual const QMetaObject *metaObject() const; + virtual void *qt_metacast(const char *); + virtual int qt_metacall(QMetaObject::Call, int, void **); + +private: + Q_DECLARE_PRIVATE(QDBusInterface) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/dbus/qdbusinterface_p.h b/src/dbus/qdbusinterface_p.h new file mode 100644 index 0000000..c18ea5e --- /dev/null +++ b/src/dbus/qdbusinterface_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the public API. This header file may +// change from version to version without notice, or even be +// removed. +// +// We mean it. +// +// + +#ifndef QDBUSINTERFACEPRIVATE_H +#define QDBUSINTERFACEPRIVATE_H + +#include <qdbusabstractinterface_p.h> +#include <qdbusmetaobject_p.h> +#include <qdbusinterface.h> + +QT_BEGIN_NAMESPACE + +class QDBusInterfacePrivate: public QDBusAbstractInterfacePrivate +{ +public: + Q_DECLARE_PUBLIC(QDBusInterface) + + QDBusMetaObject *metaObject; + + QDBusInterfacePrivate(const QString &serv, const QString &p, const QString &iface, + const QDBusConnection &con); + ~QDBusInterfacePrivate(); + + int metacall(QMetaObject::Call c, int id, void **argv); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/dbus/qdbusinternalfilters.cpp b/src/dbus/qdbusinternalfilters.cpp new file mode 100644 index 0000000..43888da --- /dev/null +++ b/src/dbus/qdbusinternalfilters.cpp @@ -0,0 +1,394 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdbusconnection_p.h" + +#include <qdbus_symbols_p.h> +#include <QtCore/qcoreapplication.h> +#include <QtCore/qmetaobject.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qthread.h> + +#include "qdbusabstractadaptor.h" +#include "qdbusabstractadaptor_p.h" +#include "qdbusconnection.h" +#include "qdbusextratypes.h" +#include "qdbusmessage.h" +#include "qdbusmetatype.h" +#include "qdbusmessage_p.h" +#include "qdbusutil_p.h" + +QT_BEGIN_NAMESPACE + +// defined in qdbusxmlgenerator.cpp +extern QString qDBusGenerateMetaObjectXml(QString interface, const QMetaObject *mo, + const QMetaObject *base, int flags); + +static const char introspectableInterfaceXml[] = + " <interface name=\"org.freedesktop.DBus.Introspectable\">\n" + " <method name=\"Introspect\">\n" + " <arg name=\"xml_data\" type=\"s\" direction=\"out\"/>\n" + " </method>\n" + " </interface>\n"; + +static const char propertiesInterfaceXml[] = + " <interface name=\"org.freedesktop.DBus.Properties\">\n" + " <method name=\"Get\">\n" + " <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n" + " <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n" + " <arg name=\"value\" type=\"v\" direction=\"out\"/>\n" + " </method>\n" + " <method name=\"Set\">\n" + " <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n" + " <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n" + " <arg name=\"value\" type=\"v\" direction=\"in\"/>\n" + " </method>\n" + " <method name=\"GetAll\">\n" + " <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n" + " <arg name=\"values\" type=\"a{sv}\" direction=\"out\"/>\n" + " <annotation name=\"com.trolltech.QtDBus.QtTypeName.Out0\" value=\"QVariantMap\"/>" + " </method>\n" + " </interface>\n"; + +static QString generateSubObjectXml(QObject *object) +{ + QString retval; + const QObjectList &objs = object->children(); + QObjectList::ConstIterator it = objs.constBegin(); + QObjectList::ConstIterator end = objs.constEnd(); + for ( ; it != end; ++it) { + QString name = (*it)->objectName(); + if (!name.isEmpty() && QDBusUtil::isValidPartOfObjectPath(name)) + retval += QString::fromLatin1(" <node name=\"%1\"/>\n") + .arg(name); + } + return retval; +} + +// declared as extern in qdbusconnection_p.h + +QString qDBusIntrospectObject(const QDBusConnectionPrivate::ObjectTreeNode &node) +{ + // object may be null + + QString xml_data(QLatin1String(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE)); + xml_data += QLatin1String("<node>\n"); + + if (node.obj) { + Q_ASSERT_X(QThread::currentThread() == node.obj->thread(), + "QDBusConnection: internal threading error", + "function called for an object that is in another thread!!"); + + if (node.flags & (QDBusConnection::ExportScriptableContents + | QDBusConnection::ExportNonScriptableContents)) { + // create XML for the object itself + const QMetaObject *mo = node.obj->metaObject(); + for ( ; mo != &QObject::staticMetaObject; mo = mo->superClass()) + xml_data += qDBusGenerateMetaObjectXml(QString(), mo, mo->superClass(), + node.flags); + } + + // does this object have adaptors? + QDBusAdaptorConnector *connector; + if (node.flags & QDBusConnection::ExportAdaptors && + (connector = qDBusFindAdaptorConnector(node.obj))) { + + // trasverse every adaptor in this object + QDBusAdaptorConnector::AdaptorMap::ConstIterator it = connector->adaptors.constBegin(); + QDBusAdaptorConnector::AdaptorMap::ConstIterator end = connector->adaptors.constEnd(); + for ( ; it != end; ++it) { + // add the interface: + QString ifaceXml = QDBusAbstractAdaptorPrivate::retrieveIntrospectionXml(it->adaptor); + if (ifaceXml.isEmpty()) { + // add the interface's contents: + ifaceXml += qDBusGenerateMetaObjectXml(QString::fromLatin1(it->interface), + it->adaptor->metaObject(), + &QDBusAbstractAdaptor::staticMetaObject, + QDBusConnection::ExportScriptableContents + | QDBusConnection::ExportNonScriptableContents); + + QDBusAbstractAdaptorPrivate::saveIntrospectionXml(it->adaptor, ifaceXml); + } + + xml_data += ifaceXml; + } + } + + xml_data += QLatin1String( propertiesInterfaceXml ); + } + + xml_data += QLatin1String( introspectableInterfaceXml ); + + if (node.flags & QDBusConnection::ExportChildObjects) { + xml_data += generateSubObjectXml(node.obj); + } else { + // generate from the object tree + QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator it = + node.children.constBegin(); + QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator end = + node.children.constEnd(); + for ( ; it != end; ++it) + if (it->obj || !it->children.isEmpty()) + xml_data += QString::fromLatin1(" <node name=\"%1\"/>\n") + .arg(it->name); + } + + xml_data += QLatin1String("</node>\n"); + return xml_data; +} + +// implement the D-Bus interface org.freedesktop.DBus.Properties + +static QDBusMessage qDBusPropertyError(const QDBusMessage &msg, const QString &interface_name) +{ + return msg.createErrorReply(QLatin1String(DBUS_ERROR_INVALID_ARGS), + QString::fromLatin1("Interface %1 was not found in object %2") + .arg(interface_name) + .arg(msg.path())); +} + +QDBusMessage qDBusPropertyGet(const QDBusConnectionPrivate::ObjectTreeNode &node, + const QDBusMessage &msg) +{ + Q_ASSERT(msg.arguments().count() == 2); + Q_ASSERT_X(!node.obj || QThread::currentThread() == node.obj->thread(), + "QDBusConnection: internal threading error", + "function called for an object that is in another thread!!"); + + QString interface_name = msg.arguments().at(0).toString(); + QByteArray property_name = msg.arguments().at(1).toString().toUtf8(); + + QDBusAdaptorConnector *connector; + QVariant value; + if (node.flags & QDBusConnection::ExportAdaptors && + (connector = qDBusFindAdaptorConnector(node.obj))) { + + // find the class that implements interface_name or try until we've found the property + // in case of an empty interface + if (interface_name.isEmpty()) { + for (QDBusAdaptorConnector::AdaptorMap::ConstIterator it = connector->adaptors.constBegin(), + end = connector->adaptors.constEnd(); it != end; ++it) { + const QMetaObject *mo = it->adaptor->metaObject(); + int pidx = mo->indexOfProperty(property_name); + if (pidx != -1) { + value = mo->property(pidx).read(it->adaptor); + break; + } + } + } else { + QDBusAdaptorConnector::AdaptorMap::ConstIterator it; + it = qLowerBound(connector->adaptors.constBegin(), connector->adaptors.constEnd(), + interface_name); + if (it != connector->adaptors.constEnd() && interface_name == QLatin1String(it->interface)) + value = it->adaptor->property(property_name); + } + } + + if (!value.isValid() && node.flags & (QDBusConnection::ExportAllProperties | + QDBusConnection::ExportNonScriptableProperties)) { + // try the object itself + int pidx = node.obj->metaObject()->indexOfProperty(property_name); + if (pidx != -1) { + QMetaProperty mp = node.obj->metaObject()->property(pidx); + if ((mp.isScriptable() && (node.flags & QDBusConnection::ExportScriptableProperties)) || + (!mp.isScriptable() && (node.flags & QDBusConnection::ExportNonScriptableProperties))) + value = mp.read(node.obj); + } + } + + if (!value.isValid()) { + // the property was not found + return qDBusPropertyError(msg, interface_name); + } + + return msg.createReply(qVariantFromValue(QDBusVariant(value))); +} + +QDBusMessage qDBusPropertySet(const QDBusConnectionPrivate::ObjectTreeNode &node, + const QDBusMessage &msg) +{ + Q_ASSERT(msg.arguments().count() == 3); + Q_ASSERT_X(!node.obj || QThread::currentThread() == node.obj->thread(), + "QDBusConnection: internal threading error", + "function called for an object that is in another thread!!"); + + QString interface_name = msg.arguments().at(0).toString(); + QByteArray property_name = msg.arguments().at(1).toString().toUtf8(); + QVariant value = qvariant_cast<QDBusVariant>(msg.arguments().at(2)).variant(); + + QDBusAdaptorConnector *connector; + if (node.flags & QDBusConnection::ExportAdaptors && + (connector = qDBusFindAdaptorConnector(node.obj))) { + + // find the class that implements interface_name or try until we've found the property + // in case of an empty interface + if (interface_name.isEmpty()) { + for (QDBusAdaptorConnector::AdaptorMap::ConstIterator it = connector->adaptors.constBegin(), + end = connector->adaptors.constEnd(); it != end; ++it) { + const QMetaObject *mo = it->adaptor->metaObject(); + int pidx = mo->indexOfProperty(property_name); + if (pidx != -1) { + mo->property(pidx).write(it->adaptor, value); + return msg.createReply(); + } + } + } else { + QDBusAdaptorConnector::AdaptorMap::ConstIterator it; + it = qLowerBound(connector->adaptors.constBegin(), connector->adaptors.constEnd(), + interface_name); + if (it != connector->adaptors.end() && interface_name == QLatin1String(it->interface)) + if (it->adaptor->setProperty(property_name, value)) + return msg.createReply(); + } + } + + if (node.flags & (QDBusConnection::ExportScriptableProperties | + QDBusConnection::ExportNonScriptableProperties)) { + // try the object itself + int pidx = node.obj->metaObject()->indexOfProperty(property_name); + if (pidx != -1) { + QMetaProperty mp = node.obj->metaObject()->property(pidx); + if ((mp.isScriptable() && (node.flags & QDBusConnection::ExportScriptableProperties)) || + (!mp.isScriptable() && (node.flags & QDBusConnection::ExportNonScriptableProperties))) + if (mp.write(node.obj, value)) + return msg.createReply(); + } + } + + // the property was not found or not written to + return qDBusPropertyError(msg, interface_name); +} + +// unite two QVariantMaps, but don't generate duplicate keys +static QVariantMap &operator+=(QVariantMap &lhs, const QVariantMap &rhs) +{ + QVariantMap::ConstIterator it = rhs.constBegin(), + end = rhs.constEnd(); + for ( ; it != end; ++it) + lhs.insert(it.key(), it.value()); + return lhs; +} + +static QVariantMap readAllProperties(QObject *object, int flags) +{ + QVariantMap result; + const QMetaObject *mo = object->metaObject(); + + // QObject has properties, so don't start from 0 + for (int i = QObject::staticMetaObject.propertyCount(); i < mo->propertyCount(); ++i) { + QMetaProperty mp = mo->property(i); + + // is it readable? + if (!mp.isReadable()) + continue; + + // is it a registered property? + int typeId = qDBusNameToTypeId(mp.typeName()); + if (!typeId) + continue; + const char *signature = QDBusMetaType::typeToSignature(typeId); + if (!signature) + continue; + + // is this property visible from the outside? + if ((mp.isScriptable() && flags & QDBusConnection::ExportScriptableProperties) || + (!mp.isScriptable() && flags & QDBusConnection::ExportNonScriptableProperties)) { + // yes, it's visible + QVariant value = mp.read(object); + if (value.isValid()) + result.insert(QString::fromLatin1(mp.name()), value); + } + } + + return result; +} + +QDBusMessage qDBusPropertyGetAll(const QDBusConnectionPrivate::ObjectTreeNode &node, + const QDBusMessage &msg) +{ + Q_ASSERT(msg.arguments().count() == 1); + Q_ASSERT_X(!node.obj || QThread::currentThread() == node.obj->thread(), + "QDBusConnection: internal threading error", + "function called for an object that is in another thread!!"); + + QString interface_name = msg.arguments().at(0).toString(); + + bool interfaceFound = false; + QVariantMap result; + + QDBusAdaptorConnector *connector; + if (node.flags & QDBusConnection::ExportAdaptors && + (connector = qDBusFindAdaptorConnector(node.obj))) { + + if (interface_name.isEmpty()) { + // iterate over all interfaces + for (QDBusAdaptorConnector::AdaptorMap::ConstIterator it = connector->adaptors.constBegin(), + end = connector->adaptors.constEnd(); it != end; ++it) { + result += readAllProperties(it->adaptor, QDBusConnection::ExportAllProperties); + } + } else { + // find the class that implements interface_name + QDBusAdaptorConnector::AdaptorMap::ConstIterator it; + it = qLowerBound(connector->adaptors.constBegin(), connector->adaptors.constEnd(), + interface_name); + if (it != connector->adaptors.constEnd() && interface_name == QLatin1String(it->interface)) { + interfaceFound = true; + result = readAllProperties(it->adaptor, QDBusConnection::ExportAllProperties); + } + } + } + + if (node.flags & QDBusConnection::ExportAllProperties && + (!interfaceFound || interface_name.isEmpty())) { + // try the object itself + result += readAllProperties(node.obj, node.flags); + interfaceFound = true; + } + + if (!interfaceFound && !interface_name.isEmpty()) { + // the interface was not found + return qDBusPropertyError(msg, interface_name); + } + + return msg.createReply(qVariantFromValue(result)); +} + +QT_END_NAMESPACE diff --git a/src/dbus/qdbusintrospection.cpp b/src/dbus/qdbusintrospection.cpp new file mode 100644 index 0000000..e642dbe --- /dev/null +++ b/src/dbus/qdbusintrospection.cpp @@ -0,0 +1,425 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdbusintrospection_p.h" +#include "qdbusxmlparser_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QDBusIntrospection + \brief Information about introspected objects and interfaces on D-Bus. + \internal + + This class provides structures and methods for parsing the XML introspection data for D-Bus. + Normally, you don't have to use the methods provided here: QDBusInterface and QDBusObject will + do that for you. + + But they may prove useful if the XML data was obtained through other means (like parsing a file). +*/ + +/*! + \class QDBusIntrospection::Argument + \brief One argument to a D-Bus method or signal. + + This struct represents one argument passed to a method or received from a method or signal in + D-Bus. The struct does not contain information on the direction (input or output). +*/ + +/*! + \variable QDBusIntrospection::Argument::type + The argument type. +*/ + +/*! + \variable QDBusIntrospection::Argument::name + The argument name. The argument name is optional, so this may be a null QString. +*/ + +/*! + \fn QDBusIntrospection::Argument::operator==(const Argument &other) const + Compares this object against \a other and return true if they are the same. +*/ + +/*! + \class QDBusIntrospection::Method + \brief Information about one method. + + This struct represents one method discovered through introspection. A method is composed of + its \a name, its input arguments, its output arguments, and, optionally, annotations. There are no + "in-out" arguments. +*/ + +/*! + \variable QDBusIntrospection::Method::name + The method's name. +*/ + +/*! + \variable QDBusIntrospection::Method::inputArgs + A list of the method's input arguments. +*/ + +/*! + \variable QDBusIntrospection::Method::outputArgs + A list of the method's output arguments (i.e., return values). +*/ + +/*! + \variable QDBusIntrospection::Method::annotations + The annotations associated with the method. Each annotation is a pair of strings, where the key + is of the same format as a D-Bus interface name. The value is arbitrary. +*/ + +/*! + \fn QDBusIntrospection::Method::operator==(const Method &other) const + Compares this object against \a other and return true if they are the same. +*/ + +/*! + \class QDBusIntrospection::Signal + \brief Information about one signal. + + This struct represents one signal discovered through introspection. A signal is composed of + its \a name, its output arguments, and, optionally, annotations. +*/ + +/*! + \variable QDBusIntrospection::Signal::name + The signal's name. +*/ + +/*! + \variable QDBusIntrospection::Signal::outputArgs + A list of the signal's arguments. +*/ + +/*! + \variable QDBusIntrospection::Signal::annotations + The annotations associated with the signal. Each annotation is a pair of strings, where the key + is of the same format as a D-Bus interface name. The value is arbitrary. +*/ + +/*! + \fn QDBusIntrospection::Signal::operator==(const Signal& other) const + Compares this object against \a other and return true if they are the same. +*/ + +/*! + \class QDBusIntrospection::Property + \brief Information about one property. + + This struct represents one property discovered through introspection. A property is composed of + its \a name, its \a type, its \a access rights, and, optionally, annotations. +*/ + +/*! + \variable QDBusIntrospection::Property::name + The property's name. +*/ + +/*! + \variable QDBusIntrospection::Property::type + The property's type. +*/ + +/*! + \enum QDBusIntrospection::Property::Access + The possible access rights for a property: + \value Read + \value Write + \value ReadWrite +*/ + +/*! + \variable QDBusIntrospection::Property::access + The property's access rights. +*/ + +/*! + \variable QDBusIntrospection::Property::annotations + The annotations associated with the property. Each annotation is a pair of strings, where the key + is of the same format as a D-Bus interface name. The value is arbitrary. +*/ + +/*! + \fn QDBusIntrospection::Property::operator==(const Property &other) const + Compares this object against \a other and return true if they are the same. +*/ + +/*! + \class QDBusIntrospection::Interface + \brief Information about one interface on the bus. + + Each interface on D-Bus has an unique \a name, identifying where that interface was defined. + Interfaces may have annotations, methods, signals and properties, but none are mandatory. +*/ + +/*! + \variable QDBusIntrospection::Interface::name + The interface's name. +*/ + +/*! + \variable QDBusIntrospection::Interface::introspection + The XML document fragment describing this interface. + + If parsed again through parseInterface, the object returned should have the same contents as + this object. +*/ + +/*! + \variable QDBusIntrospection::Interface::annotations + The annotations associated with the interface. Each annotation is a pair of strings, where the key + is of the same format as a D-Bus interface name. The value is arbitrary. +*/ + +/*! + \variable QDBusIntrospection::Interface::methods + The methods available in this interface. Note that method names are not unique (i.e., methods + can be overloaded with multiple arguments types). +*/ + +/*! + \variable QDBusIntrospection::Interface::signals_ + The signals available in this interface. Note that signal names are not unique (i.e., signals + can be overloaded with multiple argument types). + + This member is called "signals_" because "signals" is a reserved keyword in Qt. +*/ + +/*! + \variable QDBusIntrospection::Interface::properties + The properties available in this interface. Property names are unique. +*/ + +/*! + \fn QDBusIntrospection::Interface::operator==(const Interface &other) const + Compares this object against \a other and return true if they are the same. + + Note that two interfaces are considered to be the same if they have the same name. The internal + structures in the objects are not compared. +*/ + +/*! + \class QDBusIntrospection::Object + \brief Information about one object on the bus. + + An object on the D-Bus bus is represented by its service and path on the service but, unlike + interfaces, objects are mutable. That is, their contents can change with time. Therefore, + while the (service, path) pair uniquely identifies an object, the information contained in + this struct may no longer represent the object. + + An object can contain interfaces and child (sub) objects. +*/ + +/*! + \variable QDBusIntrospection::Object::service + The object's service name. + + \sa parseObject(), parseObjectTree() +*/ + +/*! + \variable QDBusIntrospection::Object::path + The object's path on the service. This is an absolute path. + + \sa parseObject(), parseObjectTree() +*/ + +/*! + \variable QDBusIntrospection::Object::introspection + The XML document fragment describing this object, its interfaces and sub-objects at the time + of the parsing. + + The result of parseObject with this XML data should be the same as the Object struct. +*/ + +/*! + \variable QDBusIntrospection::Object::interfaces + The list of interface names in this object. +*/ + +/*! + \variable QDBusIntrospection::Object::childObjects + The list of child object names in this object. Note that this is a relative name, not an + absolute path. To obtain the absolute path, concatenate with \l + {QDBusIntrospection::Object::path}{path}. +*/ + +/*! + \class QDBusIntrospection::ObjectTree + \brief Complete information about one object node and its descendency. + + This struct contains the same data as QDBusIntrospection::Object, plus the actual data for the + interfaces and child (sub) objects that was available in the XML document. +*/ + +/*! + \variable QDBusIntrospection::ObjectTree::interfaceData + A map of interfaces and their names. +*/ + +/*! + \variable QDBusIntrospection::ObjectTree::childObjectData + A map of object paths and their data. The map key contains the relative path to the object. + + Note this map contains only the child notes that do have information about the sub-object's + contents. If the XML data did not contain the information, only the object name will be listed + in childObjects, but not in childObjectData. +*/ + +/*! + \typedef QDBusIntrospection::Annotations + Contains a QMap of an annotation pair. The annotation's name is stored in the QMap key and + must be unique. The annotation's value is stored in the QMap's value and is arbitrary. +*/ + +/*! + \typedef QDBusIntrospection::Arguments + Contains a list of arguments to either a Method or a Signal. The arguments' order is important. +*/ + +/*! + \typedef QDBusIntrospection::Methods + Contains a QMap of methods and their names. The method's name is stored in the map's key and + is not necessarily unique. The order in which multiple methods with the same name are stored + in this map is undefined. +*/ + +/*! + \typedef QDBusIntrospection::Signals + Contains a QMap of signals and their names. The signal's name is stored in the map's key and + is not necessarily unique. The order in which multiple signals with the same name are stored + in this map is undefined. +*/ + +/*! + \typedef QDBusIntrospection::Properties + Contains a QMap of properties and their names. Each property must have a unique name. +*/ + +/*! + \typedef QDBusIntrospection::Interfaces + Contains a QMap of interfaces and their names. Each interface has a unique name. +*/ + +/*! + \typedef QDBusIntrospection::Objects + Contains a QMap of objects and their paths relative to their immediate parent. + + \sa parseObjectTree() +*/ + +/*! + Parses the XML document fragment (given by \a xml) containing one interface. + + The first element tag in this XML data must be either \<node\> or \<interface\>. If it is + \<node\>, then the \<interface\> tag must be a child tag of the \<node\> one. + + If there are multiple interfaces in this XML data, it is undefined which one will be + returned. +*/ +QDBusIntrospection::Interface +QDBusIntrospection::parseInterface(const QString &xml) +{ + // be lazy + Interfaces ifs = parseInterfaces(xml); + if (ifs.isEmpty()) + return Interface(); + + // return the first in map order (probably alphabetical order) + return *ifs.constBegin().value(); +} + +/*! + Parses the XML document fragment (given by \a xml) containing several interfaces. + + If the first element tag in this document fragment is \<node\>, the interfaces parsed will + be those found as child elements of the \<node\> tag. +*/ +QDBusIntrospection::Interfaces +QDBusIntrospection::parseInterfaces(const QString &xml) +{ + QString null; + QDBusXmlParser parser(null, null, xml); + return parser.interfaces(); +} + +/*! + Parses the XML document fragment (given by \a xml) containing one object, found at the service + \a service and path \a path. + + The first element tag in this document must be \<node\>. If that tag does not contain + a name attribute, the \a path argument will be used to determine the path of this + object node. + + This function does not parse the interfaces contained in the node, nor sub-object's contents. + It will only list their names. If you need to know their contents, use parseObjectTree. +*/ +QDBusIntrospection::Object +QDBusIntrospection::parseObject(const QString &xml, const QString &service, const QString &path) +{ + QDBusXmlParser parser(service, path, xml); + QSharedDataPointer<QDBusIntrospection::Object> retval = parser.object(); + if (!retval) + return QDBusIntrospection::Object(); + return *retval; +} + +/*! + Parses the XML document fragment (given by \a xml) containing one object node and returns all + the information about the interfaces and sub-objects, found at the service \a service and path + \a path. + + The Objects map returned will contain the absolute path names in the key. +*/ +QDBusIntrospection::ObjectTree +QDBusIntrospection::parseObjectTree(const QString &xml, const QString &service, const QString &path) +{ + QDBusXmlParser parser(service, path, xml); + QSharedDataPointer<QDBusIntrospection::ObjectTree> retval = parser.objectTree(); + if (!retval) + return QDBusIntrospection::ObjectTree(); + return *retval; +} + +QT_END_NAMESPACE diff --git a/src/dbus/qdbusintrospection_p.h b/src/dbus/qdbusintrospection_p.h new file mode 100644 index 0000000..b741c61 --- /dev/null +++ b/src/dbus/qdbusintrospection_p.h @@ -0,0 +1,180 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDBUSINTROSPECTION_H +#define QDBUSINTROSPECTION_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qstring.h> +#include <QtCore/qlist.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qmap.h> +#include <QtCore/qpair.h> +#include <QtCore/qshareddata.h> +#include <qdbusmacros.h> + +QT_BEGIN_NAMESPACE + +class QDBUS_EXPORT QDBusIntrospection +{ +public: + // forward declarations + struct Argument; + struct Method; + struct Signal; + struct Property; + struct Interface; + struct Object; + struct ObjectTree; + + // typedefs + typedef QMap<QString, QString> Annotations; + typedef QList<Argument> Arguments; + typedef QMultiMap<QString, Method> Methods; + typedef QMultiMap<QString, Signal> Signals; + typedef QMap<QString, Property> Properties; + typedef QMap<QString, QSharedDataPointer<Interface> > Interfaces; + typedef QMap<QString, QSharedDataPointer<ObjectTree> > Objects; + +public: + // the structs + + struct Argument + { + QString type; + QString name; + + inline bool operator==(const Argument& other) const + { return name == other.name && type == other.type; } + }; + + struct Method + { + QString name; + Arguments inputArgs; + Arguments outputArgs; + Annotations annotations; + + inline bool operator==(const Method& other) const + { return name == other.name && annotations == other.annotations && + inputArgs == other.inputArgs && outputArgs == other.outputArgs; } + }; + + struct Signal + { + QString name; + Arguments outputArgs; + Annotations annotations; + + inline bool operator==(const Signal& other) const + { return name == other.name && annotations == other.annotations && + outputArgs == other.outputArgs; } + }; + + struct Property + { + enum Access { Read, Write, ReadWrite }; + QString name; + QString type; + Access access; + Annotations annotations; + + inline bool operator==(const Property& other) const + { return access == other.access && name == other.name && + annotations == other.annotations && type == other.type; } + }; + + struct Interface: public QSharedData + { + QString name; + QString introspection; + + Annotations annotations; + Methods methods; + Signals signals_; + Properties properties; + + inline bool operator==(const Interface &other) const + { return !name.isEmpty() && name == other.name; } + }; + + struct Object: public QSharedData + { + QString service; + QString path; + QString introspection; + + QStringList interfaces; + QStringList childObjects; + }; + + struct ObjectTree: public Object + { + Interfaces interfaceData; + Objects childObjectData; + }; + +public: + static Interface parseInterface(const QString &xml); + static Interfaces parseInterfaces(const QString &xml); + static Object parseObject(const QString &xml, const QString &service = QString(), + const QString &path = QString()); + static ObjectTree parseObjectTree(const QString &xml, + const QString &service, + const QString &path); + +private: + QDBusIntrospection(); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/dbus/qdbusmacros.h b/src/dbus/qdbusmacros.h new file mode 100644 index 0000000..d3bbb78 --- /dev/null +++ b/src/dbus/qdbusmacros.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDBUSMACROS_H +#define QDBUSMACROS_H + +#include <QtCore/qglobal.h> +#include <QtCore/qmetatype.h> +#include <QtCore/qvariant.h> + +#if defined(QDBUS_MAKEDLL) +# define QDBUS_EXPORT Q_DECL_EXPORT +#else +# define QDBUS_EXPORT Q_DECL_IMPORT +#endif + +#ifndef Q_MOC_RUN +# define Q_NOREPLY +#endif + +#ifdef Q_CC_MSVC +#include <QtCore/qlist.h> +#include <QtCore/qset.h> +#include <QtCore/qhash.h> +#include <QtCore/qvector.h> +#endif + +// prevent syncqt complaints +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE +QT_MODULE(DBus) +QT_END_NAMESPACE +QT_END_HEADER + +#endif diff --git a/src/dbus/qdbusmarshaller.cpp b/src/dbus/qdbusmarshaller.cpp new file mode 100644 index 0000000..3df8a97 --- /dev/null +++ b/src/dbus/qdbusmarshaller.cpp @@ -0,0 +1,547 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdbusargument_p.h" +#include "qdbusutil_p.h" + +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) +{ + qIterAppend(&iterator, ba, DBUS_TYPE_BYTE, &arg); +} + +inline void QDBusMarshaller::append(bool arg) +{ + dbus_bool_t cast = arg; + qIterAppend(&iterator, ba, DBUS_TYPE_BOOLEAN, &cast); +} + +inline void QDBusMarshaller::append(short arg) +{ + qIterAppend(&iterator, ba, DBUS_TYPE_INT16, &arg); +} + +inline void QDBusMarshaller::append(ushort arg) +{ + qIterAppend(&iterator, ba, DBUS_TYPE_UINT16, &arg); +} + +inline void QDBusMarshaller::append(int arg) +{ + qIterAppend(&iterator, ba, DBUS_TYPE_INT32, &arg); +} + +inline void QDBusMarshaller::append(uint arg) +{ + qIterAppend(&iterator, ba, DBUS_TYPE_UINT32, &arg); +} + +inline void QDBusMarshaller::append(qlonglong arg) +{ + qIterAppend(&iterator, ba, DBUS_TYPE_INT64, &arg); +} + +inline void QDBusMarshaller::append(qulonglong arg) +{ + qIterAppend(&iterator, ba, DBUS_TYPE_UINT64, &arg); +} + +inline void QDBusMarshaller::append(double arg) +{ + qIterAppend(&iterator, ba, DBUS_TYPE_DOUBLE, &arg); +} + +void QDBusMarshaller::append(const QString &arg) +{ + QByteArray data = arg.toUtf8(); + const char *cdata = data.constData(); + qIterAppend(&iterator, ba, DBUS_TYPE_STRING, &cdata); +} + +inline void QDBusMarshaller::append(const QDBusObjectPath &arg) +{ + QByteArray data = arg.path().toUtf8(); + if (!ba && data.isEmpty()) + error(); + const char *cdata = data.constData(); + 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(); + const char *cdata = data.constData(); + qIterAppend(&iterator, ba, DBUS_TYPE_SIGNATURE, &cdata); +} + +inline void QDBusMarshaller::append(const QByteArray &arg) +{ + if (ba) { + *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) { + *ba += DBUS_TYPE_VARIANT_AS_STRING; + return true; + } + + const QVariant &value = arg.variant(); + QVariant::Type id = QVariant::Type(value.userType()); + if (id == QVariant::Invalid) { + qWarning("QDBusMarshaller: cannot add a null QDBusVariant"); + error(); + return false; + } + + QByteArray tmpSignature; + const char *signature = 0; + if (int(id) == qMetaTypeId<QDBusArgument>()) { + // 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", + QVariant::typeToName( id ), id); + error(); + return false; + } + + QDBusMarshaller sub; + 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) { + *ba += DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING; + return; + } + + QDBusMarshaller sub; + 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", + QVariant::typeToName( QVariant::Type(id) ), id); + error(); + 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", + QVariant::typeToName( QVariant::Type(kid) ), kid); + error(); + return this; + } + if (ksignature[1] != 0 || !q_dbus_type_is_basic(*ksignature)) { + qWarning("QDBusMarshaller: type '%s' (%d) cannot be used as the key type in a D-BUS map.", + QVariant::typeToName( QVariant::Type(kid) ), kid); + error(); + return this; + } + + const char *vsignature = QDBusMetaType::typeToSignature( QVariant::Type(vid) ); + if (!vsignature) { + qWarning("QDBusMarshaller: type `%s' (%d) is not registered with D-BUS. " + "Use qDBusRegisterMetaType to register it", + QVariant::typeToName( QVariant::Type(vid) ), vid); + error(); + 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; + + if (ba) + switch (code) { + case DBUS_TYPE_ARRAY: + *ba += char(code); + *ba += signature; + // fall through + + case DBUS_TYPE_DICT_ENTRY: + sub.closeCode = 0; + 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; + 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 (closeCode) + *ba += closeCode; + } else if (parent) { + q_dbus_message_iter_close_container(&parent->iterator, &iterator); + } +} + +void QDBusMarshaller::error() +{ + ok = false; + if (parent) + parent->error(); +} + +bool QDBusMarshaller::appendVariantInternal(const QVariant &arg) +{ + int id = arg.userType(); + if (id == QVariant::Invalid) { + qWarning("QDBusMarshaller: cannot add an invalid QVariant"); + error(); + return false; + } + + // intercept QDBusArgument parameters here + if (id == qMetaTypeId<QDBusArgument>()) { + QDBusArgument dbusargument = qvariant_cast<QDBusArgument>(arg); + QDBusArgumentPrivate *d = QDBusArgumentPrivate::d(dbusargument); + if (!d->message) + return false; // can't append this one... + + QDBusDemarshaller demarshaller; + 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", + QVariant::typeToName( QVariant::Type(id) ), id); + error(); + return false; + } + + switch (*signature) { +#ifdef __OPTIMIZE__ + case DBUS_TYPE_BYTE: + case DBUS_TYPE_BOOLEAN: + 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_STRING: + case DBUS_TYPE_OBJECT_PATH: + case DBUS_TYPE_SIGNATURE: { + const QByteArray data = + reinterpret_cast<const QString *>(arg.constData())->toUtf8(); + const char *rawData = data.constData(); + qIterAppend(&iterator, ba, *signature, &rawData); + 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; + 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; +#endif + + // 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: + ; // fall through + } + // fall through + + 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; + + 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 (q_dbus_type_is_basic(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 (q_dbus_type_is_fixed(element)) { + // another optimisation: 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] = { 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; // 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 diff --git a/src/dbus/qdbusmessage.cpp b/src/dbus/qdbusmessage.cpp new file mode 100644 index 0000000..19f0b04 --- /dev/null +++ b/src/dbus/qdbusmessage.cpp @@ -0,0 +1,719 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdbusmessage.h" + +#include <qdebug.h> +#include <qstringlist.h> + +#include <qdbus_symbols_p.h> + +#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) +{ +} + +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. +*/ +DBusMessage *QDBusMessagePrivate::toDBusMessage(const QDBusMessage &message) +{ + if (!qdbus_loadLibDBus()) + 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: + msg = q_dbus_message_new_method_call(data(d_ptr->service.toUtf8()), data(d_ptr->path.toUtf8()), + data(d_ptr->interface.toUtf8()), data(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: + msg = q_dbus_message_new(DBUS_MESSAGE_TYPE_ERROR); + q_dbus_message_set_error_name(msg, data(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: + msg = q_dbus_message_new_signal(data(d_ptr->path.toUtf8()), data(d_ptr->interface.toUtf8()), + data(d_ptr->name.toUtf8())); + break; + default: + Q_ASSERT(false); + break; + } +#if 0 + DBusError err; + q_dbus_error_init(&err); + if (q_dbus_error_is_set(&err)) { + QDBusError qe(&err); + qDebug() << "QDBusMessagePrivate::toDBusMessage" << qe; + } +#endif + if (!msg) + return 0; + + 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); + Q_ASSERT(false); + 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<QDBusVariant>()) { + // yes, we are + // we must marshall and demarshall again so as to create QDBusArgument + // entries for the complex types + DBusMessage *message = toDBusMessage(asSent); + 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<QVariant> &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. +*/ + +/*! + 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<QVariant> &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<QVariant> 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 +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 + diff --git a/src/dbus/qdbusmessage.h b/src/dbus/qdbusmessage.h new file mode 100644 index 0000000..5a6322d --- /dev/null +++ b/src/dbus/qdbusmessage.h @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDBUSMESSAGE_H +#define QDBUSMESSAGE_H + +#include <QtDBus/qdbusmacros.h> +#include <QtDBus/qdbuserror.h> +#include <QtCore/qlist.h> +#include <QtCore/qvariant.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(DBus) + +class QDBusMessagePrivate; +class QDBUS_EXPORT QDBusMessage +{ +public: + enum MessageType { + InvalidMessage, + MethodCallMessage, + ReplyMessage, + ErrorMessage, + SignalMessage + }; + + QDBusMessage(); + QDBusMessage(const QDBusMessage &other); + QDBusMessage &operator=(const QDBusMessage &other); + ~QDBusMessage(); + + static QDBusMessage createSignal(const QString &path, const QString &interface, + const QString &name); + static QDBusMessage createMethodCall(const QString &destination, const QString &path, + const QString &interface, const QString &method); + static QDBusMessage createError(const QString &name, const QString &msg); + static inline QDBusMessage createError(const QDBusError &err) + { return createError(err.name(), err.message()); } + static inline QDBusMessage createError(QDBusError::ErrorType type, const QString &msg) + { return createError(QDBusError::errorString(type), msg); } + + QDBusMessage createReply(const QList<QVariant> &arguments = QList<QVariant>()) const; + inline QDBusMessage createReply(const QVariant &argument) const + { return createReply(QList<QVariant>() << argument); } + + QDBusMessage createErrorReply(const QString name, const QString &msg) const; + inline QDBusMessage createErrorReply(const QDBusError &err) const + { return createErrorReply(err.name(), err.message()); } + inline QDBusMessage createErrorReply(QDBusError::ErrorType type, const QString &msg) const; + + QString service() const; + QString path() const; + QString interface() const; + QString member() const; + QString errorName() const; + QString errorMessage() const; + MessageType type() const; + QString signature() const; + + bool isReplyRequired() const; + + void setDelayedReply(bool enable) const; + bool isDelayedReply() const; + + void setArguments(const QList<QVariant> &arguments); + QList<QVariant> arguments() const; + + QDBusMessage &operator<<(const QVariant &arg); + +private: + friend class QDBusMessagePrivate; + QDBusMessagePrivate *d_ptr; +}; + +inline QDBusMessage QDBusMessage::createErrorReply(QDBusError::ErrorType atype, const QString &amsg) const +{ return createErrorReply(QDBusError::errorString(atype), amsg); } + +#ifndef QT_NO_DEBUG_STREAM +QDBUS_EXPORT QDebug operator<<(QDebug, const QDBusMessage &); +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif + diff --git a/src/dbus/qdbusmessage_p.h b/src/dbus/qdbusmessage_p.h new file mode 100644 index 0000000..317f630 --- /dev/null +++ b/src/dbus/qdbusmessage_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDBUSMESSAGE_P_H +#define QDBUSMESSAGE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include <qatomic.h> +#include <qstring.h> + +struct DBusMessage; + +QT_BEGIN_NAMESPACE + +class QDBusConnectionPrivate; + +class QDBusMessagePrivate +{ +public: + QDBusMessagePrivate(); + ~QDBusMessagePrivate(); + + QList<QVariant> arguments; + QString service, path, interface, name, message, signature; + DBusMessage *msg; + DBusMessage *reply; + int type; + int timeout; + mutable QDBusMessage *localReply; + QAtomicInt ref; + + mutable uint delayedReply : 1; + uint localMessage : 1; + + static DBusMessage *toDBusMessage(const QDBusMessage &message); + static QDBusMessage fromDBusMessage(DBusMessage *dmsg); + + static bool isLocal(const QDBusMessage &msg); + static QDBusMessage makeLocal(const QDBusConnectionPrivate &conn, + const QDBusMessage &asSent); + static QDBusMessage makeLocalReply(const QDBusConnectionPrivate &conn, + const QDBusMessage &asSent); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/dbus/qdbusmetaobject.cpp b/src/dbus/qdbusmetaobject.cpp new file mode 100644 index 0000000..8eb8b1a --- /dev/null +++ b/src/dbus/qdbusmetaobject.cpp @@ -0,0 +1,679 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdbusmetaobject_p.h" + +#include <QtCore/qbytearray.h> +#include <QtCore/qhash.h> +#include <QtCore/qstring.h> +#include <QtCore/qvarlengtharray.h> + +#include "qdbusutil_p.h" +#include "qdbuserror.h" +#include "qdbusmetatype.h" +#include "qdbusargument.h" +#include "qdbusintrospection_p.h" +#include "qdbusabstractinterface_p.h" + +QT_BEGIN_NAMESPACE + +class QDBusMetaObjectGenerator +{ +public: + QDBusMetaObjectGenerator(const QString &interface, + const QDBusIntrospection::Interface *parsedData); + void write(QDBusMetaObject *obj); + void writeWithoutXml(QDBusMetaObject *obj); + +private: + struct Method { + QByteArray parameters; + QByteArray typeName; + QByteArray tag; + QByteArray name; + QByteArray inputSignature; + QByteArray outputSignature; + QVarLengthArray<int, 4> inputTypes; + QVarLengthArray<int, 4> outputTypes; + int flags; + }; + + struct Property { + QByteArray typeName; + QByteArray signature; + int type; + int flags; + }; + struct Type { + int id; + QByteArray name; + }; + + enum PropertyFlags { + Invalid = 0x00000000, + Readable = 0x00000001, + Writable = 0x00000002, + Resettable = 0x00000004, + EnumOrFlag = 0x00000008, + StdCppSet = 0x00000100, + // Override = 0x00000200, + Designable = 0x00001000, + ResolveDesignable = 0x00002000, + Scriptable = 0x00004000, + ResolveScriptable = 0x00008000, + Stored = 0x00010000, + ResolveStored = 0x00020000, + Editable = 0x00040000, + ResolveEditable = 0x00080000, + User = 0x00100000, + ResolveUser = 0x00200000 + }; + + enum MethodFlags { + AccessPrivate = 0x00, + AccessProtected = 0x01, + AccessPublic = 0x02, + AccessMask = 0x03, //mask + + MethodMethod = 0x00, + MethodSignal = 0x04, + MethodSlot = 0x08, + MethodTypeMask = 0x0c, + + MethodCompatibility = 0x10, + MethodCloned = 0x20, + MethodScriptable = 0x40 + }; + + QMap<QByteArray, Method> methods; + QMap<QByteArray, Property> properties; + + const QDBusIntrospection::Interface *data; + QString interface; + + Type findType(const QByteArray &signature, + const QDBusIntrospection::Annotations &annotations, + const char *direction = "Out", int id = -1); + + void parseMethods(); + void parseSignals(); + void parseProperties(); +}; + +static const int intsPerProperty = 2; +static const int intsPerMethod = 5; + +// ### from kernel/qmetaobject.cpp (Qt 4.1.2): +struct QDBusMetaObjectPrivate +{ + int revision; + int className; + int classInfoCount, classInfoData; + int methodCount, methodData; + int propertyCount, propertyData; + int enumeratorCount, enumeratorData; + + // this is specific for QDBusMetaObject: + int propertyDBusData; + int methodDBusData; +}; + +QDBusMetaObjectGenerator::QDBusMetaObjectGenerator(const QString &interfaceName, + const QDBusIntrospection::Interface *parsedData) + : data(parsedData), interface(interfaceName) +{ + if (data) { + parseProperties(); + parseSignals(); // call parseSignals first so that slots override signals + parseMethods(); + } +} + +QDBusMetaObjectGenerator::Type +QDBusMetaObjectGenerator::findType(const QByteArray &signature, + const QDBusIntrospection::Annotations &annotations, + const char *direction, int id) +{ + Type result; + result.id = QVariant::Invalid; + + int type = QDBusMetaType::signatureToType(signature); + if (type == QVariant::Invalid) { + // it's not a type normally handled by our meta type system + // it must contain an annotation + QString annotationName = QString::fromLatin1("com.trolltech.QtDBus.QtTypeName"); + if (id >= 0) + annotationName += QString::fromLatin1(".%1%2") + .arg(QLatin1String(direction)) + .arg(id); + + // extract from annotations: + QByteArray typeName = annotations.value(annotationName).toLatin1(); + + // verify that it's a valid one + if (typeName.isEmpty()) + return result; // invalid + + type = QVariant::nameToType(typeName); + if (type == QVariant::UserType) + type = QMetaType::type(typeName); + if (type == QVariant::Invalid || signature != QDBusMetaType::typeToSignature(type)) + return result; // unknown type is invalid too + + result.name = typeName; + } else { + result.name = QVariant::typeToName( QVariant::Type(type) ); + } + + result.id = type; + return result; // success +} + +void QDBusMetaObjectGenerator::parseMethods() +{ + // + // TODO: + // Add cloned methods when the remote object has return types + // + + QDBusIntrospection::Methods::ConstIterator method_it = data->methods.constBegin(); + QDBusIntrospection::Methods::ConstIterator method_end = data->methods.constEnd(); + for ( ; method_it != method_end; ++method_it) { + const QDBusIntrospection::Method &m = *method_it; + Method mm; + + mm.name = m.name.toLatin1(); + QByteArray prototype = mm.name; + prototype += '('; + + bool ok = true; + + // build the input argument list + for (int i = 0; i < m.inputArgs.count(); ++i) { + const QDBusIntrospection::Argument &arg = m.inputArgs.at(i); + + Type type = findType(arg.type.toLatin1(), m.annotations, "In", i); + if (type.id == QVariant::Invalid) { + ok = false; + break; + } + + mm.inputSignature += arg.type.toLatin1(); + mm.inputTypes.append(type.id); + + mm.parameters.append(arg.name.toLatin1()); + mm.parameters.append(','); + + prototype.append(type.name); + prototype.append(','); + } + if (!ok) continue; + + // build the output argument list: + for (int i = 0; i < m.outputArgs.count(); ++i) { + const QDBusIntrospection::Argument &arg = m.outputArgs.at(i); + + Type type = findType(arg.type.toLatin1(), m.annotations, "Out", i); + if (type.id == QVariant::Invalid) { + ok = false; + break; + } + + mm.outputSignature += arg.type.toLatin1(); + mm.outputTypes.append(type.id); + + if (i == 0) { + // return value + mm.typeName = type.name; + } else { + // non-const ref parameter + mm.parameters.append(arg.name.toLatin1()); + mm.parameters.append(','); + + prototype.append(type.name); + prototype.append("&,"); + } + } + if (!ok) continue; + + // convert the last commas: + if (!mm.parameters.isEmpty()) { + mm.parameters.truncate(mm.parameters.length() - 1); + prototype[prototype.length() - 1] = ')'; + } else { + prototype.append(')'); + } + + // check the async tag + if (m.annotations.value(QLatin1String(ANNOTATION_NO_WAIT)) == QLatin1String("true")) + mm.tag = "Q_NOREPLY"; + + // meta method flags + mm.flags = AccessPublic | MethodSlot | MethodScriptable; + + // add + methods.insert(QMetaObject::normalizedSignature(prototype), mm); + } +} + +void QDBusMetaObjectGenerator::parseSignals() +{ + QDBusIntrospection::Signals::ConstIterator signal_it = data->signals_.constBegin(); + QDBusIntrospection::Signals::ConstIterator signal_end = data->signals_.constEnd(); + for ( ; signal_it != signal_end; ++signal_it) { + const QDBusIntrospection::Signal &s = *signal_it; + Method mm; + + mm.name = s.name.toLatin1(); + QByteArray prototype = mm.name; + prototype += '('; + + bool ok = true; + + // build the output argument list + for (int i = 0; i < s.outputArgs.count(); ++i) { + const QDBusIntrospection::Argument &arg = s.outputArgs.at(i); + + Type type = findType(arg.type.toLatin1(), s.annotations, "Out", i); + if (type.id == QVariant::Invalid) { + ok = false; + break; + } + + mm.inputSignature += arg.type.toLatin1(); + mm.inputTypes.append(type.id); + + mm.parameters.append(arg.name.toLatin1()); + mm.parameters.append(','); + + prototype.append(type.name); + prototype.append(','); + } + if (!ok) continue; + + // convert the last commas: + if (!mm.parameters.isEmpty()) { + mm.parameters.truncate(mm.parameters.length() - 1); + prototype[prototype.length() - 1] = ')'; + } else { + prototype.append(')'); + } + + // meta method flags + mm.flags = AccessProtected | MethodSignal | MethodScriptable; + + // add + methods.insert(QMetaObject::normalizedSignature(prototype), mm); + } +} + +void QDBusMetaObjectGenerator::parseProperties() +{ + QDBusIntrospection::Properties::ConstIterator prop_it = data->properties.constBegin(); + QDBusIntrospection::Properties::ConstIterator prop_end = data->properties.constEnd(); + for ( ; prop_it != prop_end; ++prop_it) { + const QDBusIntrospection::Property &p = *prop_it; + Property mp; + Type type = findType(p.type.toLatin1(), p.annotations); + if (type.id == QVariant::Invalid) + continue; + + QByteArray name = p.name.toLatin1(); + mp.signature = p.type.toLatin1(); + mp.type = type.id; + mp.typeName = type.name; + + // build the flags: + mp.flags = StdCppSet | Scriptable | Stored | Designable; + if (p.access != QDBusIntrospection::Property::Write) + mp.flags |= Readable; + if (p.access != QDBusIntrospection::Property::Read) + mp.flags |= Writable; + + if (mp.typeName == "QDBusVariant") + mp.flags |= 0xff << 24; + else if (mp.type < 0xff) + // encode the type in the flags + mp.flags |= mp.type << 24; + + // add the property: + properties.insert(name, mp); + } +} + +void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj) +{ + // this code here is mostly copied from qaxbase.cpp + // with a few modifications to make it cleaner + + QString className = interface; + className.replace(QLatin1Char('.'), QLatin1String("::")); + if (className.isEmpty()) + className = QLatin1String("QDBusInterface"); + + QVarLengthArray<int> idata; + idata.resize(sizeof(QDBusMetaObjectPrivate) / sizeof(int)); + + QDBusMetaObjectPrivate *header = reinterpret_cast<QDBusMetaObjectPrivate *>(idata.data()); + header->revision = 1; + header->className = 0; + header->classInfoCount = 0; + header->classInfoData = 0; + header->methodCount = methods.count(); + header->methodData = idata.size(); + header->propertyCount = properties.count(); + header->propertyData = header->methodData + header->methodCount * 5; + header->enumeratorCount = 0; + header->enumeratorData = 0; + header->propertyDBusData = header->propertyData + header->propertyCount * 3; + header->methodDBusData = header->propertyDBusData + header->propertyCount * intsPerProperty; + + int data_size = idata.size() + + (header->methodCount * (5+intsPerMethod)) + + (header->propertyCount * (3+intsPerProperty)); + foreach (const Method &mm, methods) + data_size += 2 + mm.inputTypes.count() + mm.outputTypes.count(); + idata.resize(data_size + 1); + + char null('\0'); + QByteArray stringdata = className.toLatin1(); + stringdata += null; + stringdata.reserve(8192); + + int offset = header->methodData; + int signatureOffset = header->methodDBusData; + int typeidOffset = header->methodDBusData + header->methodCount * intsPerMethod; + idata[typeidOffset++] = 0; // eod + + // add each method: + for (QMap<QByteArray, Method>::ConstIterator it = methods.constBegin(); + it != methods.constEnd(); ++it) { + // form "prototype\0parameters\0typeName\0tag\0methodname\0inputSignature\0outputSignature" + const Method &mm = it.value(); + + idata[offset++] = stringdata.length(); + stringdata += it.key(); // prototype + stringdata += null; + idata[offset++] = stringdata.length(); + stringdata += mm.parameters; + stringdata += null; + idata[offset++] = stringdata.length(); + stringdata += mm.typeName; + stringdata += null; + idata[offset++] = stringdata.length(); + stringdata += mm.tag; + stringdata += null; + idata[offset++] = mm.flags; + + idata[signatureOffset++] = stringdata.length(); + stringdata += mm.name; + stringdata += null; + idata[signatureOffset++] = stringdata.length(); + stringdata += mm.inputSignature; + stringdata += null; + idata[signatureOffset++] = stringdata.length(); + stringdata += mm.outputSignature; + stringdata += null; + + idata[signatureOffset++] = typeidOffset; + idata[typeidOffset++] = mm.inputTypes.count(); + memcpy(idata.data() + typeidOffset, mm.inputTypes.data(), mm.inputTypes.count() * sizeof(int)); + typeidOffset += mm.inputTypes.count(); + + idata[signatureOffset++] = typeidOffset; + idata[typeidOffset++] = mm.outputTypes.count(); + memcpy(idata.data() + typeidOffset, mm.outputTypes.data(), mm.outputTypes.count() * sizeof(int)); + typeidOffset += mm.outputTypes.count(); + } + + Q_ASSERT(offset == header->propertyData); + Q_ASSERT(signatureOffset == header->methodDBusData + header->methodCount * intsPerMethod); + Q_ASSERT(typeidOffset == idata.size()); + + // add each property + signatureOffset = header->propertyDBusData; + for (QMap<QByteArray, Property>::ConstIterator it = properties.constBegin(); + it != properties.constEnd(); ++it) { + const Property &mp = it.value(); + + // form is "name\0typeName\0signature\0" + idata[offset++] = stringdata.length(); + stringdata += it.key(); // name + stringdata += null; + idata[offset++] = stringdata.length(); + stringdata += mp.typeName; + stringdata += null; + idata[offset++] = mp.flags; + + idata[signatureOffset++] = stringdata.length(); + stringdata += mp.signature; + stringdata += null; + idata[signatureOffset++] = mp.type; + } + + Q_ASSERT(offset == header->propertyDBusData); + Q_ASSERT(signatureOffset == header->methodDBusData); + + char *string_data = new char[stringdata.length()]; + memcpy(string_data, stringdata, stringdata.length()); + + uint *uint_data = new uint[idata.size()]; + memcpy(uint_data, idata.data(), idata.size() * sizeof(int)); + + // put the metaobject together + obj->d.data = uint_data; + obj->d.extradata = 0; + obj->d.stringdata = string_data; + obj->d.superdata = &QDBusAbstractInterface::staticMetaObject; +} + +#if 0 +void QDBusMetaObjectGenerator::writeWithoutXml(const QString &interface) +{ + // no XML definition + QString tmp(interface); + tmp.replace(QLatin1Char('.'), QLatin1String("::")); + QByteArray name(tmp.toLatin1()); + + QDBusMetaObjectPrivate *header = new QDBusMetaObjectPrivate; + memset(header, 0, sizeof *header); + header->revision = 1; + // leave the rest with 0 + + char *stringdata = new char[name.length() + 1]; + stringdata[name.length()] = '\0'; + + d.data = reinterpret_cast<uint*>(header); + d.extradata = 0; + d.stringdata = stringdata; + d.superdata = &QDBusAbstractInterface::staticMetaObject; + cached = false; +} +#endif + +///////// +// class QDBusMetaObject + +QDBusMetaObject *QDBusMetaObject::createMetaObject(const QString &interface, const QString &xml, + QHash<QString, QDBusMetaObject *> &cache, + QDBusError &error) +{ + error = QDBusError(); + QDBusIntrospection::Interfaces parsed = QDBusIntrospection::parseInterfaces(xml); + + QDBusMetaObject *we = 0; + QDBusIntrospection::Interfaces::ConstIterator it = parsed.constBegin(); + QDBusIntrospection::Interfaces::ConstIterator end = parsed.constEnd(); + for ( ; it != end; ++it) { + // check if it's in the cache + bool us = it.key() == interface; + + QDBusMetaObject *obj = cache.value(it.key(), 0); + if ( !obj && ( us || !interface.startsWith( QLatin1String("local.") ) ) ) { + // not in cache; create + obj = new QDBusMetaObject; + QDBusMetaObjectGenerator generator(it.key(), it.value().constData()); + generator.write(obj); + + if ( (obj->cached = !it.key().startsWith( QLatin1String("local.") )) ) + // cache it + cache.insert(it.key(), obj); + else if (!us) + delete obj; + + } + + if (us) + // it's us + we = obj; + } + + if (we) + return we; + // still nothing? + + if (parsed.isEmpty()) { + // object didn't return introspection + we = new QDBusMetaObject; + QDBusMetaObjectGenerator generator(interface, 0); + generator.write(we); + we->cached = false; + return we; + } else if (interface.isEmpty()) { + // merge all interfaces + it = parsed.constBegin(); + QDBusIntrospection::Interface merged = *it.value().constData(); + + for (++it; it != end; ++it) { + merged.annotations.unite(it.value()->annotations); + merged.methods.unite(it.value()->methods); + merged.signals_.unite(it.value()->signals_); + merged.properties.unite(it.value()->properties); + } + + merged.name = QLatin1String("local.Merged"); + merged.introspection.clear(); + + we = new QDBusMetaObject; + QDBusMetaObjectGenerator generator(merged.name, &merged); + generator.write(we); + we->cached = false; + return we; + } + + // mark as an error + error = QDBusError(QDBusError::UnknownInterface, + QString( QLatin1String("Interface '%1' was not found") ) + .arg(interface)); + return 0; +} + +QDBusMetaObject::QDBusMetaObject() +{ +} + +static inline const QDBusMetaObjectPrivate *priv(const uint* data) +{ + return reinterpret_cast<const QDBusMetaObjectPrivate *>(data); +} + +const char *QDBusMetaObject::dbusNameForMethod(int id) const +{ + //id -= methodOffset(); + if (id >= 0 && id < priv(d.data)->methodCount) { + int handle = priv(d.data)->methodDBusData + id*intsPerMethod; + return d.stringdata + d.data[handle]; + } + return 0; +} + +const char *QDBusMetaObject::inputSignatureForMethod(int id) const +{ + //id -= methodOffset(); + if (id >= 0 && id < priv(d.data)->methodCount) { + int handle = priv(d.data)->methodDBusData + id*intsPerMethod; + return d.stringdata + d.data[handle + 1]; + } + return 0; +} + +const char *QDBusMetaObject::outputSignatureForMethod(int id) const +{ + //id -= methodOffset(); + if (id >= 0 && id < priv(d.data)->methodCount) { + int handle = priv(d.data)->methodDBusData + id*intsPerMethod; + return d.stringdata + d.data[handle + 2]; + } + return 0; +} + +const int *QDBusMetaObject::inputTypesForMethod(int id) const +{ + //id -= methodOffset(); + if (id >= 0 && id < priv(d.data)->methodCount) { + int handle = priv(d.data)->methodDBusData + id*intsPerMethod; + return reinterpret_cast<const int*>(d.data + d.data[handle + 3]); + } + return 0; +} + +const int *QDBusMetaObject::outputTypesForMethod(int id) const +{ + //id -= methodOffset(); + if (id >= 0 && id < priv(d.data)->methodCount) { + int handle = priv(d.data)->methodDBusData + id*intsPerMethod; + return reinterpret_cast<const int*>(d.data + d.data[handle + 4]); + } + return 0; +} + +int QDBusMetaObject::propertyMetaType(int id) const +{ + //id -= propertyOffset(); + if (id >= 0 && id < priv(d.data)->propertyCount) { + int handle = priv(d.data)->propertyDBusData + id*intsPerProperty; + return d.data[handle + 1]; + } + return 0; +} + +QT_END_NAMESPACE + diff --git a/src/dbus/qdbusmetaobject_p.h b/src/dbus/qdbusmetaobject_p.h new file mode 100644 index 0000000..8ef7c95 --- /dev/null +++ b/src/dbus/qdbusmetaobject_p.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the public API. This header file may +// change from version to version without notice, or even be +// removed. +// +// We mean it. +// +// + +#ifndef QDBUSMETAOBJECTPRIVATE_H +#define QDBUSMETAOBJECTPRIVATE_H + +#include <QtCore/qmetaobject.h> +#include <qdbusmacros.h> + +QT_BEGIN_NAMESPACE + +class QDBusError; + +struct QDBusMetaObjectPrivate; +struct QDBUS_EXPORT QDBusMetaObject: public QMetaObject +{ + bool cached; + + static QDBusMetaObject *createMetaObject(const QString &interface, const QString &xml, + QHash<QString, QDBusMetaObject *> &map, + QDBusError &error); + ~QDBusMetaObject() + { + delete [] d.stringdata; + delete [] d.data; + } + + // methods (slots & signals): + const char *dbusNameForMethod(int id) const; + const char *inputSignatureForMethod(int id) const; + const char *outputSignatureForMethod(int id) const; + const int *inputTypesForMethod(int id) const; + const int *outputTypesForMethod(int id) const; + + // properties: + int propertyMetaType(int id) const; + +private: + QDBusMetaObject(); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/dbus/qdbusmetatype.cpp b/src/dbus/qdbusmetatype.cpp new file mode 100644 index 0000000..a1a1a80 --- /dev/null +++ b/src/dbus/qdbusmetatype.cpp @@ -0,0 +1,464 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdbusmetatype.h" + +#include <string.h> +#include <qdbus_symbols_p.h> + +#include <qbytearray.h> +#include <qglobal.h> +#include <qreadwritelock.h> +#include <qvector.h> + +#include "qdbusmessage.h" +#include "qdbusutil_p.h" +#include "qdbusmetatype_p.h" +#include "qdbusargument_p.h" + +Q_DECLARE_METATYPE(QList<bool>) +Q_DECLARE_METATYPE(QList<short>) +Q_DECLARE_METATYPE(QList<ushort>) +Q_DECLARE_METATYPE(QList<int>) +Q_DECLARE_METATYPE(QList<uint>) +Q_DECLARE_METATYPE(QList<qlonglong>) +Q_DECLARE_METATYPE(QList<qulonglong>) +Q_DECLARE_METATYPE(QList<double>) + +QT_BEGIN_NAMESPACE + +class QDBusCustomTypeInfo +{ +public: + QDBusCustomTypeInfo() : signature(0, '\0'), marshall(0), demarshall(0) + { } + + // Suggestion: + // change 'signature' to char* and make QDBusCustomTypeInfo a Movable type + QByteArray signature; + QDBusMetaType::MarshallFunction marshall; + QDBusMetaType::DemarshallFunction demarshall; +}; + +template<typename T> +inline static void registerHelper(T * = 0) +{ + void (*mf)(QDBusArgument &, const T *) = qDBusMarshallHelper<T>; + void (*df)(const QDBusArgument &, T *) = qDBusDemarshallHelper<T>; + QDBusMetaType::registerMarshallOperators(qMetaTypeId<T>(), + reinterpret_cast<QDBusMetaType::MarshallFunction>(mf), + reinterpret_cast<QDBusMetaType::DemarshallFunction>(df)); +} + +int QDBusMetaTypeId::message; +int QDBusMetaTypeId::argument; +int QDBusMetaTypeId::variant; +int QDBusMetaTypeId::objectpath; +int QDBusMetaTypeId::signature; +int QDBusMetaTypeId::error; + +void QDBusMetaTypeId::init() +{ + static volatile bool initialized = false; + + // reentrancy is not a problem since everything else is locked on their own + // set the guard variable at the end + if (!initialized) { + // register our types with QtCore + message = qRegisterMetaType<QDBusMessage>("QDBusMessage"); + argument = qRegisterMetaType<QDBusArgument>("QDBusArgument"); + variant = qRegisterMetaType<QDBusVariant>("QDBusVariant"); + objectpath = qRegisterMetaType<QDBusObjectPath>("QDBusObjectPath"); + signature = qRegisterMetaType<QDBusSignature>("QDBusSignature"); + error = qRegisterMetaType<QDBusError>("QDBusError"); + +#ifndef QDBUS_NO_SPECIALTYPES + // and register QtCore's with us + registerHelper<QDate>(); + registerHelper<QTime>(); + registerHelper<QDateTime>(); + registerHelper<QRect>(); + registerHelper<QRectF>(); + registerHelper<QSize>(); + registerHelper<QSizeF>(); + registerHelper<QPoint>(); + registerHelper<QPointF>(); + registerHelper<QLine>(); + registerHelper<QLineF>(); + registerHelper<QVariantList>(); + registerHelper<QVariantMap>(); + + qDBusRegisterMetaType<QList<bool> >(); + qDBusRegisterMetaType<QList<short> >(); + qDBusRegisterMetaType<QList<ushort> >(); + qDBusRegisterMetaType<QList<int> >(); + qDBusRegisterMetaType<QList<uint> >(); + qDBusRegisterMetaType<QList<qlonglong> >(); + qDBusRegisterMetaType<QList<qulonglong> >(); + qDBusRegisterMetaType<QList<double> >(); + qDBusRegisterMetaType<QList<QDBusObjectPath> >(); + qDBusRegisterMetaType<QList<QDBusSignature> >(); +#endif + + initialized = true; + } +} + +Q_GLOBAL_STATIC(QVector<QDBusCustomTypeInfo>, customTypes) +Q_GLOBAL_STATIC(QReadWriteLock, customTypesLock) + +/*! + \class QDBusMetaType + \brief Meta-type registration system for the QtDBus module. + \internal + + The QDBusMetaType class allows you to register class types for + marshalling and demarshalling over D-Bus. D-Bus supports a very + limited set of primitive types, but allows one to extend the type + system by creating compound types, such as arrays (lists) and + structs. In order to use them with QtDBus, those types must be + registered. + + See \l {qdbustypesystem.html}{QtDBus type system} for more + information on the type system and how to register additional + types. + + \sa {qdbustypesystem.html}{QtDBus type system}, + qDBusRegisterMetaType(), QMetaType, QVariant, QDBusArgument +*/ + +/*! + \fn int qDBusRegisterMetaType() + \relates QDBusArgument + \threadsafe + \since 4.2 + + Registers \c{T} with the + \l {qdbustypesystem.html}{QtDBus type system} and the Qt \l + {QMetaType}{meta-type system}, if it's not already registered. + + To register a type, it must be declared as a meta-type with the + Q_DECLARE_METATYPE() macro, and then registered as in the + following example: + + \snippet doc/src/snippets/code/src_qdbus_qdbusmetatype.cpp 0 + + If \c{T} isn't a type derived from one of + Qt's \l{container classes}, the \c{operator<<} and + \c{operator>>} streaming operators between \c{T} and QDBusArgument + must be already declared. See the \l {qdbustypesystem.html}{QtDBus + type system} page for more information on how to declare such + types. + + This function returns the Qt meta type id for the type (the same + value that is returned from qRegisterMetaType()). + + \sa {qdbustypesystem.html}{QtDBus type system}, qRegisterMetaType(), QMetaType +*/ + +/*! + \typedef QDBusMetaType::MarshallFunction + \internal +*/ + +/*! + \typedef QDBusMetaType::DemarshallFunction + \internal +*/ + +/*! + \internal + Registers the marshalling and demarshalling functions for meta + type \a id. +*/ +void QDBusMetaType::registerMarshallOperators(int id, MarshallFunction mf, + DemarshallFunction df) +{ + QByteArray var; + QVector<QDBusCustomTypeInfo> *ct = customTypes(); + if (id < 0 || !mf || !df || !ct) + return; // error! + + QWriteLocker locker(customTypesLock()); + if (id >= ct->size()) + ct->resize(id + 1); + QDBusCustomTypeInfo &info = (*ct)[id]; + info.marshall = mf; + info.demarshall = df; +} + +/*! + \internal + Executes the marshalling of type \a id (whose data is contained in + \a data) to the D-Bus marshalling argument \a arg. Returns true if + the marshalling succeeded, or false if an error occurred. +*/ +bool QDBusMetaType::marshall(QDBusArgument &arg, int id, const void *data) +{ + QDBusMetaTypeId::init(); + + MarshallFunction mf; + { + QReadLocker locker(customTypesLock()); + QVector<QDBusCustomTypeInfo> *ct = customTypes(); + if (id >= ct->size()) + return false; // non-existant + + const QDBusCustomTypeInfo &info = (*ct).at(id); + if (!info.marshall) { + mf = 0; // make gcc happy + return false; + } else + mf = info.marshall; + } + + mf(arg, data); + return true; +} + +/*! + \internal + Executes the demarshalling of type \a id (whose data will be placed in + \a data) from the D-Bus marshalling argument \a arg. Returns true if + the demarshalling succeeded, or false if an error occurred. +*/ +bool QDBusMetaType::demarshall(const QDBusArgument &arg, int id, void *data) +{ + QDBusMetaTypeId::init(); + + DemarshallFunction df; + { + QReadLocker locker(customTypesLock()); + QVector<QDBusCustomTypeInfo> *ct = customTypes(); + if (id >= ct->size()) + return false; // non-existant + + const QDBusCustomTypeInfo &info = (*ct).at(id); + if (!info.demarshall) { + df = 0; // make gcc happy + return false; + } else + df = info.demarshall; + } + + QDBusArgument copy = arg; + df(copy, data); + return true; +} + +/*! + \fn QDBusMetaType::signatureToType(const char *signature) + \internal + + Returns the Qt meta type id for the given D-Bus signature for exactly one full type, given + by \a signature. + + Note: this function only handles the basic D-Bus types. + + \sa QDBusUtil::isValidSingleSignature(), typeToSignature(), + QVariant::type(), QVariant::userType() +*/ +int QDBusMetaType::signatureToType(const char *signature) +{ + if (!signature) + return QVariant::Invalid; + + QDBusMetaTypeId::init(); + switch (signature[0]) + { + case DBUS_TYPE_BOOLEAN: + return QVariant::Bool; + + case DBUS_TYPE_BYTE: + return QMetaType::UChar; + + case DBUS_TYPE_INT16: + return QMetaType::Short; + + case DBUS_TYPE_UINT16: + return QMetaType::UShort; + + case DBUS_TYPE_INT32: + return QVariant::Int; + + case DBUS_TYPE_UINT32: + return QVariant::UInt; + + case DBUS_TYPE_INT64: + return QVariant::LongLong; + + case DBUS_TYPE_UINT64: + return QVariant::ULongLong; + + case DBUS_TYPE_DOUBLE: + return QVariant::Double; + + case DBUS_TYPE_STRING: + return QVariant::String; + + case DBUS_TYPE_OBJECT_PATH: + return QDBusMetaTypeId::objectpath; + + case DBUS_TYPE_SIGNATURE: + return QDBusMetaTypeId::signature; + + case DBUS_TYPE_VARIANT: + return QDBusMetaTypeId::variant; + + case DBUS_TYPE_ARRAY: // special case + switch (signature[1]) { + case DBUS_TYPE_BYTE: + return QVariant::ByteArray; + + case DBUS_TYPE_STRING: + return QVariant::StringList; + + case DBUS_TYPE_VARIANT: + return QVariant::List; + + case DBUS_TYPE_OBJECT_PATH: + return qMetaTypeId<QList<QDBusObjectPath> >(); + + case DBUS_TYPE_SIGNATURE: + return qMetaTypeId<QList<QDBusSignature> >(); + + } + // fall through + default: + return QVariant::Invalid; + } +} + +/*! + \fn QDBusMetaType::typeToSignature(int type) + \internal + + Returns the D-Bus signature equivalent to the supplied meta type id \a type. + + More types can be registered with the qDBusRegisterMetaType() function. + + \sa QDBusUtil::isValidSingleSignature(), signatureToType(), + QVariant::type(), QVariant::userType() +*/ +const char *QDBusMetaType::typeToSignature(int type) +{ + // check if it's a static type + switch (type) + { + case QMetaType::UChar: + return DBUS_TYPE_BYTE_AS_STRING; + + case QVariant::Bool: + return DBUS_TYPE_BOOLEAN_AS_STRING; + + case QMetaType::Short: + return DBUS_TYPE_INT16_AS_STRING; + + case QMetaType::UShort: + return DBUS_TYPE_UINT16_AS_STRING; + + case QVariant::Int: + return DBUS_TYPE_INT32_AS_STRING; + + case QVariant::UInt: + return DBUS_TYPE_UINT32_AS_STRING; + + case QVariant::LongLong: + return DBUS_TYPE_INT64_AS_STRING; + + case QVariant::ULongLong: + return DBUS_TYPE_UINT64_AS_STRING; + + case QVariant::Double: + return DBUS_TYPE_DOUBLE_AS_STRING; + + case QVariant::String: + return DBUS_TYPE_STRING_AS_STRING; + + case QVariant::StringList: + return DBUS_TYPE_ARRAY_AS_STRING + DBUS_TYPE_STRING_AS_STRING; // as + + case QVariant::ByteArray: + return DBUS_TYPE_ARRAY_AS_STRING + DBUS_TYPE_BYTE_AS_STRING; // ay + } + + QDBusMetaTypeId::init(); + if (type == QDBusMetaTypeId::variant) + return DBUS_TYPE_VARIANT_AS_STRING; + else if (type == QDBusMetaTypeId::objectpath) + return DBUS_TYPE_OBJECT_PATH_AS_STRING; + else if (type == QDBusMetaTypeId::signature) + return DBUS_TYPE_SIGNATURE_AS_STRING; + + // try the database + QVector<QDBusCustomTypeInfo> *ct = customTypes(); + { + QReadLocker locker(customTypesLock()); + if (type >= ct->size()) + return 0; // type not registered with us + + const QDBusCustomTypeInfo &info = (*ct).at(type); + + if (!info.signature.isNull()) + return info.signature; + + if (!info.marshall) + return 0; // type not registered with us + } + + // call to user code to construct the signature type + QDBusCustomTypeInfo *info; + { + // createSignature will never return a null QByteArray + // if there was an error, it'll return "" + QByteArray signature = QDBusArgumentPrivate::createSignature(type); + + // re-acquire lock + QWriteLocker locker(customTypesLock()); + info = &(*ct)[type]; + info->signature = signature; + } + return info->signature; +} + +QT_END_NAMESPACE diff --git a/src/dbus/qdbusmetatype.h b/src/dbus/qdbusmetatype.h new file mode 100644 index 0000000..7bd8431 --- /dev/null +++ b/src/dbus/qdbusmetatype.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDBUSMETATYPE_H +#define QDBUSMETATYPE_H + +#include "QtCore/qmetatype.h" +#include <QtDBus/qdbusargument.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(DBus) + +class QDBUS_EXPORT QDBusMetaType +{ +public: + typedef void (*MarshallFunction)(QDBusArgument &, const void *); + typedef void (*DemarshallFunction)(const QDBusArgument &, void *); + + static void registerMarshallOperators(int typeId, MarshallFunction, DemarshallFunction); + static bool marshall(QDBusArgument &, int id, const void *data); + static bool demarshall(const QDBusArgument &, int id, void *data); + + static int signatureToType(const char *signature); + static const char *typeToSignature(int type); +}; + +template<typename T> +void qDBusMarshallHelper(QDBusArgument &arg, const T *t) +{ arg << *t; } + +template<typename T> +void qDBusDemarshallHelper(const QDBusArgument &arg, T *t) +{ arg >> *t; } + +template<typename T> +int qDBusRegisterMetaType( +#ifndef qdoc + T * /* dummy */ = 0 +#endif +) +{ + void (*mf)(QDBusArgument &, const T *) = qDBusMarshallHelper<T>; + void (*df)(const QDBusArgument &, T *) = qDBusDemarshallHelper<T>; + + int id = qRegisterMetaType<T>(); // make sure it's registered + QDBusMetaType::registerMarshallOperators(id, + reinterpret_cast<QDBusMetaType::MarshallFunction>(mf), + reinterpret_cast<QDBusMetaType::DemarshallFunction>(df)); + return id; +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/dbus/qdbusmetatype_p.h b/src/dbus/qdbusmetatype_p.h new file mode 100644 index 0000000..f0d40f7 --- /dev/null +++ b/src/dbus/qdbusmetatype_p.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDBUSMETATYPE_P_H +#define QDBUSMETATYPE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include <qdbusmetatype.h> + +QT_BEGIN_NAMESPACE + +struct QDBusMetaTypeId +{ + static int message; // QDBusMessage + static int argument; // QDBusArgument + static int variant; // QDBusVariant + static int objectpath; // QDBusObjectPath + static int signature; // QDBusSignature + static int error; // QDBusError + + static void init(); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/dbus/qdbusmisc.cpp b/src/dbus/qdbusmisc.cpp new file mode 100644 index 0000000..6f89238 --- /dev/null +++ b/src/dbus/qdbusmisc.cpp @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <string.h> + +#include <QtCore/qvariant.h> +#include <QtCore/qmetaobject.h> + +#include "qdbusutil_p.h" +#include "qdbusconnection_p.h" +#include "qdbusmetatype_p.h" + +QT_BEGIN_NAMESPACE + +bool qDBusCheckAsyncTag(const char *tag) +{ + static const char noReplyTag[] = "Q_NOREPLY"; + if (!tag || !*tag) + return false; + + const char *p = strstr(tag, noReplyTag); + if (p != NULL && + (p == tag || *(p-1) == ' ') && + (p[sizeof noReplyTag - 1] == '\0' || p[sizeof noReplyTag - 1] == ' ')) + return true; + + return false; +} + +int qDBusNameToTypeId(const char *name) +{ + int id = static_cast<int>( QVariant::nameToType(name) ); + if (id == QVariant::UserType) + id = QMetaType::type(name); + return id; +} + +// calculates the metatypes for the method +// the slot must have the parameters in the following form: +// - zero or more value or const-ref parameters of any kind +// - zero or one const ref of QDBusMessage +// - zero or more non-const ref parameters +// No parameter may be a template. +// this function returns -1 if the parameters don't match the above form +// this function returns the number of *input* parameters, including the QDBusMessage one if any +// this function does not check the return type, so metaTypes[0] is always 0 and always present +// metaTypes.count() >= retval + 1 in all cases +// +// sig must be the normalised signature for the method +int qDBusParametersForMethod(const QMetaMethod &mm, QList<int>& metaTypes) +{ + QDBusMetaTypeId::init(); + + QList<QByteArray> parameterTypes = mm.parameterTypes(); + metaTypes.clear(); + + metaTypes.append(0); // return type + int inputCount = 0; + bool seenMessage = false; + QList<QByteArray>::ConstIterator it = parameterTypes.constBegin(); + QList<QByteArray>::ConstIterator end = parameterTypes.constEnd(); + for ( ; it != end; ++it) { + const QByteArray &type = *it; + if (type.endsWith('*')) { + //qWarning("Could not parse the method '%s'", mm.signature()); + // pointer? + return -1; + } + + if (type.endsWith('&')) { + QByteArray basictype = type; + basictype.truncate(type.length() - 1); + + int id = qDBusNameToTypeId(basictype); + if (id == 0) { + //qWarning("Could not parse the method '%s'", mm.signature()); + // invalid type in method parameter list + return -1; + } else if (QDBusMetaType::typeToSignature(id) == 0) + return -1; + + metaTypes.append( id ); + seenMessage = true; // it cannot appear anymore anyways + continue; + } + + if (seenMessage) { // && !type.endsWith('&') + //qWarning("Could not parse the method '%s'", mm.signature()); + // non-output parameters after message or after output params + return -1; // not allowed + } + + int id = qDBusNameToTypeId(type); + if (id == 0) { + //qWarning("Could not parse the method '%s'", mm.signature()); + // invalid type in method parameter list + return -1; + } + + if (id == QDBusMetaTypeId::message) + seenMessage = true; + else if (QDBusMetaType::typeToSignature(id) == 0) + return -1; + + metaTypes.append(id); + ++inputCount; + } + + return inputCount; +} + +QT_END_NAMESPACE diff --git a/src/dbus/qdbuspendingcall.cpp b/src/dbus/qdbuspendingcall.cpp new file mode 100644 index 0000000..9753bbe --- /dev/null +++ b/src/dbus/qdbuspendingcall.cpp @@ -0,0 +1,472 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdbuspendingcall.h" +#include "qdbuspendingcall_p.h" + +#include "qdbusconnection_p.h" +#include "qdbusmetatype_p.h" +#include "qcoreapplication.h" +#include "qcoreevent.h" +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QDBusPendingCall + \inmodule QtDBus + \since 4.5 + + \brief The QDBusPendingCall class refers to one pending asynchronous call + + A QDBusPendingCall object is a reference to a method call that was + sent over D-Bus without waiting for a reply. QDBusPendingCall is an + opaque type, meant to be used as a handle for a pending reply. + + In most programs, the QDBusPendingCall class will not be used + directly. It can be safely replaced with the template-based + QDBusPendingReply, in order to access the contents of the reply or + wait for it to be complete. + + The QDBusPendingCallWatcher class allows one to connect to a signal + that will indicate when the reply has arrived or if the call has + timed out. It also provides the + QDBusPendingCallWatcher::waitForFinished() method which will suspend + the execution of the program until the reply has arrived. + + \note If you create a copy of a QDBusPendingCall object, all + information will be shared among the many copies. Therefore, + QDBusPendingCall is an explicitly-shared object and does not + provide a method of detaching the copies (since they refer + to the same pending call) + + \sa QDBusPendingReply, QDBusPendingCallWatcher, + QDBusAbstractInterface::asyncCall() +*/ + +/*! + \class QDBusPendingCallWatcher + \inmodule QtDBus + \since 4.5 + + \brief The QDBusPendingCallWatcher class provides a convenient way for + waiting for asynchronous replies + + The QDBusPendingCallWatcher provides the finished() signal that will be + emitted when a reply arrives. + + It is usually used like the following example: + + \snippet doc/src/snippets/code/src.qdbus.qdbuspendingcall.cpp 0 + + Note that it is not necessary to keep the original QDBusPendingCall + object around since QDBusPendingCallWatcher inherits from that class + too. + + The slot connected to by the above code could be something similar + to the following: + + \snippet doc/src/snippets/code/src.qdbus.qdbuspendingcall.cpp 1 + + Note the use of QDBusPendingReply to validate the argument types in + the reply. If the reply did not contain exactly two arguments + (one string and one QByteArray), QDBusPendingReply::isError() will + return true. + + \sa QDBusPendingReply, QDBusAbstractInterface::asyncCall() +*/ + +/*! + \fn void QDBusPendingCallWatcher::finished(QDBusPendingCallWatcher *self) + + This signal is emitted when the pending call has finished and its + reply is available. The \a self parameter is a pointer to the + object itself, passed for convenience so that the slot can access + the properties and determine the contents of the reply. +*/ + +void QDBusPendingCallWatcherHelper::add(QDBusPendingCallWatcher *watcher) +{ + connect(this, SIGNAL(finished()), watcher, SLOT(_q_finished()), Qt::QueuedConnection); +} + +QDBusPendingCallPrivate::~QDBusPendingCallPrivate() +{ + if (pending) { + q_dbus_pending_call_cancel(pending); + q_dbus_pending_call_unref(pending); + } + delete watcherHelper; +} + +bool QDBusPendingCallPrivate::setReplyCallback(QObject *target, const char *member) +{ + receiver = target; + metaTypes.clear(); + methodIdx = -1; + if (!target) + return true;; // unsetting + + if (!member || !*member) { + // would not be able to deliver a reply + qWarning("QDBusPendingCall::setReplyCallback: error: cannot deliver a reply to %s::%s (%s)", + target ? target->metaObject()->className() : "(null)", + member ? member + 1 : "(null)", + target ? qPrintable(target->objectName()) : "no name"); + return false; + } + + methodIdx = QDBusConnectionPrivate::findSlot(target, member + 1, metaTypes); + if (methodIdx == -1) { + QByteArray normalizedName = QMetaObject::normalizedSignature(member + 1); + methodIdx = QDBusConnectionPrivate::findSlot(target, normalizedName, metaTypes); + } + if (methodIdx == -1) { + // would not be able to deliver a reply + qWarning("QDBusPendingCall::setReplyCallback: error: cannot deliver a reply to %s::%s (%s)", + target->metaObject()->className(), + member + 1, qPrintable(target->objectName())); + return false; + } + + // success + // construct the expected signature + int count = metaTypes.count() - 1; + if (count == 1 && metaTypes.at(1) == QDBusMetaTypeId::message) { + // wildcard slot, can receive anything, so don't set the signature + return true; + } + + if (metaTypes.at(count) == QDBusMetaTypeId::message) + --count; + + // QList<int> is actually a vector + // kids, don't try this at home + setMetaTypes(count, count ? &metaTypes.at(1) : 0); + return true; +} + +void QDBusPendingCallPrivate::setMetaTypes(int count, const int *types) +{ + expectedReplyCount = count; + if (count == 0) { + expectedReplySignature = QLatin1String(""); // not null + return; + } + + QByteArray sig; + sig.reserve(count + count / 2); + for (int i = 0; i < count; ++i) { + const char *typeSig = QDBusMetaType::typeToSignature(types[i]); + if (!typeSig) { + qFatal("QDBusPendingReply: type %s is not registered with QtDBus", + QMetaType::typeName(types[i])); + } + sig += typeSig; + } + + expectedReplySignature = QString::fromLatin1(sig); +} + +void QDBusPendingCallPrivate::checkReceivedSignature() +{ + if (replyMessage.type() == QDBusMessage::InvalidMessage) + return; // not yet finished - no message to + // validate against + if (replyMessage.type() == QDBusMessage::ErrorMessage) + return; // we don't have to check the signature of an error reply + + if (expectedReplySignature.isNull()) + return; // no signature to validate against + + // can't use startsWith here because a null string doesn't start or end with an empty string + if (!replyMessage.signature().indexOf(expectedReplySignature) == 0) { + QString errorMsg = QLatin1String("Unexpected reply signature: got \"%1\", " + "expected \"%2\""); + replyMessage = QDBusMessage::createError( + QDBusError::InvalidSignature, + errorMsg.arg(replyMessage.signature(), expectedReplySignature)); + + } +} + +void QDBusPendingCallPrivate::waitForFinished() +{ + if (replyMessage.type() != QDBusMessage::InvalidMessage) + return; // already finished + + connection->waitForFinished(this); +} + +/*! + Creates a copy of the \a other pending asynchronous call. Note + that both objects will refer to the same pending call. +*/ +QDBusPendingCall::QDBusPendingCall(const QDBusPendingCall &other) + : d(other.d) +{ +} + +/*! + \internal +*/ +QDBusPendingCall::QDBusPendingCall(QDBusPendingCallPrivate *dd) + : d(dd) +{ +} + +/*! + Destroys this copy of the QDBusPendingCall object. If this copy is + also the last copy of a pending asynchronous call, the call will + be canceled and no further notifications will be received. There + will be no way of accessing the reply's contents when it arrives. +*/ +QDBusPendingCall::~QDBusPendingCall() +{ + // d deleted by QExplicitlySharedDataPointer +} + + +/*! + Creates a copy of the \a other pending asynchronous call and drops + the reference to the previously-referenced call. Note that both + objects will refer to the same pending call after this function. + + If this object contained the last reference of a pending + asynchronous call, the call will be canceled and no further + notifications will be received. There will be no way of accessing + the reply's contents when it arrives. +*/ +QDBusPendingCall &QDBusPendingCall::operator=(const QDBusPendingCall &other) +{ + d = other.d; + return *this; +} + +/*! + \fn bool QDBusPendingCallWatcher::isFinished() const + + Returns true if the pending call has finished processing and the + reply has been received. + + Note that this function only changes state if you call + waitForFinished() or if an external D-Bus event happens, which in + general only happens if you return to the event loop execution. + + \sa QDBusPendingReply::isFinished() +*/ +/*! + \fn bool QDBusPendingReply::isFinished() const + + Returns true if the pending call has finished processing and the + reply has been received. If this function returns true, the + isError(), error() and reply() methods should return valid + information. + + Note that this function only changes state if you call + waitForFinished() or if an external D-Bus event happens, which in + general only happens if you return to the event loop execution. + + \sa QDBusPendingCallWatcher::isFinished() +*/ + +bool QDBusPendingCall::isFinished() const +{ + return d && (d->replyMessage.type() != QDBusMessage::InvalidMessage); +} + +void QDBusPendingCall::waitForFinished() +{ + if (d) d->waitForFinished(); +} + +/*! + \fn bool QDBusPendingReply::isValid() const + + Returns true if the reply contains a normal reply message, false + if it contains anything else. + + If the pending call has not finished processing, this function + return false. +*/ +bool QDBusPendingCall::isValid() const +{ + return d ? d->replyMessage.type() == QDBusMessage::ReplyMessage : false; +} + +/*! + \fn bool QDBusPendingReply::isError() const + + Returns true if the reply contains an error message, false if it + contains a normal method reply. + + If the pending call has not finished processing, this function + also returns true. +*/ +bool QDBusPendingCall::isError() const +{ + return d ? d->replyMessage.type() == QDBusMessage::ErrorMessage : true; +} + +/*! + \fn QDBusError QDBusPendingReply::error() const + + Retrieves the error content of the reply message, if it has + finished processing. If the reply message has not finished + processing or if it contains a normal reply message (non-error), + this function returns an invalid QDBusError. +*/ +QDBusError QDBusPendingCall::error() const +{ + if (d) + return d->replyMessage; + + // not connected, return an error + QDBusError err = QDBusError(QDBusError::Disconnected, + QLatin1String("Not connected to D-Bus server")); + return err; +} + +/*! + \fn QDBusMessage QDBusPendingReply::reply() const + + Retrieves the reply message received for the asynchronous call + that was sent, if it has finished processing. If the pending call + is not finished, this function returns a QDBusMessage of type + QDBusMessage::InvalidMessage. + + After it has finished processing, the message type will either be + an error message or a normal method reply message. +*/ +QDBusMessage QDBusPendingCall::reply() const +{ + return d ? d->replyMessage : QDBusMessage::createError(error()); +} + +#if 0 +/*! + Sets the slot \a member in object \a target to be called when the + reply arrives. The slot's parameter list must match the reply + message's arguments for it to be called. + + It may, optionally, contain a QDBusMessage final parameter. If it + is present, the parameter will contain the reply message object. + + The callback will not be called if the reply is an error message. + + This function returns true if it could set the callback, false + otherwise. It is not a guarantee that the callback will be + called. + + \warning QDBusPendingCall only supports one callback per pending + asynchronous call, even if multiple QDBusPendingCall + objects are referencing the same pending call. +*/ +bool QDBusPendingCall::setReplyCallback(QObject *target, const char *member) +{ + if (!d) + return false; + + return d->setReplyCallback(target, member); +} +#endif + + +class QDBusPendingCallWatcherPrivate: public QObjectPrivate +{ +public: + void _q_finished(); + + Q_DECLARE_PUBLIC(QDBusPendingCallWatcher) +}; + +inline void QDBusPendingCallWatcherPrivate::_q_finished() +{ + Q_Q(QDBusPendingCallWatcher); + emit q->finished(q); +} + +/*! + Creates a QDBusPendingCallWatcher object to watch for replies on the + asynchronous pending call \a call and sets this object's parent + to \a parent. +*/ +QDBusPendingCallWatcher::QDBusPendingCallWatcher(const QDBusPendingCall &call, QObject *parent) + : QObject(*new QDBusPendingCallWatcherPrivate, parent), QDBusPendingCall(call) +{ + if (d) { + if (!d->watcherHelper) + d->watcherHelper = new QDBusPendingCallWatcherHelper; + d->watcherHelper->add(this); + } +} + +/*! + Destroys this object. If this QDBusPendingCallWatcher object was the + last reference to the unfinished pending call, the call will be + canceled. +*/ +QDBusPendingCallWatcher::~QDBusPendingCallWatcher() +{ +} + +/*! + \fn void QDBusPendingCallWatcher::waitForFinished() + + Suspends the execution of the calling thread until the reply is + received and processed. After this function returns, isFinished() + should return true, indicating the reply's contents are ready to + be processed. + + \sa QDBusPendingReply::waitForFinished() +*/ +void QDBusPendingCallWatcher::waitForFinished() +{ + if (d) { + d->waitForFinished(); + + // our signals were queued, so deliver them + QCoreApplication::sendPostedEvents(this, QEvent::MetaCall); + } +} +QT_END_NAMESPACE + +#include "moc_qdbuspendingcall.cpp" diff --git a/src/dbus/qdbuspendingcall.h b/src/dbus/qdbuspendingcall.h new file mode 100644 index 0000000..7602db3 --- /dev/null +++ b/src/dbus/qdbuspendingcall.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDBUSPENDINGCALL_H +#define QDBUSPENDINGCALL_H + +#include <QtCore/qglobal.h> +#include <QtCore/qobject.h> +#include <QtCore/qshareddata.h> + +#include <QtDBus/qdbusmacros.h> +#include <QtDBus/qdbusmessage.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(DBus) + +class QDBusConnection; +class QDBusError; +class QDBusPendingCallWatcher; + +class QDBusPendingCallPrivate; +class QDBUS_EXPORT QDBusPendingCall +{ +public: + QDBusPendingCall(const QDBusPendingCall &other); + ~QDBusPendingCall(); + QDBusPendingCall &operator=(const QDBusPendingCall &other); + +#ifndef Q_QDOC + // pretend that they aren't here + bool isFinished() const; + void waitForFinished(); + + bool isError() const; + bool isValid() const; + QDBusError error() const; + QDBusMessage reply() const; +#endif + +protected: + QExplicitlySharedDataPointer<QDBusPendingCallPrivate> d; + friend class QDBusPendingCallPrivate; + friend class QDBusPendingCallWatcher; + friend class QDBusConnection; + + QDBusPendingCall(QDBusPendingCallPrivate *dd); + +private: + QDBusPendingCall(); // not defined +}; + +class QDBusPendingCallWatcherPrivate; +class QDBUS_EXPORT QDBusPendingCallWatcher: public QObject, public QDBusPendingCall +{ + Q_OBJECT +public: + QDBusPendingCallWatcher(const QDBusPendingCall &call, QObject *parent = 0); + ~QDBusPendingCallWatcher(); + +#ifdef Q_QDOC + // trick qdoc into thinking this method is here + bool isFinished() const; +#endif + void waitForFinished(); // non-virtual override + +Q_SIGNALS: + void finished(QDBusPendingCallWatcher *self); + +private: + Q_DECLARE_PRIVATE(QDBusPendingCallWatcher) + Q_PRIVATE_SLOT(d_func(), void _q_finished()) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/dbus/qdbuspendingcall_p.h b/src/dbus/qdbuspendingcall_p.h new file mode 100644 index 0000000..877cab9 --- /dev/null +++ b/src/dbus/qdbuspendingcall_p.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the public API. This header file may +// change from version to version without notice, or even be +// removed. +// +// We mean it. +// +// + +#ifndef QDBUSPENDINGCALL_P_H +#define QDBUSPENDINGCALL_P_H + +#include <qshareddata.h> +#include <qpointer.h> +#include <qlist.h> + +#include "qdbusmessage.h" +#include "qdbus_symbols_p.h" + +QT_BEGIN_NAMESPACE + +class QDBusPendingCallWatcher; +class QDBusPendingCallWatcherHelper; +class QDBusConnectionPrivate; + +class QDBusPendingCallPrivate: public QSharedData +{ +public: + QDBusMessage sentMessage; + QDBusMessage replyMessage; +// QDBusMessage pendingReplyMessage; // used in the local loop + QDBusPendingCallWatcherHelper *watcherHelper; + DBusPendingCall *pending; + QDBusConnectionPrivate *connection; + + QString expectedReplySignature; + int expectedReplyCount; + + // for the callback + QPointer<QObject> receiver; + QList<int> metaTypes; + int methodIdx; + + bool autoDelete; + + QDBusPendingCallPrivate() : watcherHelper(0), pending(0), autoDelete(false) + { } + ~QDBusPendingCallPrivate(); + bool setReplyCallback(QObject *target, const char *member); + void waitForFinished(); + void setMetaTypes(int count, const int *types); + void checkReceivedSignature(); +}; + +class QDBusPendingCallWatcherHelper: public QObject +{ + Q_OBJECT +public: + void add(QDBusPendingCallWatcher *watcher); + + void emitSignals(const QDBusMessage &replyMessage, const QDBusMessage &sentMessage) + { + if (replyMessage.type() == QDBusMessage::ReplyMessage) + emit reply(replyMessage); + else + emit error(replyMessage, sentMessage); + emit finished(); + } + +Q_SIGNALS: + void finished(); + void reply(const QDBusMessage &msg); + void error(const QDBusError &error, const QDBusMessage &msg); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/dbus/qdbuspendingreply.cpp b/src/dbus/qdbuspendingreply.cpp new file mode 100644 index 0000000..382b555 --- /dev/null +++ b/src/dbus/qdbuspendingreply.cpp @@ -0,0 +1,277 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdbuspendingreply.h" +#include "qdbuspendingcall_p.h" +#include "qdbusmetatype.h" + +/*! + \class QDBusPendingReply + \inmodule QtDBus + \since 4.5 + + \brief The QDBusPendingReply class contains the reply to an asynchronous method call + + The QDBusPendingReply is a template class with up to 8 template + parameters. Those parameters are the types that will be used to + extract the contents of the reply's data. + + This class is similar in functionality to QDBusReply, but with two + important differences: + + \list + \o QDBusReply accepts exactly one return type, whereas + QDBusPendingReply can have from 1 to 8 types + \o QDBusReply only works on already completed replies, whereas + QDBusPendingReply allows one to wait for replies from pending + calls + \endlist + + Where with QDBusReply you would write: + + \snippet doc/src/snippets/code/src_qdbus_qdbusreply.cpp 0 + + with QDBusPendingReply, the equivalent code (including the blocking + wait for the reply) would be: + + \snippet doc/src/snippets/code/src.qdbus.qdbuspendingreply.cpp 0 + + For method calls that have more than one output argument, with + QDBusReply, you would write: + + \snippet doc/src/snippets/code/src_qdbus_qdbusreply.cpp 1 + + whereas with QDBusPendingReply, all of the output arguments should + be template parameters: + + \snippet doc/src/snippets/code/src.qdbus.qdbuspendingreply.cpp 2 + + QDBusPendingReply objects can be associated with + QDBusPendingCallWatcher objects, which emit signals when the reply + arrives. + + \sa QDBusPendingCallWatcher, QDBusReply, + QDBusAbstractInterface::asyncCall() +*/ + +/*! + \fn QDBusPendingReply::QDBusPendingReply() + + Creates an empty QDBusPendingReply object. Without assigning a + QDBusPendingCall object to this reply, QDBusPendingReply cannot do + anything. All functions return their failure values. +*/ + +/*! + \fn QDBusPendingReply::QDBusPendingReply(const QDBusPendingReply &other) + + Creates a copy of the \a other QDBusPendingReply object. Just like + QDBusPendingCall and QDBusPendingCallWatcher, this QDBusPendingReply + object will share the same pending call reference. All copies + share the same return values. +*/ + +/*! + \fn QDBusPendingReply::QDBusPendingReply(const QDBusPendingCall &call) + + Creates a QDBusPendingReply object that will take its contents from + the \a call pending asynchronous call. This QDBusPendingReply object + will share the same pending call reference as \a call. +*/ + +/*! + \fn QDBusPendingReply::QDBusPendingReply(const QDBusMessage &message) + + Creates a QDBusPendingReply object that will take its contents from + the message \a message. In this case, this object will be already + in its finished state and the reply's contents will be accessible. + + \sa isFinished() +*/ + +/*! + \fn QDBusPendingReply &QDBusPendingReply::operator=(const QDBusPendingReply &other) + + Makes a copy of \a other and drops the reference to the current + pending call. If the current reference is to an unfinished pending + call and this is the last reference, the pending call will be + canceled and there will be no way of retrieving the reply's + contents, when they arrive. +*/ + +/*! + \fn QDBusPendingReply &QDBusPendingReply::operator=(const QDBusPendingCall &call) + + Makes this object take its contents from the \a call pending call + and drops the reference to the current pending call. If the + current reference is to an unfinished pending call and this is the + last reference, the pending call will be canceled and there will + be no way of retrieving the reply's contents, when they arrive. +*/ + +/*! + \fn QDBusPendingReply &QDBusPendingReply::operator=(const QDBusMessage &message) + + Makes this object take its contents from the \a message message + and drops the reference to the current pending call. If the + current reference is to an unfinished pending call and this is the + last reference, the pending call will be canceled and there will + be no way of retrieving the reply's contents, when they arrive. + + After this function is finished, the QDBusPendingReply object will + be in its "finished" state and the \a message contents will be + accessible. + + \sa isFinished() +*/ + +/*! + \fn int QDBusPendingReply::count() const + + Return the number of arguments the reply is supposed to have. This + number matches the number of non-void template parameters in this + class. + + If the reply arrives with a different number of arguments (or with + different types), it will be transformed into an error reply + indicating a bad signature. +*/ + +/*! + \fn QVariant QDBusPendingReply::argumentAt(int index) const + + Returns the argument at position \a index in the reply's + contents. If the reply doesn't have that many elements, this + function's return value is undefined (will probably cause an + assertion failure), so it is important to verify that the + processing is finished and the reply is valid. +*/ + +/*! + \fn Type QDBusPendingReply::argumentAt() const + + Returns the argument at position \c Index (which is a template + parameter) cast to type \c Type. This function uses template code + to determine the proper \c Type type, according to the type list + used in the construction of this object. + + Note that, if the reply hasn't arrived, this function causes the + calling thread to block until the reply is processed. +*/ + +/*! + \fn T1 QDBusPendingReply::value() const + + Returns the first argument in this reply, cast to type \c T1 (the + first template parameter of this class). This is equivalent to + calling argumentAt<0>(). + + This function is provided as a convenience, matching the + QDBusReply::value() function. + + Note that, if the reply hasn't arrived, this function causes the + calling thread to block until the reply is processed. +*/ + +/*! + \fn QDBusPendingReply::operator T1() const + + Returns the first argument in this reply, cast to type \c T1 (the + first template parameter of this class). This is equivalent to + calling argumentAt<0>(). + + This function is provided as a convenience, matching the + QDBusReply::value() function. + + Note that, if the reply hasn't arrived, this function causes the + calling thread to block until the reply is processed. +*/ + +/*! + \fn void QDBusPendingReply::waitForFinished() + + Suspends the execution of the calling thread until the reply is + received and processed. After this function returns, isFinished() + should return true, indicating the reply's contents are ready to + be processed. + + \sa QDBusPendingCallWatcher::waitForFinished() +*/ + +QDBusPendingReplyData::QDBusPendingReplyData() + : QDBusPendingCall(0) // initialize base class empty +{ +} + +QDBusPendingReplyData::~QDBusPendingReplyData() +{ +} + +void QDBusPendingReplyData::assign(const QDBusPendingCall &other) +{ + QDBusPendingCall::operator=(other); +} + +void QDBusPendingReplyData::assign(const QDBusMessage &message) +{ + d = new QDBusPendingCallPrivate; // drops the reference to the old one + d->replyMessage = message; +} + +QVariant QDBusPendingReplyData::argumentAt(int index) const +{ + if (d) + d->waitForFinished(); // bypasses "const" + + Q_ASSERT_X(d && index >= 0 && index < d->replyMessage.arguments().count(), + "QDBusPendingReply::argumentAt", + "Index out of bounds"); + + return d->replyMessage.arguments().at(index); +} + +void QDBusPendingReplyData::setMetaTypes(int count, const int *types) +{ + Q_ASSERT(d); + d->setMetaTypes(count, types); + d->checkReceivedSignature(); +} + diff --git a/src/dbus/qdbuspendingreply.h b/src/dbus/qdbuspendingreply.h new file mode 100644 index 0000000..3880a7f --- /dev/null +++ b/src/dbus/qdbuspendingreply.h @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDBUSPENDINGREPLY_H +#define QDBUSPENDINGREPLY_H + +#include <QtCore/qglobal.h> +#include <QtDBus/qdbusmacros.h> +#include <QtDBus/qdbusargument.h> +#include <QtDBus/qdbuspendingcall.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(DBus) + +class QDBUS_EXPORT QDBusPendingReplyData: public QDBusPendingCall +{ +protected: + QDBusPendingReplyData(); + ~QDBusPendingReplyData(); + void assign(const QDBusPendingCall &call); + void assign(const QDBusMessage &message); + + QVariant argumentAt(int index) const; + void setMetaTypes(int count, const int *metaTypes); +}; + +namespace QDBusPendingReplyTypes { + template<int Index, + typename T1, typename T2, typename T3, typename T4, + typename T5, typename T6, typename T7, typename T8> + struct Select + { + typedef Select<Index - 1, T2, T3, T4, T5, T6, T7, T8, void> Next; + typedef typename Next::Type Type; + }; + template<typename T1, typename T2, typename T3, typename T4, + typename T5, typename T6, typename T7, typename T8> + struct Select<0, T1, T2, T3, T4, T5, T6, T7, T8> + { + typedef T1 Type; + }; + + template<typename T1> inline int metaTypeFor(T1 * = 0) + { return qMetaTypeId<T1>(); } + // specialise for QVariant, allowing it to be used in place of QDBusVariant + template<> inline int metaTypeFor<QVariant>(QVariant *) + { return qMetaTypeId<QDBusVariant>(); } + + template<typename T1, typename T2, typename T3, typename T4, + typename T5, typename T6, typename T7, typename T8> + struct ForEach + { + typedef ForEach<T2, T3, T4, T5, T6, T7, T8, void> Next; + enum { Total = Next::Total + 1 }; + static inline void fillMetaTypes(int *p) + { + *p = metaTypeFor<T1>(); + Next::fillMetaTypes(++p); + } + }; + template<> + struct ForEach<void, void, void, void, void, void, void, void> + { + enum { Total = 0 }; + static inline void fillMetaTypes(int *) + { } + }; +} // namespace QDBusPendingReplyTypes + +template<typename T1 = void, typename T2 = void, typename T3 = void, typename T4 = void, + typename T5 = void, typename T6 = void, typename T7 = void, typename T8 = void> +class QDBusPendingReply: +#ifdef Q_QDOC + public QDBusPendingCall +#else + public QDBusPendingReplyData +#endif +{ + typedef QDBusPendingReplyTypes::ForEach<T1, T2, T3, T4, T5, T6, T7, T8> ForEach; + template<int Index> struct Select : + QDBusPendingReplyTypes::Select<Index, T1, T2, T3, T4, T5, T6, T7, T8> + { + }; + +public: + enum { Count = ForEach::Total }; + + inline QDBusPendingReply() + { } + inline QDBusPendingReply(const QDBusPendingReply &other) + : QDBusPendingReplyData(other) + { } + inline QDBusPendingReply(const QDBusPendingCall &call) + { *this = call; } + inline QDBusPendingReply(const QDBusMessage &message) + { *this = message; } + inline QDBusPendingReply &operator=(const QDBusPendingReply &other) + { assign(other); return *this; } + inline QDBusPendingReply &operator=(const QDBusPendingCall &call) + { assign(call); return *this; } + inline QDBusPendingReply &operator=(const QDBusMessage &message) + { assign(message); return *this; } + + inline int count() const { return Count; } + +#if defined(Q_QDOC) || defined(Q_NO_USING_KEYWORD) + inline QVariant argumentAt(int index) const + { return QDBusPendingReplyData::argumentAt(index); } +#else + using QDBusPendingReplyData::argumentAt; +#endif + +#if defined(Q_QDOC) + bool isFinished() const; + void waitForFinished(); + + bool isValid() const; + bool isError() const; + QDBusError error() const; + QDBusMessage reply() const; + + template<int Index> inline Type argumentAt() const; + inline T1 value() const; + inline operator T1() const; +#else + template<int Index> inline + const typename Select<Index>::Type argumentAt() const + { + // static assert? + Q_ASSERT_X(Index < count() && Index >= 0, "QDBusPendingReply::argumentAt", + "Index out of bounds"); + typedef typename Select<Index>::Type ResultType; + return qdbus_cast<ResultType>(argumentAt(Index)); + } + + inline typename Select<0>::Type value() const + { + return argumentAt<0>(); + } + + inline operator typename Select<0>::Type() const + { + return argumentAt<0>(); + } +#endif + +private: + inline void calculateMetaTypes() + { + int typeIds[Count > 0 ? Count : 1]; // use at least one since zero-sized arrays aren't valid + ForEach::fillMetaTypes(typeIds); + setMetaTypes(Count, typeIds); + } + + inline void assign(const QDBusPendingCall &call) + { + QDBusPendingReplyData::assign(call); + calculateMetaTypes(); + } + + inline void assign(const QDBusMessage &message) + { + QDBusPendingReplyData::assign(message); + calculateMetaTypes(); + } +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/dbus/qdbusreply.cpp b/src/dbus/qdbusreply.cpp new file mode 100644 index 0000000..2b322ec --- /dev/null +++ b/src/dbus/qdbusreply.cpp @@ -0,0 +1,244 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdbusreply.h" +#include "qdbusmetatype.h" +#include "qdbusmetatype_p.h" +#include <QDebug> + +QT_BEGIN_NAMESPACE + +/*! + \class QDBusReply + \inmodule QtDBus + \since 4.2 + + \brief The QDBusReply class stores the reply for a method call to a remote object. + + A QDBusReply object is a subset of the QDBusMessage object that represents a method call's + reply. It contains only the first output argument or the error code and is used by + QDBusInterface-derived classes to allow returning the error code as the function's return + argument. + + It can be used in the following manner: + \snippet doc/src/snippets/code/src_qdbus_qdbusreply.cpp 0 + + If the remote method call cannot fail, you can skip the error checking: + \snippet doc/src/snippets/code/src_qdbus_qdbusreply.cpp 1 + + However, if it does fail under those conditions, the value returned by QDBusReply::value() is + a default-constructed value. It may be indistinguishable from a valid return value. + + QDBusReply objects are used for remote calls that have no output + arguments or return values (i.e., they have a "void" return + type). Use the isValid() function to test if the reply succeeded. + + \sa QDBusMessage, QDBusInterface +*/ + +/*! + \fn QDBusReply::QDBusReply(const QDBusMessage &reply) + Automatically construct a QDBusReply object from the reply message \a reply, extracting the + first return value from it if it is a success reply. +*/ + +/*! + \fn QDBusReply::QDBusReply(const QDBusPendingReply<T> &reply) + Constructs a QDBusReply object from the pending reply message, \a reply. +*/ + +/*! + \fn QDBusReply::QDBusReply(const QDBusPendingCall &pcall) + Automatically construct a QDBusReply object from the asynchronous + pending call \a pcall. If the call isn't finished yet, QDBusReply + will call QDBusPendingCall::waitForFinished(), which is a blocking + operation. + + If the return types patch, QDBusReply will extract the first + return argument from the reply. +*/ + +/*! + \fn QDBusReply::QDBusReply(const QDBusError &error) + Constructs an error reply from the D-Bus error code given by \a error. +*/ + +/*! + \fn QDBusReply::operator=(const QDBusReply &other) + Makes this object be a copy of the object \a other. +*/ + +/*! + \fn QDBusReply::operator=(const QDBusError &error) + Sets this object to contain the error code given by \a error. You + can later access it with error(). +*/ + +/*! + \fn QDBusReply::operator=(const QDBusMessage &message) + + Makes this object contain the reply specified by message \a + message. If \a message is an error message, this function will + copy the error code and message into this object + + If \a message is a standard reply message and contains at least + one parameter, it will be copied into this object, as long as it + is of the correct type. If it's not of the same type as this + QDBusError object, this function will instead set an error code + indicating a type mismatch. +*/ + +/*! + \fn QDBusReply::operator=(const QDBusPendingCall &pcall) + + Makes this object contain the reply specified by the pending + asynchronous call \a pcall. If the call is not finished yet, this + function will call QDBusPendingCall::waitForFinished() to block + until the reply arrives. + + If \a pcall finishes with an error message, this function will + copy the error code and message into this object + + If \a pcall finished with a standard reply message and contains at + least one parameter, it will be copied into this object, as long + as it is of the correct type. If it's not of the same type as this + QDBusError object, this function will instead set an error code + indicating a type mismatch. +*/ + +/*! + \fn bool QDBusReply::isValid() const + + Returns true if no error occurred; otherwise, returns false. + + \sa error() +*/ + +/*! + \fn QDBusReply::error() + + Returns the error code that was returned from the remote function call. If the remote call did + not return an error (i.e., if it succeeded), then the QDBusError object that is returned will + not be a valid error code (QDBusError::isValid() will return false). + + \sa isValid() +*/ + +/*! + \fn QDBusReply::value() const + Returns the remote function's calls return value. If the remote call returned with an error, + the return value of this function is undefined and may be undistinguishable from a valid return + value. + + This function is not available if the remote call returns \c void. +*/ + +/*! + \fn QDBusReply::operator Type() const + Returns the same as value(). + + This function is not available if the remote call returns \c void. +*/ + +/*! + \internal + Fills in the QDBusReply data \a error and \a data from the reply message \a reply. +*/ +void qDBusReplyFill(const QDBusMessage &reply, QDBusError &error, QVariant &data) +{ + error = reply; + + if (error.isValid()) { + data = QVariant(); // clear it + return; + } + + if (reply.arguments().count() >= 1 && reply.arguments().at(0).userType() == data.userType()) { + data = reply.arguments().at(0); + return; + } + + const char *expectedSignature = QDBusMetaType::typeToSignature(data.userType()); + const char *receivedType = 0; + QByteArray receivedSignature; + + if (reply.arguments().count() >= 1) { + if (reply.arguments().at(0).userType() == QDBusMetaTypeId::argument) { + // compare signatures instead + QDBusArgument arg = qvariant_cast<QDBusArgument>(reply.arguments().at(0)); + receivedSignature = arg.currentSignature().toLatin1(); + if (receivedSignature == expectedSignature) { + // matched. Demarshall it + QDBusMetaType::demarshall(arg, data.userType(), data.data()); + return; + } + } else { + // not an argument and doesn't match? + int type = reply.arguments().at(0).userType(); + receivedType = QVariant::typeToName(QVariant::Type(type)); + receivedSignature = QDBusMetaType::typeToSignature(type); + } + } + + // error + if (receivedSignature.isEmpty()) + receivedSignature = "no signature"; + QString errorMsg; + if (receivedType) { + errorMsg = QString::fromLatin1("Unexpected reply signature: got \"%1\" (%4), " + "expected \"%2\" (%3)") + .arg(QLatin1String(receivedSignature), + QLatin1String(expectedSignature), + QLatin1String(data.typeName()), + QLatin1String(receivedType)); + } else { + errorMsg = QString::fromLatin1("Unexpected reply signature: got \"%1\", " + "expected \"%2\" (%3)") + .arg(QLatin1String(receivedSignature), + QLatin1String(expectedSignature), + QLatin1String(data.typeName())); + } + + error = QDBusError(QDBusError::InvalidSignature, errorMsg); + data = QVariant(); // clear it +} + +QT_END_NAMESPACE diff --git a/src/dbus/qdbusreply.h b/src/dbus/qdbusreply.h new file mode 100644 index 0000000..182989c --- /dev/null +++ b/src/dbus/qdbusreply.h @@ -0,0 +1,196 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDBUSREPLY_H +#define QDBUSREPLY_H + +#include <QtCore/qglobal.h> +#include <QtCore/qvariant.h> + +#include <QtDBus/qdbusmacros.h> +#include <QtDBus/qdbusmessage.h> +#include <QtDBus/qdbuserror.h> +#include <QtDBus/qdbusextratypes.h> +#include <QtDBus/qdbuspendingreply.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(DBus) + +QDBUS_EXPORT void qDBusReplyFill(const QDBusMessage &reply, QDBusError &error, QVariant &data); + +template<typename T> +class QDBusReply +{ + typedef T Type; +public: + inline QDBusReply(const QDBusMessage &reply) + { + *this = reply; + } + inline QDBusReply& operator=(const QDBusMessage &reply) + { + QVariant data(qMetaTypeId(&m_data), reinterpret_cast<void*>(0)); + qDBusReplyFill(reply, m_error, data); + m_data = qvariant_cast<Type>(data); + return *this; + } + + inline QDBusReply(const QDBusPendingCall &pcall) + { + *this = pcall; + } + inline QDBusReply &operator=(const QDBusPendingCall &pcall) + { + QDBusPendingCall other(pcall); + other.waitForFinished(); + return *this = other.reply(); + } + inline QDBusReply(const QDBusPendingReply<T> &reply) + { + *this = static_cast<QDBusPendingCall>(reply); + } + + inline QDBusReply(const QDBusError &dbusError = QDBusError()) + : m_error(dbusError), m_data(Type()) + { + } + inline QDBusReply& operator=(const QDBusError& dbusError) + { + m_error = dbusError; + m_data = Type(); + return *this; + } + + inline QDBusReply& operator=(const QDBusReply& other) + { + m_error = other.m_error; + m_data = other.m_data; + return *this; + } + + inline bool isValid() const { return !m_error.isValid(); } + + inline const QDBusError& error() { return m_error; } + + inline Type value() const + { + return m_data; + } + + inline operator Type () const + { + return m_data; + } + +private: + QDBusError m_error; + Type m_data; +}; + +# ifndef Q_QDOC +// specialize for QVariant: +template<> inline QDBusReply<QVariant>& +QDBusReply<QVariant>::operator=(const QDBusMessage &reply) +{ + void *null = 0; + QVariant data(qMetaTypeId<QDBusVariant>(), null); + qDBusReplyFill(reply, m_error, data); + m_data = qvariant_cast<QDBusVariant>(data).variant(); + return *this; +} + +// specialize for void: +template<> +class QDBusReply<void> +{ +public: + inline QDBusReply(const QDBusMessage &reply) + : m_error(reply) + { + } + inline QDBusReply& operator=(const QDBusMessage &reply) + { + m_error = reply; + return *this; + } + inline QDBusReply(const QDBusError &dbusError = QDBusError()) + : m_error(dbusError) + { + } + inline QDBusReply(const QDBusPendingCall &pcall) + { + *this = pcall; + } + inline QDBusReply &operator=(const QDBusPendingCall &pcall) + { + QDBusPendingCall other(pcall); + other.waitForFinished(); + return *this = other.reply(); + } + inline QDBusReply& operator=(const QDBusError& dbusError) + { + m_error = dbusError; + return *this; + } + + inline QDBusReply& operator=(const QDBusReply& other) + { + m_error = other.m_error; + return *this; + } + + inline bool isValid() const { return !m_error.isValid(); } + + inline const QDBusError& error() { return m_error; } + +private: + QDBusError m_error; +}; +# endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/dbus/qdbusserver.cpp b/src/dbus/qdbusserver.cpp new file mode 100644 index 0000000..57ff60b --- /dev/null +++ b/src/dbus/qdbusserver.cpp @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdbusserver.h" +#include "qdbusconnection_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QDBusServer + \inmodule QtDBus + \since 4.2 + + \brief The QDBusServer class provides peer-to-peer communication + between processes on the same computer. +*/ + +/*! + Constructs a QDBusServer with the given \a address, and the given + \a parent. +*/ +QDBusServer::QDBusServer(const QString &address, QObject *parent) + : QObject(parent) +{ + if (!qdbus_loadLibDBus()) { + d = 0; + return; + } + d = new QDBusConnectionPrivate(this); + + if (address.isEmpty()) + return; + + QObject::connect(d, SIGNAL(newServerConnection(const QDBusConnection &)), + this, SIGNAL(newConnection(const QDBusConnection &))); + + // server = q_dbus_server_listen( "unix:tmpdir=/tmp", &error); + QDBusErrorInternal error; + d->setServer(q_dbus_server_listen(address.toUtf8().constData(), error), error); +} + +/*! + Returns true if this QDBusServer object is connected. + + If it isn't connected, you need to call the constructor again. +*/ +bool QDBusServer::isConnected() const +{ + return d && d->server && q_dbus_server_get_is_connected(d->server); +} + +/*! + Returns the last error that happened in this server. + + This function is provided for low-level code. +*/ +QDBusError QDBusServer::lastError() const +{ + return d->lastError; +} + +/*! + Returns the address this server is assosiated with. +*/ +QString QDBusServer::address() const +{ + QString addr; + if (d && d->server) { + char *c = q_dbus_server_get_address(d->server); + addr = QString::fromUtf8(c); + q_dbus_free(c); + } + + return addr; +} +/*! + \fn void QDBusServer::newConnection(const QDBusConnection &connection) + + This signal is currently not used, but if and when it is + used, \a connection will be the new connection. + */ + +QT_END_NAMESPACE diff --git a/src/dbus/qdbusserver.h b/src/dbus/qdbusserver.h new file mode 100644 index 0000000..f347b6c --- /dev/null +++ b/src/dbus/qdbusserver.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QDBUSSERVER_H +#define QDBUSSERVER_H + +#include <QtCore/qobject.h> +#include <QtCore/qstring.h> +#include <QtDBus/qdbusmacros.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(DBus) + +class QDBusConnectionPrivate; +class QDBusError; +class QDBusConnection; + +class QDBUS_EXPORT QDBusServer: public QObject +{ + Q_OBJECT +public: + QDBusServer(const QString &address, QObject *parent = 0); + + bool isConnected() const; + QDBusError lastError() const; + QString address() const; + +Q_SIGNALS: + void newConnection(const QDBusConnection &connection); + +private: + Q_DISABLE_COPY(QDBusServer) + QDBusConnectionPrivate *d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/dbus/qdbusthread.cpp b/src/dbus/qdbusthread.cpp new file mode 100644 index 0000000..1ff52bc --- /dev/null +++ b/src/dbus/qdbusthread.cpp @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qmutex.h> +#include <QtCore/qwaitcondition.h> + +#include <stdlib.h> +#include <qdbus_symbols_p.h> + +QT_USE_NAMESPACE + +static DBusMutex* mutex_new() +{ + return reinterpret_cast<DBusMutex *>(new QMutex(QMutex::NonRecursive)); +} + +#if 0 +static DBusMutex* recursive_mutex_new() +{ + return reinterpret_cast<DBusMutex *>(new QMutex(QMutex::Recursive)); +} +#endif + +static void mutex_free(DBusMutex *mutex) +{ + delete reinterpret_cast<QMutex *>(mutex); +} + +static dbus_bool_t mutex_lock(DBusMutex *mutex) +{ + reinterpret_cast<QMutex *>(mutex)->lock(); + return true; +} + +static dbus_bool_t mutex_unlock(DBusMutex *mutex) +{ + reinterpret_cast<QMutex *>(mutex)->unlock(); + return true; +} + +static DBusCondVar* condvar_new() +{ + return reinterpret_cast<DBusCondVar *>(new QWaitCondition); +} + +static void condvar_free(DBusCondVar *cond) +{ + delete reinterpret_cast<QWaitCondition *>(cond); +} + +static void condvar_wait(DBusCondVar *cond, DBusMutex *mutex) +{ + reinterpret_cast<QWaitCondition *>(cond)->wait(reinterpret_cast<QMutex *>(mutex)); +} + +static dbus_bool_t condvar_wait_timeout(DBusCondVar *cond, DBusMutex *mutex, int msec) +{ + return reinterpret_cast<QWaitCondition *>(cond)->wait(reinterpret_cast<QMutex *>(mutex), msec); +} + +static void condvar_wake_one(DBusCondVar *cond) +{ + reinterpret_cast<QWaitCondition *>(cond)->wakeOne(); +} + +static void condvar_wake_all(DBusCondVar *cond) +{ + reinterpret_cast<QWaitCondition *>(cond)->wakeAll(); +} + +QT_BEGIN_NAMESPACE + +bool qDBusInitThreads() +{ + // ### Disable the recursive mutex functions. + static DBusThreadFunctions fcn = { + DBUS_THREAD_FUNCTIONS_MUTEX_NEW_MASK | + DBUS_THREAD_FUNCTIONS_MUTEX_FREE_MASK | + DBUS_THREAD_FUNCTIONS_MUTEX_LOCK_MASK | + DBUS_THREAD_FUNCTIONS_MUTEX_UNLOCK_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK, +#if 0 + DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_NEW_MASK | + DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_FREE_MASK | + DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_LOCK_MASK | + DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_UNLOCK_MASK, +#endif + mutex_new, + mutex_free, + mutex_lock, + mutex_unlock, + condvar_new, + condvar_free, + condvar_wait, + condvar_wait_timeout, + condvar_wake_one, + condvar_wake_all, +#if 0 + recursive_mutex_new, + mutex_free, + mutex_lock, + mutex_unlock, +#else + 0, 0, 0, 0, +#endif + 0, 0, 0, 0 + }; + +#if !defined QT_LINKED_LIBDBUS + void (*threads_init_default)() = (void (*)())qdbus_resolve_conditionally("dbus_threads_init_default"); + void (*threads_init)(DBusThreadFunctions *) = (void (*)(DBusThreadFunctions*))qdbus_resolve_conditionally("dbus_threads_init"); + + if (threads_init_default) + threads_init_default(); + else if (threads_init) + threads_init(&fcn); + else + return false; + + return true; +#else + dbus_threads_init(&fcn); + + return true; +#endif +} + +QT_END_NAMESPACE diff --git a/src/dbus/qdbusthreaddebug_p.h b/src/dbus/qdbusthreaddebug_p.h new file mode 100644 index 0000000..715bd6f --- /dev/null +++ b/src/dbus/qdbusthreaddebug_p.h @@ -0,0 +1,229 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QDBUSTHREADDEBUG_P_H +#define QDBUSTHREADDEBUG_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#if !defined(QDBUS_THREAD_DEBUG) && defined(QT_BUILD_INTERNAL) +# define QDBUS_THREAD_DEBUG 1 +#endif + +#if QDBUS_THREAD_DEBUG +QT_BEGIN_NAMESPACE +typedef void (*qdbusThreadDebugFunc)(int, int, QDBusConnectionPrivate *); +QDBUS_EXPORT void qdbusDefaultThreadDebug(int, int, QDBusConnectionPrivate *); +extern QDBUS_EXPORT qdbusThreadDebugFunc qdbusThreadDebug; +QT_END_NAMESPACE +#endif + +enum ThreadAction { + ConnectAction = 0, + DisconnectAction = 1, + RegisterObjectAction = 2, + UnregisterObjectAction = 3, + ObjectRegisteredAtAction = 4, + + CloseConnectionAction = 10, + ObjectDestroyedAction = 11, + RelaySignalAction = 12, + HandleObjectCallAction = 13, + HandleSignalAction = 14, + ConnectRelayAction = 15, + DisconnectRelayAction = 16, + FindMetaObject1Action = 17, + FindMetaObject2Action = 18, + RegisterServiceAction = 19, + UnregisterServiceAction = 20, + UpdateSignalHookOwnerAction = 21, + HandleObjectCallPostEventAction = 22, + HandleObjectCallSemaphoreAction = 23, + DoDispatchAction = 24, + SendWithReplyAsyncAction = 25, + MessageResultReceivedAction = 26, + ActivateSignalAction = 27, + PendingCallBlockAction = 28, + + AddTimeoutAction = 50, + RemoveTimeoutAction = 51, + KillTimerAction = 52, + TimerEventAction = 53, + AddWatchAction = 60, + RemoveWatchAction = 61, + ToggleWatchAction = 62, + SocketReadAction = 63, + SocketWriteAction = 64, +}; + +struct QDBusLockerBase +{ + enum Condition + { + BeforeLock, + AfterLock, + BeforeUnlock, + AfterUnlock, + + BeforePost, + AfterPost, + BeforeDeliver, + AfterDeliver, + + BeforeAcquire, + AfterAcquire, + BeforeRelease, + AfterRelease + }; + +#if QDBUS_THREAD_DEBUG + static inline void reportThreadAction(int action, int condition, QDBusConnectionPrivate *ptr) + { if (qdbusThreadDebug) qdbusThreadDebug(action, condition, ptr); } +#else + static inline void reportThreadAction(int, int, QDBusConnectionPrivate *) { } +#endif +}; + +struct QDBusReadLocker: QDBusLockerBase +{ + QDBusConnectionPrivate *self; + ThreadAction action; + inline QDBusReadLocker(ThreadAction a, QDBusConnectionPrivate *s) + : self(s), action(a) + { + reportThreadAction(action, BeforeLock, self); + self->lock.lockForRead(); + reportThreadAction(action, AfterLock, self); + } + + inline ~QDBusReadLocker() + { + reportThreadAction(action, BeforeUnlock, self); + self->lock.unlock(); + reportThreadAction(action, AfterUnlock, self); + } +}; + +struct QDBusWriteLocker: QDBusLockerBase +{ + QDBusConnectionPrivate *self; + ThreadAction action; + inline QDBusWriteLocker(ThreadAction a, QDBusConnectionPrivate *s) + : self(s), action(a) + { + reportThreadAction(action, BeforeLock, self); + self->lock.lockForWrite(); + reportThreadAction(action, AfterLock, self); + } + + inline ~QDBusWriteLocker() + { + reportThreadAction(action, BeforeUnlock, self); + self->lock.unlock(); + reportThreadAction(action, AfterUnlock, self); + } +}; + +struct QDBusMutexLocker: QDBusLockerBase +{ + QDBusConnectionPrivate *self; + QMutex *mutex; + ThreadAction action; + inline QDBusMutexLocker(ThreadAction a, QDBusConnectionPrivate *s, + QMutex *m) + : self(s), mutex(m), action(a) + { + reportThreadAction(action, BeforeLock, self); + mutex->lock(); + reportThreadAction(action, AfterLock, self); + } + + inline ~QDBusMutexLocker() + { + reportThreadAction(action, BeforeUnlock, self); + mutex->unlock(); + reportThreadAction(action, AfterUnlock, self); + } +}; + +struct QDBusDispatchLocker: QDBusMutexLocker +{ + inline QDBusDispatchLocker(ThreadAction a, QDBusConnectionPrivate *s) + : QDBusMutexLocker(a, s, &s->dispatchLock) + { } +}; + +struct QDBusWatchAndTimeoutLocker: QDBusMutexLocker +{ + inline QDBusWatchAndTimeoutLocker(ThreadAction a, QDBusConnectionPrivate *s) + : QDBusMutexLocker(a, s, &s->watchAndTimeoutLock) + { } +}; + +#if QDBUS_THREAD_DEBUG +# define SEM_ACQUIRE(action, sem) \ + do { \ + QDBusLockerBase::reportThreadAction(action, QDBusLockerBase::BeforeAcquire, this); \ + sem.acquire(); \ + QDBusLockerBase::reportThreadAction(action, QDBusLockerBase::AfterAcquire, this); \ + } while (0) + +# define SEM_RELEASE(action, sem) \ + do { \ + QDBusLockerBase::reportThreadAction(action, QDBusLockerBase::BeforeRelease, that); \ + sem.release(); \ + QDBusLockerBase::reportThreadAction(action, QDBusLockerBase::AfterRelease, that); \ + } while (0) + +#else +# define SEM_ACQUIRE(action, sem) sem.acquire() +# define SEM_RELEASE(action, sem) sem.release() +#endif + +#endif diff --git a/src/dbus/qdbusutil.cpp b/src/dbus/qdbusutil.cpp new file mode 100644 index 0000000..471b899 --- /dev/null +++ b/src/dbus/qdbusutil.cpp @@ -0,0 +1,468 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdbusutil_p.h" + +#include <qdbus_symbols_p.h> + +#include <QtCore/qstringlist.h> + +#include "qdbusargument.h" + +QT_BEGIN_NAMESPACE + +static inline bool isValidCharacterNoDash(const QChar &c) +{ + register ushort u = c.unicode(); + return (u >= 'a' && u <= 'z') + || (u >= 'A' && u <= 'Z') + || (u >= '0' && u <= '9') + || (u == '_'); +} + +static inline bool isValidCharacter(const QChar &c) +{ + register ushort u = c.unicode(); + return (u >= 'a' && u <= 'z') + || (u >= 'A' && u <= 'Z') + || (u >= '0' && u <= '9') + || (u == '_') || (u == '-'); +} + +static inline bool isValidNumber(const QChar &c) +{ + register ushort u = c.unicode(); + return (u >= '0' && u <= '9'); +} + +static bool argToString(const QDBusArgument &arg, QString &out); + +static bool variantToString(const QVariant &arg, QString &out) +{ + int argType = arg.userType(); + + if (argType == QVariant::StringList) { + out += QLatin1String("{"); + QStringList list = arg.toStringList(); + foreach (QString item, list) + out += QLatin1Char('\"') + item + QLatin1String("\", "); + if (!list.isEmpty()) + out.chop(2); + out += QLatin1String("}"); + } else if (argType == QVariant::ByteArray) { + out += QLatin1String("{"); + QByteArray list = arg.toByteArray(); + for (int i = 0; i < list.count(); ++i) { + out += QString::number(list.at(i)); + out += QLatin1String(", "); + } + if (!list.isEmpty()) + out.chop(2); + out += QLatin1String("}"); + } else if (argType == QVariant::List) { + out += QLatin1String("{"); + QList<QVariant> list = arg.toList(); + foreach (QVariant item, list) { + if (!variantToString(item, out)) + return false; + out += QLatin1String(", "); + } + if (!list.isEmpty()) + out.chop(2); + out += QLatin1String("}"); + } else if (argType == QMetaType::Char || argType == QMetaType::Short || argType == QMetaType::Int + || argType == QMetaType::Long || argType == QMetaType::LongLong) { + out += QString::number(arg.toLongLong()); + } else if (argType == QMetaType::UChar || argType == QMetaType::UShort || argType == QMetaType::UInt + || argType == QMetaType::ULong || argType == QMetaType::ULongLong) { + out += QString::number(arg.toULongLong()); + } else if (argType == QMetaType::Double) { + out += QString::number(arg.toDouble()); + } else if (argType == QMetaType::Bool) { + out += QLatin1String(arg.toBool() ? "true" : "false"); + } else if (argType == qMetaTypeId<QDBusArgument>()) { + argToString(qvariant_cast<QDBusArgument>(arg), out); + } else if (argType == qMetaTypeId<QDBusObjectPath>()) { + const QString path = qvariant_cast<QDBusObjectPath>(arg).path(); + out += QLatin1String("[ObjectPath: "); + out += path; + out += QLatin1Char(']'); + } else if (argType == qMetaTypeId<QDBusSignature>()) { + out += QLatin1String("[Signature: ") + qvariant_cast<QDBusSignature>(arg).signature(); + out += QLatin1Char(']'); + } else if (argType == qMetaTypeId<QDBusVariant>()) { + const QVariant v = qvariant_cast<QDBusVariant>(arg).variant(); + out += QLatin1String("[Variant"); + int vUserType = v.userType(); + if (vUserType != qMetaTypeId<QDBusVariant>() + && vUserType != qMetaTypeId<QDBusSignature>() + && vUserType != qMetaTypeId<QDBusObjectPath>() + && vUserType != qMetaTypeId<QDBusArgument>()) + out += QLatin1Char('(') + QLatin1String(v.typeName()) + QLatin1Char(')'); + out += QLatin1String(": "); + if (!variantToString(v, out)) + return false; + out += QLatin1Char(']'); + } else if (arg.canConvert(QVariant::String)) { + out += QLatin1String("\"") + arg.toString() + QLatin1String("\""); + } else { + out += QLatin1Char('['); + out += QLatin1String(arg.typeName()); + out += QLatin1Char(']'); + } + + return true; +} + +bool argToString(const QDBusArgument &busArg, QString &out) +{ + QString busSig = busArg.currentSignature(); + bool doIterate = false; + QDBusArgument::ElementType elementType = busArg.currentType(); + + if (elementType != QDBusArgument::BasicType && elementType != QDBusArgument::VariantType + && elementType != QDBusArgument::MapEntryType) + out += QLatin1String("[Argument: ") + busSig + QLatin1Char(' '); + + switch (elementType) { + case QDBusArgument::BasicType: + case QDBusArgument::VariantType: + if (!variantToString(busArg.asVariant(), out)) + return false; + break; + case QDBusArgument::StructureType: + busArg.beginStructure(); + doIterate = true; + break; + case QDBusArgument::ArrayType: + busArg.beginArray(); + out += QLatin1Char('{'); + doIterate = true; + break; + case QDBusArgument::MapType: + busArg.beginMap(); + out += QLatin1Char('{'); + doIterate = true; + break; + case QDBusArgument::MapEntryType: + busArg.beginMapEntry(); + if (!variantToString(busArg.asVariant(), out)) + return false; + out += QLatin1String(" = "); + if (!argToString(busArg, out)) + return false; + busArg.endMapEntry(); + break; + case QDBusArgument::UnknownType: + default: + out += QLatin1String("<ERROR - Unknown Type>"); + return false; + } + if (doIterate && !busArg.atEnd()) { + while (!busArg.atEnd()) { + if (!argToString(busArg, out)) + return false; + out += QLatin1String(", "); + } + out.chop(2); + } + switch (elementType) { + case QDBusArgument::BasicType: + case QDBusArgument::VariantType: + case QDBusArgument::UnknownType: + case QDBusArgument::MapEntryType: + // nothing to do + break; + case QDBusArgument::StructureType: + busArg.endStructure(); + break; + case QDBusArgument::ArrayType: + out += QLatin1Char('}'); + busArg.endArray(); + break; + case QDBusArgument::MapType: + out += QLatin1Char('}'); + busArg.endMap(); + break; + } + + if (elementType != QDBusArgument::BasicType && elementType != QDBusArgument::VariantType + && elementType != QDBusArgument::MapEntryType) + out += QLatin1String("]"); + + return true; +} + +/*! + \namespace QDBusUtil + \inmodule QtDBus + \internal + + \brief The QDBusUtil namespace contains a few functions that are of general use when + dealing with D-Bus strings. +*/ +namespace QDBusUtil +{ + /*! + \internal + \since 4.5 + Dumps the contents of a QtDBus argument from \a arg into a string. + */ + QString argumentToString(const QVariant &arg) + { + QString out; + + variantToString(arg, out); + + return out; + } + + /*! + \internal + \fn bool QDBusUtil::isValidPartOfObjectPath(const QString &part) + See QDBusUtil::isValidObjectPath + */ + bool isValidPartOfObjectPath(const QString &part) + { + if (part.isEmpty()) + return false; // can't be valid if it's empty + + const QChar *c = part.unicode(); + for (int i = 0; i < part.length(); ++i) + if (!isValidCharacterNoDash(c[i])) + return false; + + return true; + } + + /*! + \fn bool QDBusUtil::isValidInterfaceName(const QString &ifaceName) + Returns true if this is \a ifaceName is a valid interface name. + + Valid interface names must: + \list + \o not be empty + \o not exceed 255 characters in length + \o be composed of dot-separated string components that contain only ASCII letters, digits + and the underscore ("_") character + \o contain at least two such components + \endlist + */ + bool isValidInterfaceName(const QString& ifaceName) + { + if (ifaceName.isEmpty() || ifaceName.length() > DBUS_MAXIMUM_NAME_LENGTH) + return false; + + QStringList parts = ifaceName.split(QLatin1Char('.')); + if (parts.count() < 2) + return false; // at least two parts + + for (int i = 0; i < parts.count(); ++i) + if (!isValidMemberName(parts.at(i))) + return false; + + return true; + } + + /*! + \fn bool QDBusUtil::isValidUniqueConnectionName(const QString &connName) + Returns true if \a connName is a valid unique connection name. + + Unique connection names start with a colon (":") and are followed by a list of dot-separated + components composed of ASCII letters, digits, the hypen or the underscore ("_") character. + */ + bool isValidUniqueConnectionName(const QString &connName) + { + if (connName.isEmpty() || connName.length() > DBUS_MAXIMUM_NAME_LENGTH || + !connName.startsWith(QLatin1Char(':'))) + return false; + + QStringList parts = connName.mid(1).split(QLatin1Char('.')); + if (parts.count() < 1) + return false; + + for (int i = 0; i < parts.count(); ++i) { + const QString &part = parts.at(i); + if (part.isEmpty()) + return false; + + const QChar* c = part.unicode(); + for (int j = 0; j < part.length(); ++j) + if (!isValidCharacter(c[j])) + return false; + } + + return true; + } + + /*! + \fn bool QDBusUtil::isValidBusName(const QString &busName) + Returns true if \a busName is a valid bus name. + + A valid bus name is either a valid unique connection name or follows the rules: + \list + \o is not empty + \o does not exceed 255 characters in length + \o be composed of dot-separated string components that contain only ASCII letters, digits, + hyphens or underscores ("_"), but don't start with a digit + \o contains at least two such elements + \endlist + + \sa isValidUniqueConnectionName() + */ + bool isValidBusName(const QString &busName) + { + if (busName.isEmpty() || busName.length() > DBUS_MAXIMUM_NAME_LENGTH) + return false; + + if (busName.startsWith(QLatin1Char(':'))) + return isValidUniqueConnectionName(busName); + + QStringList parts = busName.split(QLatin1Char('.')); + if (parts.count() < 1) + return false; + + for (int i = 0; i < parts.count(); ++i) { + const QString &part = parts.at(i); + if (part.isEmpty()) + return false; + + const QChar *c = part.unicode(); + if (isValidNumber(c[0])) + return false; + for (int j = 0; j < part.length(); ++j) + if (!isValidCharacter(c[j])) + return false; + } + + return true; + } + + /*! + \fn bool QDBusUtil::isValidMemberName(const QString &memberName) + Returns true if \a memberName is a valid member name. A valid member name does not exceed + 255 characters in length, is not empty, is composed only of ASCII letters, digits and + underscores, but does not start with a digit. + */ + bool isValidMemberName(const QString &memberName) + { + if (memberName.isEmpty() || memberName.length() > DBUS_MAXIMUM_NAME_LENGTH) + return false; + + const QChar* c = memberName.unicode(); + if (isValidNumber(c[0])) + return false; + for (int j = 0; j < memberName.length(); ++j) + if (!isValidCharacterNoDash(c[j])) + return false; + return true; + } + + /*! + \fn bool QDBusUtil::isValidErrorName(const QString &errorName) + Returns true if \a errorName is a valid error name. Valid error names are valid interface + names and vice-versa, so this function is actually an alias for isValidInterfaceName. + */ + bool isValidErrorName(const QString &errorName) + { + return isValidInterfaceName(errorName); + } + + /*! + \fn bool QDBusUtil::isValidObjectPath(const QString &path) + Returns true if \a path is valid object path. + + Valid object paths follow the rules: + \list + \o start with the slash character ("/") + \o do not end in a slash, unless the path is just the initial slash + \o do not contain any two slashes in sequence + \o contain slash-separated parts, each of which is composed of ASCII letters, digits and + underscores ("_") + \endlist + */ + bool isValidObjectPath(const QString &path) + { + if (path == QLatin1String("/")) + return true; + + if (!path.startsWith(QLatin1Char('/')) || path.indexOf(QLatin1String("//")) != -1 || + path.endsWith(QLatin1Char('/'))) + return false; + + QStringList parts = path.split(QLatin1Char('/')); + Q_ASSERT(parts.count() >= 1); + parts.removeFirst(); // it starts with /, so we get an empty first part + + for (int i = 0; i < parts.count(); ++i) + if (!isValidPartOfObjectPath(parts.at(i))) + return false; + + return true; + } + + /*! + \fn bool QDBusUtil::isValidSignature(const QString &signature) + Returns true if \a signature is a valid D-Bus type signature for one or more types. + This function returns true if it can all of \a signature into valid, individual types and no + characters remain in \a signature. + + \sa isValidSingleSignature() + */ + bool isValidSignature(const QString &signature) + { + return q_dbus_signature_validate(signature.toUtf8(), 0); + } + + /*! + \fn bool QDBusUtil::isValidSingleSignature(const QString &signature) + Returns true if \a signature is a valid D-Bus type signature for exactly one full type. This + function tries to convert the type signature into a D-Bus type and, if it succeeds and no + characters remain in the signature, it returns true. + */ + bool isValidSingleSignature(const QString &signature) + { + return q_dbus_signature_validate_single(signature.toUtf8(), 0); + } + +} // namespace QDBusUtil + +QT_END_NAMESPACE diff --git a/src/dbus/qdbusutil_p.h b/src/dbus/qdbusutil_p.h new file mode 100644 index 0000000..4f6f985 --- /dev/null +++ b/src/dbus/qdbusutil_p.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDBUSUTIL_H +#define QDBUSUTIL_H + +#include <QtCore/qstring.h> +#include <QtCore/qvariant.h> + +#include <QtDBus/qdbusmacros.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +namespace QDBusUtil +{ + QDBUS_EXPORT bool isValidInterfaceName(const QString &ifaceName); + + QDBUS_EXPORT bool isValidUniqueConnectionName(const QString &busName); + + QDBUS_EXPORT bool isValidBusName(const QString &busName); + + QDBUS_EXPORT bool isValidMemberName(const QString &memberName); + + QDBUS_EXPORT bool isValidErrorName(const QString &errorName); + + QDBUS_EXPORT bool isValidPartOfObjectPath(const QString &path); + + QDBUS_EXPORT bool isValidObjectPath(const QString &path); + + QDBUS_EXPORT bool isValidSignature(const QString &signature); + + QDBUS_EXPORT bool isValidSingleSignature(const QString &signature); + + QDBUS_EXPORT QString argumentToString(const QVariant &variant); +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/dbus/qdbusxmlgenerator.cpp b/src/dbus/qdbusxmlgenerator.cpp new file mode 100644 index 0000000..43a982c --- /dev/null +++ b/src/dbus/qdbusxmlgenerator.cpp @@ -0,0 +1,341 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qcoreapplication.h> +#include <QtCore/qmetaobject.h> +#include <QtCore/qstringlist.h> + +#include "qdbusinterface_p.h" // for ANNOTATION_NO_WAIT +#include "qdbusabstractadaptor_p.h" // for QCLASSINFO_DBUS_* +#include "qdbusconnection_p.h" // for the flags +#include "qdbusmetatype_p.h" +#include "qdbusmetatype.h" +#include "qdbusutil_p.h" + +QT_BEGIN_NAMESPACE + +extern QDBUS_EXPORT QString qDBusGenerateMetaObjectXml(QString interface, const QMetaObject *mo, + const QMetaObject *base, int flags); + +static inline QString typeNameToXml(const char *typeName) +{ + // ### copied from qtextdocument.cpp + // ### move this into QtCore at some point + QString plain = QLatin1String(typeName); + QString rich; + rich.reserve(int(plain.length() * 1.1)); + for (int i = 0; i < plain.length(); ++i) { + if (plain.at(i) == QLatin1Char('<')) + rich += QLatin1String("<"); + else if (plain.at(i) == QLatin1Char('>')) + rich += QLatin1String(">"); + else if (plain.at(i) == QLatin1Char('&')) + rich += QLatin1String("&"); + else + rich += plain.at(i); + } + return rich; +} + +// implement the D-Bus org.freedesktop.DBus.Introspectable interface +// we do that by analysing the metaObject of all the adaptor interfaces + +static QString generateInterfaceXml(const QMetaObject *mo, int flags, int methodOffset, int propOffset) +{ + QString retval; + + // start with properties: + if (flags & (QDBusConnection::ExportScriptableProperties | + QDBusConnection::ExportNonScriptableProperties)) { + for (int i = propOffset; i < mo->propertyCount(); ++i) { + static const char *accessvalues[] = {0, "read", "write", "readwrite"}; + + QMetaProperty mp = mo->property(i); + + if (!((mp.isScriptable() && (flags & QDBusConnection::ExportScriptableProperties)) || + (!mp.isScriptable() && (flags & QDBusConnection::ExportNonScriptableProperties)))) + continue; + + int access = 0; + if (mp.isReadable()) + access |= 1; + if (mp.isWritable()) + access |= 2; + + int typeId = qDBusNameToTypeId(mp.typeName()); + if (!typeId) + continue; + const char *signature = QDBusMetaType::typeToSignature(typeId); + if (!signature) + continue; + + retval += QString::fromLatin1(" <property name=\"%1\" type=\"%2\" access=\"%3\"") + .arg(QLatin1String(mp.name())) + .arg(QLatin1String(signature)) + .arg(QLatin1String(accessvalues[access])); + + if (QDBusMetaType::signatureToType(signature) == QVariant::Invalid) { + const char *typeName = QVariant::typeToName(QVariant::Type(typeId)); + retval += QString::fromLatin1(">\n <annotation name=\"com.trolltech.QtDBus.QtTypeName\" value=\"%3\"/>\n </property>\n") + .arg(typeNameToXml(typeName)); + } else { + retval += QLatin1String("/>\n"); + } + } + } + + // now add methods: + for (int i = methodOffset; i < mo->methodCount(); ++i) { + QMetaMethod mm = mo->method(i); + QByteArray signature = mm.signature(); + int paren = signature.indexOf('('); + + bool isSignal; + if (mm.methodType() == QMetaMethod::Signal) + // adding a signal + isSignal = true; + else if (mm.methodType() == QMetaMethod::Slot && mm.access() == QMetaMethod::Public) + isSignal = false; + else + continue; // neither signal nor public slot + + if (isSignal && !(flags & (QDBusConnection::ExportScriptableSignals | + QDBusConnection::ExportNonScriptableSignals))) + continue; // we're not exporting any signals + if (!isSignal && !(flags & (QDBusConnection::ExportScriptableSlots | + QDBusConnection::ExportNonScriptableSlots))) + continue; // we're not exporting any slots + + QString xml = QString::fromLatin1(" <%1 name=\"%2\">\n") + .arg(isSignal ? QLatin1String("signal") : QLatin1String("method")) + .arg(QLatin1String(signature.left(paren))); + + // check the return type first + int typeId = qDBusNameToTypeId(mm.typeName()); + if (typeId) { + const char *typeName = QDBusMetaType::typeToSignature(typeId); + if (typeName) { + xml += QString::fromLatin1(" <arg type=\"%1\" direction=\"out\"/>\n") + .arg(typeNameToXml(typeName)); + + // do we need to describe this argument? + if (QDBusMetaType::signatureToType(typeName) == QVariant::Invalid) + xml += QString::fromLatin1(" <annotation name=\"com.trolltech.QtDBus.QtTypeName.Out0\" value=\"%1\"/>\n") + .arg(typeNameToXml(mm.typeName())); + } else + continue; + } + else if (*mm.typeName()) + continue; // wasn't a valid type + + QList<QByteArray> names = mm.parameterNames(); + QList<int> types; + int inputCount = qDBusParametersForMethod(mm, types); + if (inputCount == -1) + continue; // invalid form + if (isSignal && inputCount + 1 != types.count()) + continue; // signal with output arguments? + if (isSignal && types.at(inputCount) == QDBusMetaTypeId::message) + continue; // signal with QDBusMessage argument? + if (isSignal && mm.attributes() & QMetaMethod::Cloned) + continue; // cloned signal? + + int j; + bool isScriptable = mm.attributes() & QMetaMethod::Scriptable; + for (j = 1; j < types.count(); ++j) { + // input parameter for a slot or output for a signal + if (types.at(j) == QDBusMetaTypeId::message) { + isScriptable = true; + continue; + } + + QString name; + if (!names.at(j - 1).isEmpty()) + name = QString::fromLatin1("name=\"%1\" ").arg(QLatin1String(names.at(j - 1))); + + bool isOutput = isSignal || j > inputCount; + + const char *signature = QDBusMetaType::typeToSignature(types.at(j)); + xml += QString::fromLatin1(" <arg %1type=\"%2\" direction=\"%3\"/>\n") + .arg(name) + .arg(QLatin1String(signature)) + .arg(isOutput ? QLatin1String("out") : QLatin1String("in")); + + // do we need to describe this argument? + if (QDBusMetaType::signatureToType(signature) == QVariant::Invalid) { + const char *typeName = QVariant::typeToName( QVariant::Type(types.at(j)) ); + xml += QString::fromLatin1(" <annotation name=\"com.trolltech.QtDBus.QtTypeName.%1%2\" value=\"%3\"/>\n") + .arg(isOutput ? QLatin1String("Out") : QLatin1String("In")) + .arg(isOutput ? j - inputCount : j - 1) + .arg(typeNameToXml(typeName)); + } + } + + int wantedMask; + if (isScriptable) + wantedMask = isSignal ? QDBusConnection::ExportScriptableSignals + : QDBusConnection::ExportScriptableSlots; + else + wantedMask = isSignal ? QDBusConnection::ExportNonScriptableSignals + : QDBusConnection::ExportNonScriptableSlots; + if ((flags & wantedMask) != wantedMask) + continue; + + if (qDBusCheckAsyncTag(mm.tag())) + // add the no-reply annotation + xml += QLatin1String(" <annotation name=\"" ANNOTATION_NO_WAIT "\"" + " value=\"true\"/>\n"); + + retval += xml; + retval += QString::fromLatin1(" </%1>\n") + .arg(isSignal ? QLatin1String("signal") : QLatin1String("method")); + } + + return retval; +} + +QString qDBusInterfaceFromMetaObject(const QMetaObject *mo) +{ + QString interface; + + int idx = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTERFACE); + if (idx >= mo->classInfoOffset()) { + interface = QLatin1String(mo->classInfo(idx).value()); + } else { + interface = QLatin1String(mo->className()); + interface.replace(QLatin1String("::"), QLatin1String(".")); + + if (interface.startsWith(QLatin1String("QDBus"))) { + interface.prepend(QLatin1String("com.trolltech.QtDBus.")); + } else if (interface.startsWith(QLatin1Char('Q')) && + interface.length() >= 2 && interface.at(1).isUpper()) { + // assume it's Qt + interface.prepend(QLatin1String("com.trolltech.Qt.")); + } else if (!QCoreApplication::instance()|| + QCoreApplication::instance()->applicationName().isEmpty()) { + interface.prepend(QLatin1String("local.")); + } else { + interface.prepend(QLatin1Char('.')).prepend(QCoreApplication::instance()->applicationName()); + QStringList domainName = + QCoreApplication::instance()->organizationDomain().split(QLatin1Char('.'), + QString::SkipEmptyParts); + if (domainName.isEmpty()) + interface.prepend(QLatin1String("local.")); + else + for (int i = 0; i < domainName.count(); ++i) + interface.prepend(QLatin1Char('.')).prepend(domainName.at(i)); + } + } + + return interface; + } + +QString qDBusGenerateMetaObjectXml(QString interface, const QMetaObject *mo, + const QMetaObject *base, int flags) +{ + if (interface.isEmpty()) + // generate the interface name from the meta object + interface = qDBusInterfaceFromMetaObject(mo); + + QString xml; + int idx = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTROSPECTION); + if (idx >= mo->classInfoOffset()) + return QString::fromUtf8(mo->classInfo(idx).value()); + else + xml = generateInterfaceXml(mo, flags, base->methodCount(), base->propertyCount()); + + if (xml.isEmpty()) + return QString(); // don't add an empty interface + return QString::fromLatin1(" <interface name=\"%1\">\n%2 </interface>\n") + .arg(interface, xml); +} +#if 0 +QString qDBusGenerateMetaObjectXml(QString interface, const QMetaObject *mo, const QMetaObject *base, + int flags) +{ + if (interface.isEmpty()) { + // generate the interface name from the meta object + int idx = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTERFACE); + if (idx >= mo->classInfoOffset()) { + interface = QLatin1String(mo->classInfo(idx).value()); + } else { + interface = QLatin1String(mo->className()); + interface.replace(QLatin1String("::"), QLatin1String(".")); + + if (interface.startsWith(QLatin1String("QDBus"))) { + interface.prepend(QLatin1String("com.trolltech.QtDBus.")); + } else if (interface.startsWith(QLatin1Char('Q')) && + interface.length() >= 2 && interface.at(1).isUpper()) { + // assume it's Qt + interface.prepend(QLatin1String("com.trolltech.Qt.")); + } else if (!QCoreApplication::instance()|| + QCoreApplication::instance()->applicationName().isEmpty()) { + interface.prepend(QLatin1String("local.")); + } else { + interface.prepend(QLatin1Char('.')).prepend(QCoreApplication::instance()->applicationName()); + QStringList domainName = + QCoreApplication::instance()->organizationDomain().split(QLatin1Char('.'), + QString::SkipEmptyParts); + if (domainName.isEmpty()) + interface.prepend(QLatin1String("local.")); + else + for (int i = 0; i < domainName.count(); ++i) + interface.prepend(QLatin1Char('.')).prepend(domainName.at(i)); + } + } + } + + QString xml; + int idx = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTROSPECTION); + if (idx >= mo->classInfoOffset()) + return QString::fromUtf8(mo->classInfo(idx).value()); + else + xml = generateInterfaceXml(mo, flags, base->methodCount(), base->propertyCount()); + + if (xml.isEmpty()) + return QString(); // don't add an empty interface + return QString::fromLatin1(" <interface name=\"%1\">\n%2 </interface>\n") + .arg(interface, xml); +} + +#endif + +QT_END_NAMESPACE diff --git a/src/dbus/qdbusxmlparser.cpp b/src/dbus/qdbusxmlparser.cpp new file mode 100644 index 0000000..e3aa0dd --- /dev/null +++ b/src/dbus/qdbusxmlparser.cpp @@ -0,0 +1,369 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdbusxmlparser_p.h" +#include "qdbusinterface.h" +#include "qdbusinterface_p.h" +#include "qdbusconnection_p.h" +#include "qdbusutil_p.h" + +#include <QtXml/qdom.h> +#include <QtCore/qmap.h> +#include <QtCore/qvariant.h> +#include <QtCore/qtextstream.h> + +QT_BEGIN_NAMESPACE + +static QDBusIntrospection::Annotations +parseAnnotations(const QDomElement& elem) +{ + QDBusIntrospection::Annotations retval; + QDomNodeList list = elem.elementsByTagName(QLatin1String("annotation")); + for (int i = 0; i < list.count(); ++i) + { + QDomElement ann = list.item(i).toElement(); + if (ann.isNull()) + continue; + + QString name = ann.attribute(QLatin1String("name")), + value = ann.attribute(QLatin1String("value")); + + if (!QDBusUtil::isValidInterfaceName(name)) { + qWarning("Invalid D-BUS annotation '%s' found while parsing introspection", + qPrintable(name)); + continue; + } + + retval.insert(name, value); + } + + return retval; +} + +static QDBusIntrospection::Arguments +parseArgs(const QDomElement& elem, const QLatin1String& direction, bool acceptEmpty) +{ + QDBusIntrospection::Arguments retval; + QDomNodeList list = elem.elementsByTagName(QLatin1String("arg")); + for (int i = 0; i < list.count(); ++i) + { + QDomElement arg = list.item(i).toElement(); + if (arg.isNull()) + continue; + + if ((acceptEmpty && !arg.hasAttribute(QLatin1String("direction"))) || + arg.attribute(QLatin1String("direction")) == direction) { + + QDBusIntrospection::Argument argData; + if (arg.hasAttribute(QLatin1String("name"))) + argData.name = arg.attribute(QLatin1String("name")); // can be empty + argData.type = arg.attribute(QLatin1String("type")); + if (!QDBusUtil::isValidSingleSignature(argData.type)) { + qWarning("Invalid D-BUS type signature '%s' found while parsing introspection", + qPrintable(argData.type)); + continue; + } + + retval << argData; + } + } + return retval; +} + +QDBusXmlParser::QDBusXmlParser(const QString& service, const QString& path, + const QString& xmlData) + : m_service(service), m_path(path) +{ + QDomDocument doc; + doc.setContent(xmlData); + m_node = doc.firstChildElement(QLatin1String("node")); +} + +QDBusXmlParser::QDBusXmlParser(const QString& service, const QString& path, + const QDomElement& node) + : m_service(service), m_path(path), m_node(node) +{ +} + +QDBusIntrospection::Interfaces +QDBusXmlParser::interfaces() const +{ + QDBusIntrospection::Interfaces retval; + + if (m_node.isNull()) + return retval; + + QDomNodeList interfaceList = m_node.elementsByTagName(QLatin1String("interface")); + for (int i = 0; i < interfaceList.count(); ++i) + { + QDomElement iface = interfaceList.item(i).toElement(); + QString ifaceName = iface.attribute(QLatin1String("name")); + if (iface.isNull()) + continue; // for whatever reason + if (!QDBusUtil::isValidInterfaceName(ifaceName)) { + qWarning("Invalid D-BUS interface name '%s' found while parsing introspection", + qPrintable(ifaceName)); + continue; + } + + QDBusIntrospection::Interface *ifaceData = new QDBusIntrospection::Interface; + ifaceData->name = ifaceName; + { + // save the data + QTextStream ts(&ifaceData->introspection); + iface.save(ts,2); + } + + // parse annotations + ifaceData->annotations = parseAnnotations(iface); + + // parse methods + QDomNodeList list = iface.elementsByTagName(QLatin1String("method")); + for (int j = 0; j < list.count(); ++j) + { + QDomElement method = list.item(j).toElement(); + QString methodName = method.attribute(QLatin1String("name")); + if (method.isNull()) + continue; + if (!QDBusUtil::isValidMemberName(methodName)) { + qWarning("Invalid D-BUS member name '%s' found in interface '%s' while parsing introspection", + qPrintable(methodName), qPrintable(ifaceName)); + continue; + } + + QDBusIntrospection::Method methodData; + methodData.name = methodName; + + // parse arguments + methodData.inputArgs = parseArgs(method, QLatin1String("in"), true); + methodData.outputArgs = parseArgs(method, QLatin1String("out"), false); + methodData.annotations = parseAnnotations(method); + + // add it + ifaceData->methods.insert(methodName, methodData); + } + + // parse signals + list = iface.elementsByTagName(QLatin1String("signal")); + for (int j = 0; j < list.count(); ++j) + { + QDomElement signal = list.item(j).toElement(); + QString signalName = signal.attribute(QLatin1String("name")); + if (signal.isNull()) + continue; + if (!QDBusUtil::isValidMemberName(signalName)) { + qWarning("Invalid D-BUS member name '%s' found in interface '%s' while parsing introspection", + qPrintable(signalName), qPrintable(ifaceName)); + continue; + } + + QDBusIntrospection::Signal signalData; + signalData.name = signalName; + + // parse data + signalData.outputArgs = parseArgs(signal, QLatin1String("out"), true); + signalData.annotations = parseAnnotations(signal); + + // add it + ifaceData->signals_.insert(signalName, signalData); + } + + // parse properties + list = iface.elementsByTagName(QLatin1String("property")); + for (int j = 0; j < list.count(); ++j) + { + QDomElement property = list.item(j).toElement(); + QString propertyName = property.attribute(QLatin1String("name")); + if (property.isNull()) + continue; + if (!QDBusUtil::isValidMemberName(propertyName)) { + qWarning("Invalid D-BUS member name '%s' found in interface '%s' while parsing introspection", + qPrintable(propertyName), qPrintable(ifaceName)); + continue; + } + + QDBusIntrospection::Property propertyData; + + // parse data + propertyData.name = propertyName; + propertyData.type = property.attribute(QLatin1String("type")); + propertyData.annotations = parseAnnotations(property); + + if (!QDBusUtil::isValidSingleSignature(propertyData.type)) { + // cannot be! + qWarning("Invalid D-BUS type signature '%s' found in property '%s.%s' while parsing introspection", + qPrintable(propertyData.type), qPrintable(ifaceName), + qPrintable(propertyName)); + continue; + } + + QString access = property.attribute(QLatin1String("access")); + if (access == QLatin1String("read")) + propertyData.access = QDBusIntrospection::Property::Read; + else if (access == QLatin1String("write")) + propertyData.access = QDBusIntrospection::Property::Write; + else if (access == QLatin1String("readwrite")) + propertyData.access = QDBusIntrospection::Property::ReadWrite; + else { + qWarning("Invalid D-BUS property access '%s' found in property '%s.%s' while parsing introspection", + qPrintable(access), qPrintable(ifaceName), + qPrintable(propertyName)); + continue; // invalid one! + } + + // add it + ifaceData->properties.insert(propertyName, propertyData); + } + + // add it + retval.insert(ifaceName, QSharedDataPointer<QDBusIntrospection::Interface>(ifaceData)); + } + + return retval; +} + +QSharedDataPointer<QDBusIntrospection::Object> +QDBusXmlParser::object() const +{ + if (m_node.isNull()) + return QSharedDataPointer<QDBusIntrospection::Object>(); + + QDBusIntrospection::Object* objData; + objData = new QDBusIntrospection::Object; + objData->service = m_service; + objData->path = m_path; + + // check if we have anything to process + if (objData->introspection.isNull() && !m_node.firstChild().isNull()) { + // yes, introspect this object + QTextStream ts(&objData->introspection); + m_node.save(ts,2); + + QDomNodeList objects = m_node.elementsByTagName(QLatin1String("node")); + for (int i = 0; i < objects.count(); ++i) { + QDomElement obj = objects.item(i).toElement(); + QString objName = obj.attribute(QLatin1String("name")); + if (obj.isNull()) + continue; // for whatever reason + if (!QDBusUtil::isValidObjectPath(m_path + QLatin1Char('/') + objName)) { + qWarning("Invalid D-BUS object path '%s/%s' found while parsing introspection", + qPrintable(m_path), qPrintable(objName)); + continue; + } + + objData->childObjects.append(objName); + } + + QDomNodeList interfaceList = m_node.elementsByTagName(QLatin1String("interface")); + for (int i = 0; i < interfaceList.count(); ++i) { + QDomElement iface = interfaceList.item(i).toElement(); + QString ifaceName = iface.attribute(QLatin1String("name")); + if (iface.isNull()) + continue; + if (!QDBusUtil::isValidInterfaceName(ifaceName)) { + qWarning("Invalid D-BUS interface name '%s' found while parsing introspection", + qPrintable(ifaceName)); + continue; + } + + objData->interfaces.append(ifaceName); + } + } else { + objData->introspection = QLatin1String("<node/>\n"); + } + + QSharedDataPointer<QDBusIntrospection::Object> retval; + retval = objData; + return retval; +} + +QSharedDataPointer<QDBusIntrospection::ObjectTree> +QDBusXmlParser::objectTree() const +{ + QSharedDataPointer<QDBusIntrospection::ObjectTree> retval; + + if (m_node.isNull()) + return retval; + + retval = new QDBusIntrospection::ObjectTree; + + retval->service = m_service; + retval->path = m_path; + + QTextStream ts(&retval->introspection); + m_node.save(ts,2); + + // interfaces are easy: + retval->interfaceData = interfaces(); + retval->interfaces = retval->interfaceData.keys(); + + // sub-objects are slightly more difficult: + QDomNodeList objects = m_node.elementsByTagName(QLatin1String("node")); + for (int i = 0; i < objects.count(); ++i) { + QDomElement obj = objects.item(i).toElement(); + QString objName = obj.attribute(QLatin1String("name")); + if (obj.isNull() || objName.isEmpty()) + continue; // for whatever reason + + // check if we have anything to process + if (!obj.firstChild().isNull()) { + // yes, introspect this object + QString xml; + QTextStream ts2(&xml); + obj.save(ts2,0); + + // parse it + QString objAbsName = m_path; + if (!objAbsName.endsWith(QLatin1Char('/'))) + objAbsName.append(QLatin1Char('/')); + objAbsName += objName; + + QDBusXmlParser parser(m_service, objAbsName, obj); + retval->childObjectData.insert(objName, parser.objectTree()); + } + + retval->childObjects << objName; + } + + return QSharedDataPointer<QDBusIntrospection::ObjectTree>( retval ); +} + +QT_END_NAMESPACE diff --git a/src/dbus/qdbusxmlparser_p.h b/src/dbus/qdbusxmlparser_p.h new file mode 100644 index 0000000..6e0f391 --- /dev/null +++ b/src/dbus/qdbusxmlparser_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDBUSXMLPARSER_H +#define QDBUSXMLPARSER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qmap.h> +#include <QtXml/qdom.h> +#include <qdbusmacros.h> +#include <qdbusintrospection_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \internal +*/ +class QDBusXmlParser +{ + QString m_service; + QString m_path; + QDomElement m_node; + +public: + QDBusXmlParser(const QString& service, const QString& path, + const QString& xmlData); + QDBusXmlParser(const QString& service, const QString& path, + const QDomElement& node); + + QDBusIntrospection::Interfaces interfaces() const; + QSharedDataPointer<QDBusIntrospection::Object> object() const; + QSharedDataPointer<QDBusIntrospection::ObjectTree> objectTree() const; +}; + +QT_END_NAMESPACE + +#endif |