diff options
author | Thiago Macieira <thiago.macieira@trolltech.com> | 2006-02-15 16:25:11 +0000 |
---|---|---|
committer | Thiago Macieira <thiago.macieira@trolltech.com> | 2006-02-15 16:25:11 +0000 |
commit | 174f04c441723d78faa3346944944ab15603ca8c (patch) | |
tree | 3c7941775bc9e847c5d65a7fb4abf11ed328bcef /qt | |
parent | a696be1a0d10478e91f0f72f7ce743a19461f68d (diff) |
Merge the changes to the bindings from the KDE Subversion server.
This is a major change: library is source- and binary-incompatible to
what it used to be.
All testcases are green, functionality is preserved.
It is not feature-complete. Development will continue in the branch in the
Subversion server for a while.
Diffstat (limited to 'qt')
36 files changed, 5741 insertions, 463 deletions
diff --git a/qt/Makefile.am b/qt/Makefile.am index 9247ba1..9117d27 100644 --- a/qt/Makefile.am +++ b/qt/Makefile.am @@ -6,11 +6,19 @@ dbusincludedir=$(includedir)/dbus-1.0/dbus lib_LTLIBRARIES=libdbus-qt4-1.la dbusinclude_HEADERS= \ - qdbuserror.h \ - qdbusmessage.h \ - qdbusserver.h \ + qdbusmacros.h \ + qdbuserror.h \ + qdbusmessage.h \ + qdbusserver.h \ qdbusconnection.h \ - qdbusvariant.h + qdbusvariant.h \ + qdbusobject.h \ + qdbusinterface.h \ + qdbustype.h \ + qdbusstandardinterfaces.h \ + qdbusutil.h \ + qdbusintrospection.h \ + qdbusabstractadaptor.h libdbus_qt4_1_la_SOURCES = \ $(top_srcdir)/qt/qdbusconnection.cpp \ @@ -19,29 +27,46 @@ libdbus_qt4_1_la_SOURCES = \ $(top_srcdir)/qt/qdbusmarshall.cpp \ $(top_srcdir)/qt/qdbusmessage.cpp \ $(top_srcdir)/qt/qdbusserver.cpp \ + $(top_srcdir)/qt/qdbustype.cpp \ + $(top_srcdir)/qt/qdbusobject.cpp \ + $(top_srcdir)/qt/qdbusinterface.cpp \ + $(top_srcdir)/qt/qdbusstandardinterfaces.cpp \ + $(top_srcdir)/qt/qdbusxmlparser.cpp \ + $(top_srcdir)/qt/qdbusutil.cpp \ + $(top_srcdir)/qt/qdbusintrospection.cpp \ + $(top_srcdir)/qt/qdbusabstractadaptor.cpp \ + $(top_srcdir)/qt/qdbusthread.cpp \ + \ + $(top_srcdir)/dbus/qdbus.h \ + $(top_srcdir)/qt/qdbusabstractadaptor.h \ $(top_srcdir)/qt/qdbusconnection.h \ - $(top_srcdir)/qt/qdbuserror.h \ - $(top_srcdir)/qt/qdbusmessage.h \ - $(top_srcdir)/qt/qdbusserver.h \ $(top_srcdir)/qt/qdbusconnection_p.h \ - $(top_srcdir)/dbus/qdbus.h \ + $(top_srcdir)/qt/qdbuserror.h \ + $(top_srcdir)/qt/qdbusinterface.h \ + $(top_srcdir)/qt/qdbusinterface_p.h \ + $(top_srcdir)/qt/qdbusintrospection.h \ + $(top_srcdir)/qt/qdbusmacros.h \ $(top_srcdir)/qt/qdbusmarshall.h \ + $(top_srcdir)/qt/qdbusmessage.h \ $(top_srcdir)/qt/qdbusmessage_p.h \ - $(top_srcdir)/qt/qdbusvariant.h + $(top_srcdir)/qt/qdbusobject.h \ + $(top_srcdir)/qt/qdbusobject_p.h \ + $(top_srcdir)/qt/qdbusserver.h \ + $(top_srcdir)/qt/qdbusstandardinterfaces. + $(top_srcdir)/qt/qdbustype.h \ + $(top_srcdir)/qt/qdbusvariant.h \ + $(top_srcdir)/qt/qdbusxmlparser_p.h -$(top_srcdir)/qt/qdbusserver.cpp: qdbusserver.moc -$(top_srcdir)/qt/qdbusconnection.cpp: qdbusconnection.moc +$(top_srcdir)/qt/qdbusabstractadaptor.lo: qdbusabstractadaptor.moc +$(top_srcdir)/qt/qdbusserver.lo: qdbusserver.moc +$(top_srcdir)/qt/qdbusconnection.lo: qdbusconnection_p.moc -CLEANFILES=qdbusserver.moc qdbusconnection.moc +CLEANFILES=qdbusabstractadaptor.moc qdbusserver.moc qdbusconnection.moc libdbus_qt4_1_la_LIBADD= $(DBUS_QT_LIBS) $(top_builddir)/dbus/libdbus-1.la libdbus_qt4_1_la_LDFLAGS= -version-info 1:0 -no-undefined -# _p.h files are a exception -qdbusconnection.moc: qdbusconnection_p.h - $(QT_MOC) -o qdbusconnection.moc $(top_srcdir)/qt/qdbusconnection_p.h - %.moc: %.h $(QT_MOC) $< > $@ endif diff --git a/qt/README b/qt/README new file mode 100644 index 0000000..efa2799 --- /dev/null +++ b/qt/README @@ -0,0 +1,10 @@ +These are the Qt4 D-Bus bindings. + +They are being maintained by Trolltech AS. As we are currently +considering placing this code in a future version of Qt, we would like +to ask any contributors to contact us before submitting code to this +repository. + + +For more information, please contact + Thiago Macieira <thiago.macieira@trolltech.com> diff --git a/qt/qdbusabstractadaptor.cpp b/qt/qdbusabstractadaptor.cpp new file mode 100644 index 0000000..335469e --- /dev/null +++ b/qt/qdbusabstractadaptor.cpp @@ -0,0 +1,64 @@ +/* -*- mode: C++ -*- + * + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "qdbusabstractadaptor.h" + +#include <QtCore/qmetaobject.h> +#include <QtCore/qtimer.h> + +QDBusAbstractAdaptor::QDBusAbstractAdaptor(QObject* parent) + : QObject(parent) +{ + QTimer::singleShot(0, this, SLOT(polish())); +} + +QDBusAbstractAdaptor::~QDBusAbstractAdaptor() +{ +} + +void QDBusAbstractAdaptor::setAutoRelaySignals(bool enable) +{ + const QMetaObject *us = metaObject(); + 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 = mm.signature(); + sig.prepend(QSIGNAL_CODE + '0'); + if (enable) + connect(parent(), sig, sig); + else + parent()->disconnect(sig, this, sig); + } +} + +void QDBusAbstractAdaptor::polish() +{ + // future work: + // connect every signal in this adaptor to a slot that will relay them into D-Bus +} + +#include "qdbusabstractadaptor.moc" diff --git a/qt/qdbusabstractadaptor.h b/qt/qdbusabstractadaptor.h new file mode 100644 index 0000000..49535f5 --- /dev/null +++ b/qt/qdbusabstractadaptor.h @@ -0,0 +1,44 @@ +/* -*- mode: C++ -*- + * + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef QDBUSABSTRACTADAPTOR_H +#define QDBUSABSTRACTADAPTOR_H + +#include <QtCore/qobject.h> +#include "qdbusmacros.h" + +class QDBUS_EXPORT QDBusAbstractAdaptor: public QObject +{ + Q_OBJECT +public: + QDBusAbstractAdaptor(QObject* parent); + ~QDBusAbstractAdaptor(); + +protected: + void setAutoRelaySignals(bool enable); + +private slots: + void polish(); +}; + +#endif diff --git a/qt/qdbusconnection.cpp b/qt/qdbusconnection.cpp index f5e1bf3..8ac13e4 100644 --- a/qt/qdbusconnection.cpp +++ b/qt/qdbusconnection.cpp @@ -1,6 +1,8 @@ /* qdbusconnection.cpp * * Copyright (C) 2005 Harald Fernengel <harry@kdevelop.org> + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> * * Licensed under the Academic Free License version 2.1 * @@ -15,16 +17,21 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, write to the Free Software Foundation + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ -#include <QtCore/qdebug.h> -#include <QtCore/qcoreapplication.h> +#include <qdebug.h> +#include <qcoreapplication.h> #include "qdbusconnection.h" +#include "qdbuserror.h" +#include "qdbusmessage.h" #include "qdbusconnection_p.h" +#include "qdbusinterface_p.h" +#include "qdbusobject_p.h" +#include "qdbusutil.h" QT_STATIC_CONST_IMPL char *QDBusConnection::default_connection_name = "qt_dbus_default_connection"; @@ -197,14 +204,7 @@ bool QDBusConnection::send(const QDBusMessage &message) const { if (!d || !d->connection) return false; - - DBusMessage *msg = message.toDBusMessage(); - if (!msg) - return false; - - bool isOk = dbus_connection_send(d->connection, msg, 0); - dbus_message_unref(msg); - return isOk; + return d->send(message); } int QDBusConnection::sendWithReplyAsync(const QDBusMessage &message, QObject *receiver, @@ -221,57 +221,98 @@ QDBusMessage QDBusConnection::sendWithReply(const QDBusMessage &message) const if (!d || !d->connection) return QDBusMessage::fromDBusMessage(0); - DBusMessage *msg = message.toDBusMessage(); - if (!msg) - return QDBusMessage::fromDBusMessage(0); - DBusMessage *reply = dbus_connection_send_with_reply_and_block(d->connection, msg, - -1, &d->error); - d->handleError(); - dbus_message_unref(msg); + if (!QCoreApplication::instance()) { + DBusMessage *msg = message.toDBusMessage(); + if (!msg) + return QDBusMessage::fromDBusMessage(0); + + DBusMessage *reply = dbus_connection_send_with_reply_and_block(d->connection, msg, + -1, &d->error); + d->handleError(); + dbus_message_unref(msg); + + if (lastError().isValid()) + return QDBusMessage::fromError(lastError()); - return QDBusMessage::fromDBusMessage(reply); + return QDBusMessage::fromDBusMessage(reply); + } else { + QDBusReplyWaiter waiter; + if (d->sendWithReplyAsync(message, &waiter, SLOT(reply(const QDBusMessage&))) > 0) { + // enter the event loop and wait for a reply + waiter.exec(QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents); + + d->lastError = waiter.replyMsg; // set or clear error + return waiter.replyMsg; + } + + return QDBusMessage::fromDBusMessage(0); + } } -bool QDBusConnection::connect(const QString &path, const QString &interface, +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); +} + +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; - QDBusConnectionPrivate::SignalHook hook; + QString source = getNameOwner(service); + if (source.isEmpty()) + return false; + source += path; + // check the slot + QDBusConnectionPrivate::SignalHook hook; + if ((hook.midx = QDBusConnectionPrivate::findSlot(receiver, slot + 1, hook.params)) == -1) + return false; + hook.interface = interface; hook.name = name; + hook.signature = signature; hook.obj = QPointer<QObject>(receiver); - if (!hook.setSlot(slot + 1)) - return false; - d->signalHooks.insertMulti(path, hook); + d->signalHooks.insertMulti(source, hook); d->connect(receiver, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*))); return true; } +bool QDBusConnection::registerObject(const QString &path, QObject *object, RegisterOptions options) +{ + return registerObject(path, QString(), object, options); +} + bool QDBusConnection::registerObject(const QString &path, const QString &interface, - QObject *object) + QObject *object, RegisterOptions options) { - if (!d || !d->connection || !object || path.isEmpty() || interface.isEmpty()) + if (!d || !d->connection || !object || !options || !QDBusUtil::isValidObjectPath(path)) return false; - QDBusConnectionPrivate::ObjectHook hook; - hook.interface = interface; - hook.obj = object; + QString iface = interface; + if (options & ExportForAnyInterface) + iface.clear(); - QDBusConnectionPrivate::ObjectHookHash::iterator it = d->objectHooks.find(path); - while (it != d->objectHooks.end() && it.key() == path) { - if (it.value().interface == interface) { - d->objectHooks.erase(it); - break; - } - ++it; - } + QDBusConnectionPrivate::ObjectDataHash& hook = d->objectHooks[path]; + + // if we're replacing and matching any interface, then we're replacing every interface + // this catches ExportAdaptors | Reexport too + if (( options & ( ExportForAnyInterface | Reexport )) == ( ExportForAnyInterface | Reexport )) + hook.clear(); + + // we're not matching any interface, but if we're not replacing, make sure it doesn't exist yet + else if (( options & Reexport ) == 0 && hook.find(iface) != hook.end()) + return false; - d->objectHooks.insert(path, hook); + QDBusConnectionPrivate::ObjectData& data = hook[iface]; + + data.flags = options; + data.obj = object; d->connect(object, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*))); qDebug("REGISTERED FOR %s", path.toLocal8Bit().constData()); @@ -284,10 +325,45 @@ void QDBusConnection::unregisterObject(const QString &path) if (!d || !d->connection) return; - // TODO - check interfaces d->objectHooks.remove(path); } +QDBusInterface QDBusConnection::findInterface(const QString& service, const QString& path, + const QString& interface) +{ + // create one + QDBusInterfacePrivate *priv = new QDBusInterfacePrivate; + priv->conn = *this; + + if (!QDBusUtil::isValidObjectPath(path) || !QDBusUtil::isValidInterfaceName(interface)) + return QDBusInterface(priv); + + // check if it's there first + QString owner = getNameOwner(service); + if (owner.isEmpty()) + return QDBusInterface(priv); + + // getNameOwner returns empty if d is 0 + Q_ASSERT(d); + priv->service = owner; + priv->path = path; + priv->data = d->findInterface(interface).constData(); + + return QDBusInterface(priv); // will increment priv's refcount +} + +QDBusObject QDBusConnection::findObject(const QString& service, const QString& path) +{ + QDBusObjectPrivate* priv = 0; + if (d && QDBusUtil::isValidObjectPath(path)) { + QString owner = getNameOwner(service); + + if (!owner.isEmpty()) + priv = new QDBusObjectPrivate(d, owner, path); + } + return QDBusObject(priv, *this); +} + bool QDBusConnection::isConnected( ) const { return d && d->connection && dbus_connection_get_is_connected(d->connection); @@ -307,21 +383,38 @@ QString QDBusConnection::baseService() const bool QDBusConnection::requestName(const QString &name, NameRequestMode mode) { - //FIXME: DBUS_NAME_FLAGS_* are bit fields not enumeration - static const int DBusModes[] = { 0, DBUS_NAME_FLAG_ALLOW_REPLACEMENT, - DBUS_NAME_FLAG_REPLACE_EXISTING }; - Q_ASSERT(mode == 0 || mode == AllowReplace || - mode == ReplaceExisting ); - - DBusError error; - dbus_error_init (&error); - dbus_bus_request_name(d->connection, name.toUtf8(), DBusModes[mode], &error); - if (dbus_error_is_set (&error)) { - qDebug("Error %s\n", error.message); - dbus_error_free (&error); + static const int DBusModes[] = { DBUS_NAME_FLAG_ALLOW_REPLACEMENT, 0, + DBUS_NAME_FLAG_REPLACE_EXISTING | DBUS_NAME_FLAG_ALLOW_REPLACEMENT}; + + int retval = dbus_bus_request_name(d->connection, name.toUtf8(), DBusModes[mode], &d->error); + d->handleError(); + return retval == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER || + retval == DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER; +} + +bool QDBusConnection::releaseName(const QString &name) +{ + int retval = dbus_bus_release_name(d->connection, name.toUtf8(), &d->error); + d->handleError(); + if (lastError().isValid()) return false; - } - return true; + return retval == DBUS_RELEASE_NAME_REPLY_RELEASED; +} + +QString QDBusConnection::getNameOwner(const QString& name) +{ + if (QDBusUtil::isValidUniqueConnectionName(name)) + return name; + if (!d || !QDBusUtil::isValidBusName(name)) + return QString(); + + QDBusMessage msg = QDBusMessage::methodCall(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, "GetNameOwner"); + msg << name; + QDBusMessage reply = sendWithReply(msg); + if (!lastError().isValid() && reply.type() == QDBusMessage::ReplyMessage) + return reply.first().toString(); + return QString(); } -#include "qdbusconnection.moc" +#include "qdbusconnection_p.moc" diff --git a/qt/qdbusconnection.h b/qt/qdbusconnection.h index bbab0ec..c6913b2 100644 --- a/qt/qdbusconnection.h +++ b/qt/qdbusconnection.h @@ -1,6 +1,8 @@ /* qdbusconnection.h QDBusConnection object * * Copyright (C) 2005 Harald Fernengel <harry@kdevelop.org> + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> * * Licensed under the Academic Free License version 2.1 * @@ -15,18 +17,21 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, write to the Free Software Foundation + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #ifndef QDBUSCONNECTION_H #define QDBUSCONNECTION_H -#include "dbus/qdbus.h" +#include "qdbusmacros.h" #include <QtCore/qstring.h> class QDBusConnectionPrivate; +class QDBusXmlParser; +class QDBusObject; +class QDBusInterface; class QDBusError; class QDBusMessage; class QByteArray; @@ -46,8 +51,11 @@ public: bool isConnected() const; QDBusError lastError() const; - enum NameRequestMode { NoReplace = 0, AllowReplace = 1, ReplaceExisting = 2 }; + enum NameRequestMode { NoReplace = 0, ProhibitReplace = 1, ReplaceExisting = 2 }; bool requestName(const QString &name, NameRequestMode mode = NoReplace); + bool releaseName(const QString& name); + QString getNameOwner(const QString& name); + QString baseService() const; @@ -56,13 +64,42 @@ public: int sendWithReplyAsync(const QDBusMessage &message, QObject *receiver, const char *slot) const; - bool connect(const QString &path, const QString &interface, + bool connect(const QString &service, const QString &path, const QString &interface, const QString &name, QObject *receiver, const char *slot); - - bool registerObject(const QString &path, const QString &interface, - QObject *object); + bool connect(const QString &service, const QString &path, const QString &interface, + const QString &name, const QString& signature, + QObject *receiver, const char *slot); + + enum RegisterOption { + ExportForAnyInterface = 0x01, + ExportAdaptors = 0x03, + + ExportOwnSlots = 0x10, + ExportOwnSignals = 0x20, + ExportOwnProperties = 0x40, + ExportOwnContents = 0xf0, + + ExportNonScriptableSlots = 0x100, + ExportNonScriptableSignals = 0x200, + ExportNonScriptableProperties = 0x400, + ExportNonScriptables = 0xf00, + + ExportChildObjects = 0x1000, + + Reexport = 0x100000, + }; + Q_DECLARE_FLAGS(RegisterOptions, RegisterOption); + + bool registerObject(const QString &path, const QString &interface, QObject *object, + RegisterOptions options = ExportOwnContents); + bool registerObject(const QString &path, QObject *object, + RegisterOptions options = ExportAdaptors); void unregisterObject(const QString &path); + QDBusObject findObject(const QString& service, const QString& path); + QDBusInterface findInterface(const QString& service, const QString& path, const QString& interface); + + static QDBusConnection addConnection(BusType type, const QString &name = QLatin1String(default_connection_name)); static QDBusConnection addConnection(const QString &address, @@ -72,7 +109,9 @@ public: QT_STATIC_CONST char *default_connection_name; private: + friend class QDBusObject; QDBusConnectionPrivate *d; }; +Q_DECLARE_OPERATORS_FOR_FLAGS(QDBusConnection::RegisterOptions) #endif diff --git a/qt/qdbusconnection_p.h b/qt/qdbusconnection_p.h index 3f78dad..fa0fdd8 100644 --- a/qt/qdbusconnection_p.h +++ b/qt/qdbusconnection_p.h @@ -1,6 +1,8 @@ /* qdbusconnection_p.h QDBusConnection private object * * Copyright (C) 2005 Harald Fernengel <harry@kdevelop.org> + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> * * Licensed under the Academic Free License version 2.1 * @@ -15,8 +17,8 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, write to the Free Software Foundation + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ @@ -38,16 +40,24 @@ #include "qdbuserror.h" #include <QtCore/qatomic.h> +#include <QtCore/qmutex.h> #include <QtCore/qhash.h> #include <QtCore/qobject.h> #include <QtCore/qpointer.h> #include <QtCore/qvarlengtharray.h> +#include <QtCore/qeventloop.h> +#include <QtCore/qmutex.h> #include <dbus/dbus.h> +#include "qdbusmessage.h" +#include "qdbusintrospection.h" + class QDBusMessage; class QSocketNotifier; class QTimerEvent; +class QDBusObjectPrivate; +class CallDeliveryEvent; typedef struct DBusConnection; typedef struct DBusServer; @@ -56,6 +66,43 @@ class QDBusConnectionPrivate: public QObject { Q_OBJECT public: + // structs and enums + enum ConnectionMode { InvalidMode, ServerMode, ClientMode }; + + struct Watcher + { + Watcher(): watch(0), read(0), write(0) {} + DBusWatch *watch; + QSocketNotifier *read; + QSocketNotifier *write; + }; + + struct SignalHook + { + QString interface, name, signature; + QPointer<QObject> obj; + int midx; + QList<int> params; + }; + + struct ObjectData + { + QPointer<QObject> obj; + int flags; + }; + +public: + // typedefs + typedef QMultiHash<int, Watcher> WatcherHash; + typedef QHash<int, DBusTimeout *> TimeoutHash; + typedef QMultiHash<QString, SignalHook> SignalHookHash; + typedef QHash<QString, ObjectData> ObjectDataHash; + typedef QHash<QString, ObjectDataHash> ObjectHookHash; + typedef QHash<QString, QSharedDataPointer<QDBusIntrospection::Interface> > KnownInterfacesHash; + typedef QHash<QString, QDBusIntrospection::Object* > KnownObjectsHash; + +public: + // public methods QDBusConnectionPrivate(QObject *parent = 0); ~QDBusConnectionPrivate(); @@ -66,66 +113,81 @@ public: void closeConnection(); void timerEvent(QTimerEvent *e); - bool handleSignal(DBusMessage *msg) const; - bool handleObjectCall(DBusMessage *message) const; + bool handleSignal(const QString &path, const QDBusMessage &msg); + bool send(const QDBusMessage &message) const; + int sendWithReplyAsync(const QDBusMessage &message, QObject *receiver, + const char *method) const; + + bool handleSignal(const QDBusMessage &msg); + bool handleObjectCall(const QDBusMessage &message); bool handleError(); + void disposeOfLocked(QDBusIntrospection::Object* obj); + void disposeOf(QDBusObjectPrivate* obj); + QSharedDataPointer<QDBusIntrospection::Interface> findInterface(const QString& name); + QDBusIntrospection::Object* findObject(const QString& service, + const QString& path); + + bool activateReply(QObject *object, int idx, const QList<int>& metaTypes, + const QDBusMessage &msg); + bool activateSignal(const SignalHook& hook, const QDBusMessage &msg); + bool activateCall(QObject* object, int flags, const QDBusMessage &msg); + bool activateAdaptor(QObject *object, int flags, const QDBusMessage &msg); + bool activateObject(const ObjectData& data, const QDBusMessage &msg); + void deliverCall(const CallDeliveryEvent &data) const; + +protected: + virtual void customEvent(QEvent *event); + public slots: + // public slots void socketRead(int); void socketWrite(int); void objectDestroyed(QObject *o); public: + // public member variables DBusError error; QDBusError lastError; - enum ConnectionMode { InvalidMode, ServerMode, ClientMode }; - QAtomic ref; + QMutex mutex; ConnectionMode mode; DBusConnection *connection; DBusServer *server; - static int messageMetaType; - static int registerMessageMetaType(); - bool handleSignal(const QString &path, const QDBusMessage &msg) const; - int sendWithReplyAsync(const QDBusMessage &message, QObject *receiver, - const char *method) const; - - struct Watcher - { - Watcher(): watch(0), read(0), write(0) {} - DBusWatch *watch; - QSocketNotifier *read; - QSocketNotifier *write; - }; - typedef QMultiHash<int, Watcher> WatcherHash; WatcherHash watchers; - - typedef QHash<int, DBusTimeout *> TimeoutHash; TimeoutHash timeouts; - - struct SignalHook - { - QString interface, name; - QPointer<QObject> obj; - int midx; - QVarLengthArray<int, 10> params; - - bool setSlot(const char *slotName); - }; - - typedef QMultiHash<QString, SignalHook> SignalHookHash; SignalHookHash signalHooks; - - struct ObjectHook - { - QString interface; - QPointer<QObject> obj; - }; - typedef QMultiHash<QString, ObjectHook> ObjectHookHash; ObjectHookHash objectHooks; QList<DBusTimeout *> pendingTimeouts; + +public: + // public mutable member variables + mutable KnownInterfacesHash knownInterfaces; + mutable KnownObjectsHash knownObjects; + +public: + // static methods + static int messageMetaType; + static int registerMessageMetaType(); + static int findSlot(QObject *obj, const char *slotName, QList<int>& params); }; +class QDBusReplyWaiter: public QEventLoop +{ + Q_OBJECT +public: + QDBusMessage replyMsg; + +#ifndef QT_NO_DEBUG + int level; + int exec(ProcessEventsFlags flags); + void exit(int = 0); +#endif + +public slots: + void reply(const QDBusMessage &msg); +}; + #endif diff --git a/qt/qdbuserror.cpp b/qt/qdbuserror.cpp index 0f7dc92..64b68b3 100644 --- a/qt/qdbuserror.cpp +++ b/qt/qdbuserror.cpp @@ -1,6 +1,8 @@ /* qdbuserror.h QDBusError object * * Copyright (C) 2005 Harald Fernengel <harry@kdevelop.org> + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> * * Licensed under the Academic Free License version 2.1 * @@ -15,16 +17,17 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, write to the Free Software Foundation + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "qdbuserror.h" -#include <QtCore/qdebug.h> +#include <qdebug.h> #include <dbus/dbus.h> +#include "qdbusmessage.h" QDBusError::QDBusError(const DBusError *error) { @@ -35,6 +38,16 @@ QDBusError::QDBusError(const DBusError *error) msg = QString::fromUtf8(error->message); } +QDBusError::QDBusError(const QDBusMessage &qdmsg) +{ + if (qdmsg.type() != QDBusMessage::ErrorMessage) + return; + + nm = qdmsg.name(); + if (qdmsg.count()) + msg = qdmsg[0].toString(); +} + #ifndef QT_NO_DEBUG QDebug operator<<(QDebug dbg, const QDBusError &msg) { diff --git a/qt/qdbuserror.h b/qt/qdbuserror.h index 07d2c56..9450088 100644 --- a/qt/qdbuserror.h +++ b/qt/qdbuserror.h @@ -1,6 +1,8 @@ /* qdbuserror.h QDBusError object * * Copyright (C) 2005 Harald Fernengel <harry@kdevelop.org> + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> * * Licensed under the Academic Free License version 2.1 * @@ -15,23 +17,25 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, write to the Free Software Foundation + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #ifndef QDBUSERROR_H #define QDBUSERROR_H -#include "dbus/qdbus.h" +#include "qdbusmacros.h" #include <QtCore/qstring.h> struct DBusError; +class QDBusMessage; class QDBUS_EXPORT QDBusError { public: QDBusError(const DBusError *error = 0); + QDBusError(const QDBusMessage& msg); inline QString name() const { return nm; } inline QString message() const { return msg; } diff --git a/qt/qdbusintegrator.cpp b/qt/qdbusintegrator.cpp index 4a19d33..79baa10 100644 --- a/qt/qdbusintegrator.cpp +++ b/qt/qdbusintegrator.cpp @@ -1,6 +1,8 @@ /* qdbusintegrator.cpp QDBusConnection private implementation * * Copyright (C) 2005 Harald Fernengel <harry@kdevelop.org> + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> * * Licensed under the Academic Free License version 2.1 * @@ -15,22 +17,54 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, write to the Free Software Foundation + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ -#include <QtCore/qcoreapplication.h> -#include <QtCore/qcoreevent.h> -#include <QtCore/qdebug.h> -#include <QtCore/qmetaobject.h> -#include <QtCore/qsocketnotifier.h> +#include <qcoreapplication.h> +#include <qcoreevent.h> +#include <qdebug.h> +#include <qmetaobject.h> +#include <qsocketnotifier.h> +#include <qcoreevent.h> +#include <qtimer.h> +#include "qdbusvariant.h" #include "qdbusconnection_p.h" +#include "qdbusinterface_p.h" +#include "qdbusobject_p.h" #include "qdbusmessage.h" +#include "qdbusabstractadaptor.h" int QDBusConnectionPrivate::messageMetaType = 0; +struct QDBusPendingCall +{ + QPointer<QObject> receiver; + QList<int> metaTypes; + int methodIdx; + DBusPendingCall *pending; + const QDBusConnectionPrivate *connection; +}; + +class CallDeliveryEvent: public QEvent +{ +public: + CallDeliveryEvent() + : QEvent(QEvent::User), object(0), flags(0), slotIdx(-1) + { } + + const QDBusConnectionPrivate *conn; + QPointer<QObject> object; + QDBusMessage message; + QList<int> metaTypes; + + int flags; + int slotIdx; + bool generateReply : 1; +}; + static dbus_bool_t qDBusAddTimeout(DBusTimeout *timeout, void *data) { Q_ASSERT(timeout); @@ -103,6 +137,7 @@ static dbus_bool_t qDBusAddWatch(DBusWatch *watch, void *data) watcher.watch = watch; if (QCoreApplication::instance()) { watcher.read = new QSocketNotifier(fd, QSocketNotifier::Read, d); + watcher.read->setEnabled(dbus_watch_get_enabled(watch)); d->connect(watcher.read, SIGNAL(activated(int)), SLOT(socketRead(int))); } } @@ -111,6 +146,7 @@ static dbus_bool_t qDBusAddWatch(DBusWatch *watch, void *data) watcher.watch = watch; if (QCoreApplication::instance()) { watcher.write = new QSocketNotifier(fd, QSocketNotifier::Write, d); + watcher.write->setEnabled(dbus_watch_get_enabled(watch)); d->connect(watcher.write, SIGNAL(activated(int)), SLOT(socketWrite(int))); } } @@ -188,168 +224,560 @@ static DBusHandlerResult qDBusSignalFilter(DBusConnection *connection, bool handled = false; QDBusMessage amsg = QDBusMessage::fromDBusMessage(message); - qDebug() << "got message: " << dbus_message_get_type(message) << amsg; + qDebug() << "got message:" << amsg; if (msgType == DBUS_MESSAGE_TYPE_SIGNAL) { - handled = d->handleSignal(message); + handled = d->handleSignal(amsg); } else if (msgType == DBUS_MESSAGE_TYPE_METHOD_CALL) { - handled = d->handleObjectCall(message); + handled = d->handleObjectCall(amsg); } return handled ? DBUS_HANDLER_RESULT_HANDLED : DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } -static bool qInvokeDBusSlot(const QDBusConnectionPrivate::SignalHook& hook, const QDBusMessage &msg) +static bool checkAsyncTag(const char *tag) { - int count = msg.count(); - if (!(count == hook.params.count() - || (count + 1 == hook.params.count() - && hook.params[count] == QDBusConnectionPrivate::messageMetaType))) + if (!tag || !*tag) return false; - QVarLengthArray<void *, 16> params; - params.append(0); // return value - for (int i = 0; i < msg.count(); ++i) { - const QVariant &v = msg.at(i); - if (int(v.type()) != hook.params[i]) { - return false; - } - params.append(const_cast<void *>(v.constData())); + const char *p = strstr(tag, "async"); + if (p != NULL && + (p == tag || *(p-1) == ' ') && + (p[6] == '\0' || p[6] == ' ')) + return true; + + p = strstr(tag, "Q_ASYNC"); + if (p != NULL && + (p == tag || *(p-1) == ' ') && + (p[8] == '\0' || p[8] == ' ')) + return true; + + return false; +} + +static QList<QByteArray> splitParameters(const char *p) +{ + QList<QByteArray> retval; + ++p; + const char *e = p; + while (*e != ')') { + while (*e != ')' && *e != ',') + ++e; + + // found the end of this parameter + retval += QByteArray(p, e - p); + + if (*e != ')') + p = ++e; } - if (count + 1 == hook.params.count()) - params.append(const_cast<QDBusMessage *>(&msg)); - return hook.obj->qt_metacall(QMetaObject::InvokeMetaMethod, hook.midx, params.data()) < 0; + return retval; } -static bool qInvokeDBusSlot(QObject *object, int idx, const QDBusMessage &msg) +static bool typesMatch(int metaId, QVariant::Type variantType) { - Q_ASSERT(object); + if (metaId == (int)variantType) + return true; - const QMetaMethod method = object->metaObject()->method(idx); - if (!method.signature()) - return false; + if (variantType == QVariant::Int && metaId == QMetaType::Short) + return true; - QVarLengthArray<void *> params; - params.append(0); // ### return type + if (variantType == QVariant::UInt && (metaId == QMetaType::UShort || + metaId == QMetaType::UChar)) + return true; - QList<QByteArray> parameterTypes = method.parameterTypes(); + return false; // no match +} - // check parameters, the slot should have <= parameters than the message - // also allow the QDBusMessage itself as last parameter slot - if ((parameterTypes.count() > msg.count()) - || (parameterTypes.count() + 1 != msg.count()) - && parameterTypes.last() != "QDBusMessage") { - qWarning("Cannot deliver asynchronous reply to object named '%s' because of parameter " - "mismatch. Please check your sendWithReplyAsync() statements.", - object->objectName().toLocal8Bit().constData()); - return false; +static int returnTypeId(const char *name) +{ + if (!name || !*name) + return QMetaType::Void; + + // force normalizedSignature to work for us + QVarLengthArray<char, 32> buf(strlen(name) + 3); + buf.append("_(", 2); + buf.append(name, strlen(name)); + buf.append(')'); + + QByteArray normalized = QMetaObject::normalizedSignature( buf.data() ); + normalized.truncate(normalized.length() - 1); + return QMetaType::type(normalized.constData() + 2); +} + +static int typeId(const char *type) +{ + int id = static_cast<int>( QVariant::nameToType(type) ); + if (id == QVariant::UserType) + id = QMetaType::type(type); + + switch (id) { + case QVariant::Bool: + case QVariant::Int: + case QVariant::UInt: + case QVariant::Char: + case QMetaType::Short: + case QMetaType::UShort: + case QMetaType::UChar: + case QVariant::LongLong: + case QVariant::ULongLong: + case QVariant::Double: + case QVariant::String: + case QVariant::Date: + case QVariant::Time: + case QVariant::DateTime: + case QVariant::Map: + case QVariant::StringList: + case QVariant::ByteArray: + case QVariant::List: + return id; + + default: + if (id == qMetaTypeId<QDBusVariant>() || id == QDBusConnectionPrivate::messageMetaType) + return id; + + return 0; // invalid + } +} + + +// 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 +static int parametersForMethod(const QByteArray &sig, QList<int>& metaTypes) +{ + if (sig.indexOf('<') != -1) { + qWarning("Could not parse the method '%s'", sig.constData()); + // there's no type with templates that we can handle + return -1; } - int i; - for (i = 0; i < parameterTypes.count(); ++i) { - const QByteArray param = parameterTypes.at(i); - if (param == msg.at(i).typeName()) { - params.append(const_cast<void *>(msg.at(i).constData())); - } else if (i == parameterTypes.count() - 1 && param == "QDBusMessage") { - params.append(const_cast<void *>(static_cast<const void *>(&msg))); - } else { - qWarning("Parameter mismatch while delivering message, expected '%s', got '%s'", - msg.at(i).typeName(), param.constData()); - return false; + int paren = sig.indexOf('('); + QList<QByteArray> parameterTypes = splitParameters(sig.data() + paren); + metaTypes.clear(); + + metaTypes.append(0); // return type + int inputCount = 0; + bool seenMessage = false; + foreach (QByteArray type, parameterTypes) { + if (type.endsWith('*')) { + qWarning("Could not parse the method '%s'", sig.constData()); + // pointer? + return -1; + } + + if (type.endsWith('&')) { + type.truncate(type.length() - 1); + int id = typeId(type); + if (id == 0) { + qWarning("Could not parse the method '%s'", sig.constData()); + // invalid type in method parameter list + return -1; + } + + metaTypes.append( id ); + + if (metaTypes.last() == 0) { + qWarning("Could not parse the method '%s'", sig.constData()); + // void? + return -1; + } + + continue; + } + + if (seenMessage) { // && !type.endsWith('&') + qWarning("Could not parse the method '%s'", sig.constData()); + // non-output parameters after message or after output params + return -1; // not allowed + } + + int id = typeId(type); + if (id == 0) { + qWarning("Could not parse the method '%s'", sig.constData()); + // invalid type in method parameter list + return -1; } + metaTypes.append(id); + ++inputCount; + + if (id == QDBusConnectionPrivate::messageMetaType) + seenMessage = true; } - return object->qt_metacall(QMetaObject::InvokeMetaMethod, idx, params.data()) < 0; + + return inputCount; } -static bool qInvokeDBusSlot(QObject *object, QDBusMessage *msg) +static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags, + const QDBusTypeList &types, QList<int>& metaTypes, bool &isAsync, int &msgPos) +{ + // find the first slot + const QMetaObject *super = mo; + while (qstrcmp(super->className(), "QObject") != 0 && + qstrcmp(super->className(), "QDBusAbstractAdaptor") != 0) + super = super->superClass(); + + int attributeMask = (flags & QDBusConnection::ExportNonScriptableSlots) ? + 0 : QMetaMethod::Scriptable; + + for (int idx = super->methodCount() ; idx <= mo->methodCount(); ++idx) { + QMetaMethod mm = mo->method(idx); + + // check access: + if (mm.access() != QMetaMethod::Public) + continue; + + // check type: + // unnecessary, since slots are never public: + //if (mm.methodType() != QMetaMethod::Slot) + // continue; + + // check name: + QByteArray sig = QMetaObject::normalizedSignature(mm.signature()); + int paren = sig.indexOf('('); + if (paren != name.length() || !sig.startsWith( name )) + continue; + + int returnType = returnTypeId(mm.typeName()); + isAsync = checkAsyncTag(mm.tag()); + + // consistency check: + if (isAsync && returnType != QMetaType::Void) + continue; + + int inputCount = parametersForMethod(sig, metaTypes); + if (inputCount == -1) + continue; // problem parsing + + metaTypes[0] = returnType; + msgPos = 0; + if (inputCount > 0 && + metaTypes.at(inputCount) == QDBusConnectionPrivate::messageMetaType) { + // no input parameters is allowed as long as the message meta type is there + msgPos = inputCount; + --inputCount; + } + + if (inputCount) { + // try to match the parameters + if (inputCount < types.count()) + continue; // not enough parameters + + bool matches = true; + int i; + for (i = 0; i < types.count(); ++i) + if ( !typesMatch(metaTypes.at(i + 1), types.at(i).qvariantType()) ) { + matches = false; + break; + } + + if (!matches) + continue; // we didn't match them all + + // consistency check: + if (isAsync && metaTypes.count() > i + 1) + continue; + } + + if (!msgPos && (mm.attributes() & attributeMask) != attributeMask) + continue; // not exported + + // if we got here, this slot matched + return idx; + } + + // no slot matched + return -1; +} + +bool 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) + return activateReply(hook.obj, hook.midx, hook.params, msg); +} + +bool QDBusConnectionPrivate::activateReply(QObject *object, int idx, const QList<int> &metaTypes, + const QDBusMessage &msg) +{ + // This is called by qDBusResultReceived and is used to deliver the return value + // 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) Q_ASSERT(object); - Q_ASSERT(msg); - const QMetaObject *mo = object->metaObject(); - QVarLengthArray<void *> params; - params.append(0); // ### return type + int n = metaTypes.count() - 1; + if (metaTypes[n] == QDBusConnectionPrivate::messageMetaType) + --n; + + // check that types match + for (int i = 0; i < n; ++i) + if (!typesMatch(metaTypes.at(i + 1), msg.at(i).type())) + return false; // no match + + // we can deliver + // prepare for the call + CallDeliveryEvent *data = new CallDeliveryEvent; + data->conn = this; + data->object = object; + data->flags = 0; + data->message = msg; + data->metaTypes = metaTypes; + data->slotIdx = idx; + data->generateReply = false; + + QCoreApplication::postEvent( this, data ); + + return true; +} + +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 name + // - 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. + // + // Because the marshalling of D-Bus data into QVariant loses the information on + // the original types, the message signature is used to determine the original type. + // Aside from that, the "int" and "unsigned" types will be tried as well. + // + // Return message handling depends on whether the asynchronous tag ("async" or "Q_ASYNC") + // tag is found, whether the slot takes a QDBusMessage parameter and whether there are + // return values (non-const reference parameters or a return type). + // The table indicates the possibilities: + // async QDBusMessage parameter return values return message generated + // yes irrelevant irrelevant no + // no irrelevant yes yes + // no yes no no + // no no no yes + // + // 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. + + Q_ASSERT(object); - /* Try to find a slot with all args and the QDBusMessage */ - QByteArray slotName = msg->name().toUtf8(); // QVarLengthArray? - slotName.append("("); - for (int i = 0; i < msg->count(); ++i) { - slotName.append(msg->at(i).typeName()).append(","); - params.append(const_cast<void *>(msg->at(i).constData())); + QList<int> metaTypes; + int idx; + bool isAsync; + int msgPos; + + { + const QMetaObject *mo = object->metaObject(); + QDBusTypeList typeList(msg.signature().toUtf8()); + + // find a slot that matches according to the rules above + idx = ::findSlot(mo, msg.name().toUtf8(), flags, typeList, metaTypes, isAsync, msgPos); + if (idx == -1) + // no match + return false; } - slotName.append("QDBusMessage)"); - int idx = mo->indexOfSlot(slotName.constData()); - if (idx >= 0) { - params.append(msg); - return object->qt_metacall(QMetaObject::InvokeMetaMethod, idx, params.data()) < 0; + bool generateReply; + if (isAsync) + generateReply = false; + else if (metaTypes[0] != QMetaType::Void) + generateReply = true; + else { + if (msgPos != 0) + // generate a reply if there are more parameters past QDBusMessage + generateReply = metaTypes.count() > msgPos + 1; + else + // generate a reply if there are more parameters than input parameters + generateReply = metaTypes.count() > msg.count() + 1; } - /* Try to find only args, without the QDBusMessage */ - slotName.chop(13); - slotName[slotName.count() - 1] = ')'; + // found the slot to be called + // prepare for the call: + CallDeliveryEvent *call = new CallDeliveryEvent; + call->conn = this; - idx = mo->indexOfSlot(slotName.constData()); - if (idx >= 0 && (mo->method(idx).attributes() & QMetaMethod::Scriptable)) - return object->qt_metacall(QMetaObject::InvokeMetaMethod, idx, params.data()) < 0; + // parameters: + call->object = object; + call->flags = flags; + call->message = msg; - /* Try to find a slot with only QDBusMessage */ - slotName = msg->name().toUtf8(); - slotName.append("(QDBusMessage)"); + // save our state: + call->metaTypes = metaTypes; + call->slotIdx = idx; + call->generateReply = generateReply; - idx = mo->indexOfSlot(slotName.constData()); - if (idx >= 0) - return QMetaObject::invokeMethod(object, msg->name().toUtf8().constData(), - Q_ARG(QDBusMessage, *msg)); + QCoreApplication::postEvent( this, call ); - return false; + // ready + return true; } -int QDBusConnectionPrivate::registerMessageMetaType() +void QDBusConnectionPrivate::deliverCall(const CallDeliveryEvent& data) const { - int tp = messageMetaType = qRegisterMetaType<QDBusMessage>("QDBusMessage"); - return tp; -} + // resume state: + const QList<int>& metaTypes = data.metaTypes; + const QDBusMessage& msg = data.message; + + QVarLengthArray<void *, 10> params; + params.reserve(metaTypes.count()); + +#if __BYTE_ORDER != __LITTLE_ENDIAN + union integer + { + short s; + unsigned short us; + unsigned char uc; + } + QVarLengthArray<integer, 4> auxParameters; +#endif + // let's create the parameter list -bool QDBusConnectionPrivate::SignalHook::setSlot(const char *slotName) -{ - Q_ASSERT(static_cast<QObject *>(obj)); Q_ASSERT(slotName); + // first one is the return type -- add it below + params.append(0); - QByteArray normalizedName = QMetaObject::normalizedSignature(slotName); - const QMetaObject *mo = obj->metaObject(); - midx = mo->indexOfMethod(normalizedName.constData()); - if (midx < 0) - return false; + // add the input parameters + int i; + for (i = 0; i < msg.count(); ++i) { + int id = metaTypes[i + 1]; + if (id == QDBusConnectionPrivate::messageMetaType) + break; - const QList<QByteArray> ptypes = mo->method(midx).parameterTypes(); - for (int i = 0; i < ptypes.count(); ++i) { - int t = QVariant::nameToType(ptypes.at(i).constData()); - if (t == QVariant::UserType) - t = QMetaType::type(ptypes.at(i).constData()); - if (t == QVariant::Invalid) - return false; - params.append(t); +#if __BYTE_ORDER == __LITTLE_ENDIAN + params.append(const_cast<void *>( msg.at(i).constData() )); +#else + if (id == msg.at(i).type()) + params.append(const_cast<void *>( msg.at(i).constData() )); + else { + // need some help + integer aux; + const QVariant &var = msg.at(i); + if (id == QMetaType::Short) + aux.s = var.toInt(); + else if (id == QMetaType::UShort) + aux.us = var.toUInt(); + else + aux.uc = var.toUInt(); + auxParameters.append(aux); + params.append( &auxParameters[auxParameters.count()] ); + } +#endif } - return true; + bool takesMessage = false; + if (metaTypes.count() > i + 1 && metaTypes[i + 1] == QDBusConnectionPrivate::messageMetaType) { + 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); + params.append( arg.data() ); + outputArgs.append( arg ); + } + for ( ; i < metaTypes.count(); ++i) { + QVariant arg(metaTypes[i], null); + params.append( arg.data() ); + outputArgs.append( arg ); + } + + // make call: + bool fail; + if (data.object.isNull()) + fail = true; + else + fail = data.object->qt_metacall(QMetaObject::InvokeMetaMethod, + data.slotIdx, params.data()) >= 0; + + // do we create a reply? + if (data.generateReply) { + if (!fail) { + // yes + QDBusMessage reply = QDBusMessage::methodReply(msg); + reply += outputArgs; + + qDebug() << "Automatically sending reply:" << reply; + send(reply); + } + else { + // generate internal error + QDBusMessage reply = QDBusMessage::error(msg, "com.trolltech.QtDBus.InternalError", + "Failed to deliver message"); + qDebug("Internal error: Failed to deliver message"); + send(reply); + } + } + + return; +} + +void QDBusConnectionPrivate::customEvent(QEvent *event) +{ + // nothing else should be sending custom events at us + CallDeliveryEvent* call = static_cast<CallDeliveryEvent *>(event); + + // self check: + Q_ASSERT(call->conn == this); + + deliverCall(*call); } QDBusConnectionPrivate::QDBusConnectionPrivate(QObject *parent) : QObject(parent), ref(1), mode(InvalidMode), connection(0), server(0) { + extern bool qDBusInitThreads(); static const int msgType = registerMessageMetaType(); + static const bool threads = qDBusInitThreads(); + Q_UNUSED(msgType); + Q_UNUSED(threads); dbus_error_init(&error); } QDBusConnectionPrivate::~QDBusConnectionPrivate() { + Q_ASSERT(knownObjects.isEmpty()); + if (dbus_error_is_set(&error)) dbus_error_free(&error); closeConnection(); + + KnownInterfacesHash::iterator it = knownInterfaces.begin(); + while (it != knownInterfaces.end()) { + const QSharedDataPointer<QDBusIntrospection::Interface>& item = *it; + + const_cast<QDBusIntrospection::Interface*>(item.constData())->ref.deref(); + + it = knownInterfaces.erase(it); + } } void QDBusConnectionPrivate::closeConnection() @@ -413,7 +841,6 @@ void QDBusConnectionPrivate::socketRead(int fd) } if (mode == ClientMode) while (dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS); - // ### break out of loop? } void QDBusConnectionPrivate::socketWrite(int fd) @@ -432,11 +859,20 @@ void QDBusConnectionPrivate::objectDestroyed(QObject *obj) { ObjectHookHash::iterator it = objectHooks.begin(); while (it != objectHooks.end()) { - if (static_cast<QObject *>(it.value().obj) == obj) + ObjectDataHash::iterator dit = it->begin(); + while (dit != it->end()) { + if (static_cast<QObject *>(dit.value().obj) == obj) + dit = it->erase(dit); + else + ++dit; + } + + if (it->isEmpty()) it = objectHooks.erase(it); else ++it; } + SignalHookHash::iterator sit = signalHooks.begin(); while (sit != signalHooks.end()) { if (static_cast<QObject *>(sit.value().obj) == obj) @@ -447,56 +883,135 @@ void QDBusConnectionPrivate::objectDestroyed(QObject *obj) obj->disconnect(this); } -bool QDBusConnectionPrivate::handleObjectCall(DBusMessage *message) const +bool QDBusConnectionPrivate::activateAdaptor(QObject* object, int flags, + const QDBusMessage &msg) { - QDBusMessage msg = QDBusMessage::fromDBusMessage(message); + // 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 - ObjectHook hook; - ObjectHookHash::ConstIterator it = objectHooks.find(msg.path()); - while (it != objectHooks.constEnd() && it.key() == msg.path()) { - if (it.value().interface == msg.interface()) { - hook = it.value(); - break; - } else if (it.value().interface.isEmpty()) { - hook = it.value(); + Q_ASSERT(object); + flags |= QDBusConnection::ExportNonScriptableSlots; + + const QObjectList& children = object->children(); + QObjectList::const_iterator child = children.begin(), + end = children.end(); + for ( ; child != end; ++child) { + // check if this is an adaptor + if (!qobject_cast<QDBusAbstractAdaptor *>(*child)) + continue; // not adaptor + + const QMetaObject *mo = (*child)->metaObject(); + int ciend = mo->classInfoCount(); + for (int i = 0; i < ciend; ++i) { + QMetaClassInfo mci = mo->classInfo(i); + if (strcmp(mci.name(), "DBus Interface") == 0 && *mci.value()) { + // one interface. + // is this it? + if (msg.interface().isEmpty() || msg.interface() == mci.value()) + return activateCall(*child, flags, msg); + } } - ++it; } + return false; +} - if (!hook.obj) { - qDebug("NO OBJECT for %s", msg.path().toLocal8Bit().constData()); - return false; +int QDBusConnectionPrivate::registerMessageMetaType() +{ + int tp = messageMetaType = qRegisterMetaType<QDBusMessage>("QDBusMessage"); + return tp; +} + +int QDBusConnectionPrivate::findSlot(QObject* obj, const char *slotName, QList<int>& params) +{ + Q_ASSERT(slotName); + QByteArray normalizedName = QMetaObject::normalizedSignature(slotName); + int midx = obj->metaObject()->indexOfMethod(normalizedName); + if (midx == -1) { + qWarning("No such slot '%s' while connecting D-Bus", slotName); + return -1; } - if (!qInvokeDBusSlot(hook.obj, &msg)) { - qDebug("NO SUCH SLOT: %s(QDBusMessage)", msg.name().toLocal8Bit().constData()); + int inputCount = parametersForMethod(normalizedName, params); + if ( inputCount == -1 || inputCount + 1 != params.count() ) + return -1; // failed to parse or invalid arguments or output arguments + + return midx; +} + +bool QDBusConnectionPrivate::activateObject(const QDBusConnectionPrivate::ObjectData &hook, + const QDBusMessage &msg) +{ + if (!hook.obj) + return false; // object is gone + + if (hook.flags & QDBusConnection::ExportAdaptors) + return activateAdaptor(hook.obj, hook.flags, msg); + else + return activateCall(hook.obj, hook.flags, msg); +} + +bool QDBusConnectionPrivate::handleObjectCall(const QDBusMessage &msg) +{ + ObjectHookHash::ConstIterator it = objectHooks.find(msg.path()); + if (it == objectHooks.constEnd()) return false; + + bool ok = false; + const ObjectDataHash& hook = it.value(); + ObjectDataHash::ConstIterator hit; + if (msg.interface().isEmpty()) { + // we must go through all the objects and interfaces + + for (hit = hook.begin(); hit != hook.end(); ++hit) { + ok = activateObject(hit.value(), msg); + if (ok) + break; // processed + } + } else { + // find the interface: + hit = hook.find(msg.interface()); + if (hit != hook.end()) + ok = activateObject(hit.value(), msg); + + if (!ok) { + // try adaptors (or any interface) + hit = hook.find(QString()); + if (hit != hook.end()) + ok = activateObject(hit.value(), msg); + } } - return true; + qDebug(ok ? "Call scheduled" : "Call failed"); + return ok; } -bool QDBusConnectionPrivate::handleSignal(const QString &path, const QDBusMessage &msg) const +bool QDBusConnectionPrivate::handleSignal(const QString &path, const QDBusMessage &msg) { SignalHookHash::const_iterator it = signalHooks.find(path); qDebug("looking for: %s", path.toLocal8Bit().constData()); qDebug() << signalHooks.keys(); - while (it != signalHooks.constEnd() && it.key() == path) { + for ( ; it != signalHooks.constEnd() && it.key() == path; ++ it) { const SignalHook &hook = it.value(); - if ((hook.name.isEmpty() || hook.name == msg.name()) - && (hook.interface.isEmpty() || hook.interface == msg.interface())) - qInvokeDBusSlot(hook, msg); - ++it; + if ( hook.obj.isNull() ) + continue; + if ( !hook.name.isEmpty() && hook.name != msg.name() ) + continue; + if ( !hook.interface.isEmpty() && hook.interface != msg.interface() ) + continue; + if ( !hook.signature.isEmpty() && hook.signature != msg.signature() ) + continue; + + activateSignal(hook, msg); } return true; } -bool QDBusConnectionPrivate::handleSignal(DBusMessage *message) const +bool QDBusConnectionPrivate::handleSignal(const QDBusMessage& msg) { - QDBusMessage msg = QDBusMessage::fromDBusMessage(message); - // yes, it is a single "|" below... - return handleSignal(QString(), msg) | handleSignal(msg.path(), msg); + return handleSignal(QString(), msg) | handleSignal(msg.sender() + msg.path(), msg); } static dbus_int32_t server_slot = -1; @@ -569,53 +1084,152 @@ void QDBusConnectionPrivate::setConnection(DBusConnection *dbc) qDebug("base service: %s", service); } -struct QDBusPendingCall -{ - QPointer<QObject> receiver; - int methodIdx; - DBusPendingCall *pending; -}; - static void qDBusResultReceived(DBusPendingCall *pending, void *user_data) { QDBusPendingCall *call = reinterpret_cast<QDBusPendingCall *>(user_data); + QDBusConnectionPrivate *connection = const_cast<QDBusConnectionPrivate *>(call->connection); Q_ASSERT(call->pending == pending); if (!call->receiver.isNull() && call->methodIdx != -1) { DBusMessage *reply = dbus_pending_call_steal_reply(pending); - qInvokeDBusSlot(call->receiver, call->methodIdx, QDBusMessage::fromDBusMessage(reply)); + connection->activateReply(call->receiver, call->methodIdx, call->metaTypes, + QDBusMessage::fromDBusMessage(reply)); } dbus_pending_call_unref(pending); delete call; } +bool QDBusConnectionPrivate::send(const QDBusMessage& message) const +{ + DBusMessage *msg = message.toDBusMessage(); + if (!msg) + return false; + + dbus_message_set_no_reply(msg, true); // the reply would not be delivered to anything + + qDebug() << "sending message:" << message; + bool isOk = dbus_connection_send(connection, msg, 0); + dbus_message_unref(msg); + return isOk; +} + int QDBusConnectionPrivate::sendWithReplyAsync(const QDBusMessage &message, QObject *receiver, - const char *method) const + const char *method) const { DBusMessage *msg = message.toDBusMessage(); if (!msg) return 0; int slotIdx = -1; - if (receiver && method && *method) { - QByteArray normalized = QMetaObject::normalizedSignature(method + 1); - slotIdx = receiver->metaObject()->indexOfMethod(normalized.constData()); - if (slotIdx == -1) - qWarning("QDBusConnection::sendWithReplyAsync: no such method: '%s'", - normalized.constData()); - } + QList<int> metaTypes; + if (receiver && method && *method) + slotIdx = findSlot(receiver, method + 1, metaTypes); + qDebug() << "sending message:" << message; DBusPendingCall *pending = 0; if (dbus_connection_send_with_reply(connection, msg, &pending, message.timeout())) { if (slotIdx != -1) { QDBusPendingCall *pcall = new QDBusPendingCall; pcall->receiver = receiver; + pcall->metaTypes = metaTypes; pcall->methodIdx = slotIdx; + pcall->connection = this; pcall->pending = dbus_pending_call_ref(pending); dbus_pending_call_set_notify(pending, qDBusResultReceived, pcall, 0); } + dbus_pending_call_unref(pending); return dbus_message_get_serial(msg); } return 0; } + +QSharedDataPointer<QDBusIntrospection::Interface> +QDBusConnectionPrivate::findInterface(const QString& name) +{ + QMutexLocker locker(&mutex); + QSharedDataPointer<QDBusIntrospection::Interface> data = knownInterfaces.value(name); + if (!data) { + data = new QDBusIntrospection::Interface; + data->name = name; + data->ref.ref(); // don't delete + + knownInterfaces.insert(name, data); + } + return data; +} + +QDBusIntrospection::Object* +QDBusConnectionPrivate::findObject(const QString& service, const QString& path) +{ + QMutexLocker locker(&mutex); + QDBusIntrospection::Object* data = knownObjects.value(service + path); + if (!data) { + data = new QDBusIntrospection::Object; + data->service = service; + data->path = path; + + knownObjects.insert(service + path, data); + } + + return data; +} + +void QDBusConnectionPrivate::disposeOfLocked(QDBusIntrospection::Object* p) +{ + if (p && !p->ref.deref()) { // ref-- + // no one else is using it + // get rid of the reference + QString objName = p->service + p->path; + +#ifndef QT_NO_DEBUG + // debug code + Q_ASSERT(p == knownObjects.take(objName)); +#else + // non-debug + knownObjects.remove(objName); +#endif + + // remove sub-objects too + if (!objName.endsWith('/')) + objName.append('/'); + foreach (QString subObjName, p->childObjects) + disposeOfLocked(knownObjects.value(objName + subObjName)); + + delete p; + } +} + +void QDBusConnectionPrivate::disposeOf(QDBusObjectPrivate* p) +{ + // We're called from QDBusConnectionPrivate's destructor + // that means the object it represents is going out of scope + + QMutexLocker locker(&mutex); + disposeOfLocked( const_cast<QDBusIntrospection::Object*>(p->data) ); +} + +#ifndef QT_NO_DEBUG +int QDBusReplyWaiter::exec(QEventLoop::ProcessEventsFlags flags) +{ + static int eventlevel; + level = ++eventlevel; + qDebug("QDBusReplyWaiter::exec %p level %d starting", this, level); + int retcode = QEventLoop::exec(flags); + qDebug("QDBusReplyWaiter::exec %p level %d exiting", this, level); + --eventlevel; + return retcode; +} + +void QDBusReplyWaiter::exit(int retcode) +{ + qDebug("QDBusReplyWaiter::exit %p level %d called", this, level); + QEventLoop::exit(retcode); +} +#endif + +void QDBusReplyWaiter::reply(const QDBusMessage &msg) +{ + replyMsg = msg; + QTimer::singleShot(0, this, SLOT(quit())); +} diff --git a/qt/qdbusinterface.cpp b/qt/qdbusinterface.cpp new file mode 100644 index 0000000..36354fc --- /dev/null +++ b/qt/qdbusinterface.cpp @@ -0,0 +1,256 @@ +/* -*- C++ -*- + * + * Copyright (C) 2005 Thiago Macieira <thiago@kde.org> + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "qdbusinterface.h" +#include "qdbusobject.h" +#include "qdbusstandardinterfaces.h" + +#include "qdbusinterface_p.h" + +QDBusInterface::QDBusInterface(QDBusInterfacePrivate* p) + : d(p) +{ + d->ref.ref(); +} + +QDBusInterface::QDBusInterface(const QDBusObject& obj, const QString& name) + : d(0) +{ + *this = obj.connection().findInterface(obj.service(), obj.path(), name); +} + +QDBusInterface::QDBusInterface(QDBusConnection& conn, const QString& service, const QString& path, + const QString& name) + : d(0) +{ + *this = conn.findInterface(service, path, name); +} + +QDBusInterface::~QDBusInterface() +{ + if (!d->ref.deref()) + delete d; +} + +QDBusInterface& QDBusInterface::operator=(const QDBusInterface& other) +{ + other.d->ref.ref(); + QDBusInterfacePrivate* old = qAtomicSetPtr(&d, other.d); + if (old && !old->ref.deref()) + delete old; + + return *this; +} + +QDBusConnection QDBusInterface::connection() const +{ + return d->conn; +} + +QString QDBusInterface::service() const +{ + return d->service; +} + +QString QDBusInterface::path() const +{ + return d->path; +} + +QString QDBusInterface::interface() const +{ + return d->data->name; +} + +QString QDBusInterface::introspectionData() const +{ + d->introspect(); + return d->data->introspection; +} + +const QDBusIntrospection::Interface& QDBusInterface::interfaceData() const +{ + d->introspect(); + return *d->data; +} + +const QDBusIntrospection::Annotations& QDBusInterface::annotationData() const +{ + d->introspect(); + return d->data->annotations; +} + +const QDBusIntrospection::Methods& QDBusInterface::methodData() const +{ + d->introspect(); + return d->data->methods; +} + +const QDBusIntrospection::Signals& QDBusInterface::signalData() const +{ + d->introspect(); + return d->data->signals_; +} + +const QDBusIntrospection::Properties& QDBusInterface::propertyData() const +{ + d->introspect(); + return d->data->properties; +} + +QDBusMessage QDBusInterface::callWithArgs(const QDBusIntrospection::Method& method, + const QList<QVariant>& a_args, + CallMode mode) +{ + QString signature(""); // empty, not null + QVariantList args = a_args; + + if (!method.inputArgs.isEmpty()) + { + // go over the list of parameters for the method + QDBusIntrospection::Arguments::const_iterator it = method.inputArgs.begin(), + end = method.inputArgs.end(); + int arg; + for (arg = 0; it != end; ++it, ++arg) + { + // find the marshalled name for this type + QString typeSig = QLatin1String(it->type.dbusSignature()); + signature += typeSig; + } + } + else + args.clear(); + + if (method.annotations.contains(ANNOTATION_NO_WAIT)) + mode = NoWaitForReply; + + return callWithArgs(method.name, signature, args, mode); +} + +QDBusMessage QDBusInterface::callWithArgs(const QString& method, const QList<QVariant>& args, + CallMode mode) +{ + QString m = method, sig; + // split out the signature from the method + int pos = method.indexOf('.'); + if (pos != -1) { + m.truncate(pos); + sig = method.mid(pos + 1); + } + return callWithArgs(m, sig, args, mode); +} + +QDBusMessage QDBusInterface::callWithArgs(const QString& method, const QString& signature, + const QList<QVariant>& args, CallMode mode) +{ + QDBusMessage msg = QDBusMessage::methodCall(service(), path(), interface(), method, signature); + msg.QList<QVariant>::operator=(args); + + QDBusMessage reply; + if (mode == WaitForReply) + reply = d->conn.sendWithReply(msg); + else + d->conn.send(msg); + + d->lastError = reply; // will clear if reply isn't an error + + // ensure that there is at least one element + if (reply.isEmpty()) + reply << QVariant(); + + return reply; +} + +bool QDBusInterface::connect(const QDBusIntrospection::Signal& sig, QObject* obj, const char *slot) +{ + QString signature(""); // empty, not null + + if (!sig.outputArgs.isEmpty()) + { + // go over the list of parameters for the method + QDBusIntrospection::Arguments::const_iterator it = sig.outputArgs.begin(), + end = sig.outputArgs.end(); + int arg; + for (arg = 0; it != end; ++it, ++arg) + { + // find the marshalled name for this type + QString typeSig = QLatin1String(it->type.dbusSignature()); + signature += typeSig; + } + } + + return connect(sig.name, signature, obj, slot); +} + +bool QDBusInterface::connect(const QString& signalName, QObject* obj, const char *slot) +{ + QString s = signalName, sig; + // split out the signature from the name + int pos = signalName.indexOf('.'); + if (pos != -1) { + s.truncate(pos); + sig = signalName.mid(pos + 1); + } + return connect(s, sig, obj, slot); +} + +bool QDBusInterface::connect(const QString& signalName, const QString& signature, + QObject* obj, const char *slot) +{ + return d->conn.connect(service(), path(), interface(), signalName, signature, obj, slot); +} + +QVariant QDBusInterface::propertyGet(const QDBusIntrospection::Property& prop) +{ + // sanity checking + if (prop.access == QDBusIntrospection::Property::Write) + return QVariant(); // write-only prop + + QDBusPropertiesInterface pi(object()); + return pi.get(interface(), prop.name); +} + +QVariant QDBusInterface::propertyGet(const QString& propName) +{ + // can't do sanity checking + QDBusPropertiesInterface pi(object()); + return pi.get(interface(), propName); +} + +void QDBusInterface::propertySet(const QDBusIntrospection::Property& prop, QVariant newValue) +{ + // sanity checking + if (prop.access == QDBusIntrospection::Property::Read) + return; + + QDBusPropertiesInterface pi(object()); + pi.set(interface(), prop.name, newValue); +} + +void QDBusInterface::propertySet(const QString& propName, QVariant newValue) +{ + // can't do sanity checking + QDBusPropertiesInterface pi(object()); + pi.set(interface(), propName, newValue); +} diff --git a/qt/qdbusinterface.h b/qt/qdbusinterface.h new file mode 100644 index 0000000..2b96cae --- /dev/null +++ b/qt/qdbusinterface.h @@ -0,0 +1,310 @@ +/* -*- C++ -*- + * + * Copyright (C) 2005 Thiago Macieira <thiago@kde.org> + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef QDBUSINTERFACE_H +#define QDBUSINTERFACE_H + +#include "qdbusmessage.h" +#include "qdbusobject.h" +#include "qdbusintrospection.h" +#include <QtCore/qstring.h> +#include <QtCore/qvariant.h> +#include <QtCore/qlist.h> + +class QDBusConnection; + +class QDBusInterfacePrivate; +/** + * Base class for all DBUS interfaces in the QtDBUS binding. + */ +class QDBUS_EXPORT QDBusInterface +{ + friend class QDBusConnection; + +public: + enum CallMode { + WaitForReply, + NoWaitForReply + }; + +public: + /** + * Construct an interface of the given name + */ + QDBusInterface(const QDBusObject& obj, const QString& name); + + /** + * @overload. + * Construct an interface of the given name + */ + QDBusInterface(QDBusConnection& conn, const QString& service, const QString& path, const QString& name); + + /** + * Construct a copy of the interface. + */ + QDBusInterface(const QDBusInterface&); + + /** + * Destructs this interface. + */ + virtual ~QDBusInterface(); + + /** + * Copy the interface. + */ + QDBusInterface& operator=(const QDBusInterface&); + + /** + * Returns the object associated with this interface. + */ + inline QDBusObject object() + { return QDBusObject(*this); } + + inline const QDBusObject object() const + { return QDBusObject(*this); } + + /** + * Returns the connection this interface is on. + */ + QDBusConnection connection() const; + + /** + * Returns the name of the service this interface is associated with. + */ + QString service() const; + + /** + * Returns the object path that this interface is associated with. + */ + QString path() const; + + /** + * Returns the name of this interface. + */ + QString interface() const; + + /** + * Returns the introspection XML fragment data of this interface. + */ + virtual QString introspectionData() const; + + /** + * Returns the interface data for this interface. + */ + const QDBusIntrospection::Interface& interfaceData() const; + + /** + * Returns the annotations present in this interface, if any. + */ + const QDBusIntrospection::Annotations& annotationData() const; + + /** + * List all methods in this interface. + */ + const QDBusIntrospection::Methods& methodData() const; + + /** + * List all signals in this interface. + */ + const QDBusIntrospection::Signals& signalData() const; + + /** + * List all properties in this interface. + */ + const QDBusIntrospection::Properties& propertyData() const; + + /** + * Call the given method. + */ + QDBusMessage callWithArgs(const QDBusIntrospection::Method& method, + const QList<QVariant>& args = QList<QVariant>(), + CallMode mode = WaitForReply); + + /** + * Call the given method. + */ + QDBusMessage callWithArgs(const QString& method, const QList<QVariant>& args = QList<QVariant>(), + CallMode mode = WaitForReply); + + /** + * Call the given method. + */ + QDBusMessage callWithArgs(const QString& method, const QString& signature, + const QList<QVariant>& args = QList<QVariant>(), + CallMode mode = WaitForReply); + + /** + * Connects the DBUS signal to the given slot. + */ + bool connect(const QDBusIntrospection::Signal&, QObject* obj, const char *slot); + + /** + * Connects the DBUS signal to the given slot. + */ + bool connect(const QString& signalName, QObject* obj, const char *slot); + + /** + * Connects the DBUS signal to the given slot. + */ + bool connect(const QString& signalName, const QString& signature, + QObject* obj, const char *slot); + + /** + * Gets the value of the given property. + */ + QVariant propertyGet(const QDBusIntrospection::Property&); + + /** + * Gets the value of the given property. + */ + QVariant propertyGet(const QString& property); + + /** + * Sets the value of the given property. + */ + void propertySet(const QDBusIntrospection::Property&, QVariant newValue); + + /** + * Sets the value of the given property. + */ + void propertySet(const QString& property, QVariant newValue); + + /** + * Casts to QDBusObject. + */ + inline operator QDBusObject() + { return QDBusObject(*this); } + + /** + * Casts to const QDBusObject. + */ + inline operator const QDBusObject() const + { return QDBusObject(*this); } + + /** + * Call the given method. + */ + template<typename MethodType> + inline QDBusMessage call(MethodType m) + { + return callWithArgs(m); + } + + /** + * Call the given method. + */ + template<typename MethodType, typename T1> + inline QDBusMessage call(MethodType m, T1 t1) + { + QList<QVariant> args; + args << t1; + return callWithArgs(m, args); + } + + /** + * Call the given method. + */ + template<typename MethodType, typename T1, typename T2> + inline QDBusMessage call(MethodType m, T1 t1, T2 t2) + { + QList<QVariant> args; + args << t1 << t2; + return callWithArgs(m, args); + } + + /** + * Call the given method. + */ + template<typename MethodType, typename T1, typename T2, typename T3> + inline QDBusMessage call(MethodType m, T1 t1, T2 t2, T3 t3) + { + QList<QVariant> args; + args << t1 << t2 << t3; + return callWithArgs(m, args); + } + + /** + * Call the given method. + */ + template<typename MethodType, typename T1, typename T2, typename T3, typename T4> + inline QDBusMessage call(MethodType m, T1 t1, T2 t2, T3 t3, T4 t4) + { + QList<QVariant> args; + args << t1 << t2 << t3 << t4; + return callWithArgs(m, args); + } + + /** + * Call the given method. + */ + template<typename MethodType, typename T1, typename T2, typename T3, typename T4, typename T5> + inline QDBusMessage call(MethodType m, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5) + { + QList<QVariant> args; + args << t1 << t2 << t3 << t4 << t5; + return callWithArgs(m, args); + } + + /** + * Call the given method. + */ + template<typename MethodType, typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6> + inline QDBusMessage call(MethodType m, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6) + { + QList<QVariant> args; + args << t1 << t2 << t3 << t4 << t5 << t6; + return callWithArgs(m, args); + } + + /** + * Call the given method. + */ + template<typename MethodType, typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7> + inline QDBusMessage call(MethodType m, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7) + { + QList<QVariant> args; + args << t1 << t2 << t3 << t4 << t5 << t6 << t7; + return callWithArgs(m, args); + } + + /** + * Call the given method. + */ + template<typename MethodType, typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8> + inline QDBusMessage call(MethodType m, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8) + { + QList<QVariant> args; + args << t1 << t2 << t3 << t4 << t5 << t6 << t7 << t8; + return callWithArgs(m, args); + } + +private: + QDBusInterface(QDBusInterfacePrivate*); + QDBusInterfacePrivate *d; +}; + +#endif diff --git a/qt/qdbusinterface_p.h b/qt/qdbusinterface_p.h new file mode 100644 index 0000000..52fa800 --- /dev/null +++ b/qt/qdbusinterface_p.h @@ -0,0 +1,67 @@ +/* + * + * Copyright (C) 2006 Thiago José Macieira <thiago@kde.org> + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +// +// 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 "qdbusobject.h" +#include "qdbusinterface.h" +#include "qdbusconnection.h" +#include "qdbuserror.h" + +#define ANNOTATION_NO_WAIT "com.trolltech.DBus.NoWaitForReply" + +class QDBusInterfacePrivate +{ +public: + QAtomic ref; + QDBusConnection conn; + QString service; + QString path; + QDBusError lastError; + + //QConstSharedDataPointer<QDBusIntrospection::Interface> data; + const QDBusIntrospection::Interface* data; + + inline bool needsIntrospection() const + { return data->introspection.isNull(); } + + inline void introspect() + { if (needsIntrospection()) QDBusObject(conn, service, path).introspect(); } +}; + + +#endif diff --git a/qt/qdbusintrospection.cpp b/qt/qdbusintrospection.cpp new file mode 100644 index 0000000..e1183b7 --- /dev/null +++ b/qt/qdbusintrospection.cpp @@ -0,0 +1,380 @@ +/* -*- C++ -*- + * + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "qdbusintrospection.h" +#include "qdbusxmlparser_p.h" + +/*! + \class QDBusIntrospection + \brief Information about introspected objects and interfaces on D-Bus. + + 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). +*/ + +/*! + \struct 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). +*/ + +/*! + \var QDBusIntrospection::Argument::type + The argument type. +*/ + +/*! + \var QDBusIntrospection::Argument::name + The argument name. The argument name is optional, so this may be a null QString. +*/ + +/*! + \struct 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. +*/ + +/*! + \var QDBusIntrospection::Method::name + The method's name. +*/ + +/*! + \var QDBusIntrospection::Method::inputArgs + A list of the method's input arguments. +*/ + +/*! + \var QDBusIntrospection::Method::outputArgs + A list of the method's output arguments (i.e., return values). +*/ + +/*! + \var 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. +*/ + +/*! + \struct 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. +*/ + +/*! + \var QDBusIntrospection::Signal::outputArgs + A list of the signal's arguments. +*/ + +/*! + \var 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. +*/ + +/*! + \struct 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. +*/ + +/*! + \var QDBusIntrospection::Property::name + The property's name. +*/ + +/*! + \var QDBusIntrospection::Property::type + The property's type. +*/ + +/*! + \enum QDBusIntrospection::Property::Access + The possible access rights for a property: + - Read + - Write + - ReadWrite +*/ + +/*! + \var QDBusIntrospection::Property::access + The property's access rights. +*/ + +/*! + \var 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. +*/ + +/*! + \struct 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. +*/ + +/*! + \var QDBusIntrospection::Interface::name + The interface's name. +*/ + +/*! + \var 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. +*/ + +/*! + \var 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. +*/ + +/*! + \var 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). +*/ + +/*! + \var 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. +*/ + +/*! + \var QDBusIntrospection::Interface::properties + The properties available in this interface. Property names are unique. +*/ + +/*! + \struct 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. +*/ + +/*! + \var QDBusIntrospection::Object::service + The object's service name. + + \sa parseObject, parseObjectTree +*/ + +/*! + \var QDBusIntrospection::Object::path + The object's path on the service. This is an absolute path. + + \sa parseObject, parseObjectTree +*/ + +/*! + \var 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. +*/ + +/*! + \var QDBusIntrospection::Object::interfaces + The list of interface names in this object. +*/ + +/*! + \var 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 \ref path. +*/ + +/*! + \struct 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. +*/ + +/*! + \var QDBusIntrospection::ObjectTree::interfaceData + A map of interfaces and their names. +*/ + +/*! + \var 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 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. + + \param xml the XML data to be parsed + \returns the parsed interface +*/ +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 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. + + \param xml the XML data to be parsed + \returns the parsed interfaces +*/ +QDBusIntrospection::Interfaces +QDBusIntrospection::parseInterfaces(const QString &xml) +{ + QDBusXmlParser parser(QString(), QString(), xml); + return parser.interfaces(); +} + +/*! + Parses the XML document fragment containing one object. + + 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. + + \param xml the XML data to be parsed + \param service the service where this object is found + \param path the absolute path to this node on the remote service + \returns the parsed object +*/ +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 containing one object node and returns all the information + about the interfaces and sub-objects. + + The Objects map returned will contain the absolute path names in the key. + + \param xml the XML data to be parsed + \param service the service where this object is found + \param path the absolute path to this node on the remote service + \returns the parsed objects and interfaces +*/ +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; +} diff --git a/qt/qdbusintrospection.h b/qt/qdbusintrospection.h new file mode 100644 index 0000000..4e1a874 --- /dev/null +++ b/qt/qdbusintrospection.h @@ -0,0 +1,148 @@ +/* -*- C++ -*- + * + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef QDBUSINTROSPECTION_H +#define QDBUSINTROSPECTION_H + +#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 "qdbustype.h" +#include "qdbusmacros.h" + +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 + { + QDBusType 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; + QDBusType 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 == 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(); +}; + +#endif diff --git a/qt/qdbusmacros.h b/qt/qdbusmacros.h new file mode 100644 index 0000000..e36bfb7 --- /dev/null +++ b/qt/qdbusmacros.h @@ -0,0 +1,36 @@ +/* qdbusmessage.h QDBusMessage object + * + * Copyright (C) 2005 Harald Fernengel <harry@kdevelop.org> + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef QDBUSMACROS_H +#define QDBUSMACROS_H + +#include <QtCore/qglobal.h> + +#ifndef QDBUS_EXPORT +#ifdef QDBUS_MAKEDLL +# define QDBUS_EXPORT Q_DECL_EXPORT +#else +# define QDBUS_EXPORT Q_DECL_IMPORT +#endif +#endif + +#endif diff --git a/qt/qdbusmarshall.cpp b/qt/qdbusmarshall.cpp index 31c3589..19de0d9 100644 --- a/qt/qdbusmarshall.cpp +++ b/qt/qdbusmarshall.cpp @@ -1,6 +1,8 @@ /* qdbusmarshall.cpp * * Copyright (C) 2005 Harald Fernengel <harry@kdevelop.org> + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> * * Licensed under the Academic Free License version 2.1 * @@ -15,21 +17,22 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, write to the Free Software Foundation + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "qdbusmarshall.h" +#include "qdbustype.h" #include "qdbusvariant.h" -#include <QtCore/qdebug.h> -#include <QtCore/qvariant.h> -#include <QtCore/qlist.h> -#include <QtCore/qmap.h> -#include <QtCore/qstringlist.h> -#include <QtCore/qvarlengtharray.h> -#include <QtCore/qvector.h> +#include <qdebug.h> +#include <qvariant.h> +#include <qlist.h> +#include <qmap.h> +#include <qstringlist.h> +#include <qvarlengtharray.h> +#include <qvector.h> #include <dbus/dbus.h> @@ -60,6 +63,10 @@ static QVariant qFetchParameter(DBusMessageIter *it) switch (dbus_message_iter_get_arg_type(it)) { case DBUS_TYPE_BYTE: return qIterGet<unsigned char>(it); + case DBUS_TYPE_INT16: + return qIterGet<dbus_int16_t>(it); + case DBUS_TYPE_UINT16: + return qIterGet<dbus_uint16_t>(it); case DBUS_TYPE_INT32: return qIterGet<dbus_int32_t>(it); case DBUS_TYPE_UINT32: @@ -78,15 +85,26 @@ static QVariant qFetchParameter(DBusMessageIter *it) return QString::fromUtf8(qIterGet<char *>(it)); case DBUS_TYPE_ARRAY: { int arrayType = dbus_message_iter_get_element_type(it); - if (arrayType == DBUS_TYPE_STRING || arrayType == DBUS_TYPE_OBJECT_PATH) { + if (arrayType == DBUS_TYPE_STRING || arrayType == DBUS_TYPE_OBJECT_PATH || + arrayType == DBUS_TYPE_SIGNATURE) { return qFetchStringList(it); + } else if (arrayType == DBUS_TYPE_BYTE) { + DBusMessageIter sub; + dbus_message_iter_recurse(it, &sub); + int len = dbus_message_iter_get_array_len(&sub); + char* data; + dbus_message_iter_get_fixed_array(&sub,&data,&len); + return QByteArray(data,len); } else if (arrayType == DBUS_TYPE_DICT_ENTRY) { // ### support other types of maps? QMap<QString, QVariant> map; DBusMessageIter sub; + dbus_message_iter_recurse(it, &sub); if (!dbus_message_iter_has_next(&sub)) + // empty map return map; + do { DBusMessageIter itemIter; dbus_message_iter_recurse(&sub, &itemIter); @@ -96,43 +114,30 @@ static QVariant qFetchParameter(DBusMessageIter *it) map.insertMulti(key, qFetchParameter(&itemIter)); } while (dbus_message_iter_next(&sub)); return map; - } else { - QList<QVariant> list; - DBusMessageIter sub; - dbus_message_iter_recurse(it, &sub); - if (!dbus_message_iter_has_next(&sub)) - return list; - do { - list.append(qFetchParameter(&sub)); - } while (dbus_message_iter_next(&sub)); - return list; } - break; } + } + // fall through + // common handling for structs and lists + case DBUS_TYPE_STRUCT: { + QList<QVariant> list; + DBusMessageIter sub; + dbus_message_iter_recurse(it, &sub); + if (!dbus_message_iter_has_next(&sub)) + return list; + do { + list.append(qFetchParameter(&sub)); + } while (dbus_message_iter_next(&sub)); + return list; + } case DBUS_TYPE_VARIANT: { QDBusVariant dvariant; DBusMessageIter sub; dbus_message_iter_recurse(it, &sub); - dvariant.signature = QString::fromUtf8(dbus_message_iter_get_signature(&sub)); + dvariant.type = QDBusType(dbus_message_iter_get_signature(&sub)); dvariant.value = qFetchParameter(&sub); return qVariantFromValue(dvariant); } -#if 0 - case DBUS_TYPE_DICT: { - QMap<QString, QVariant> map; - DBusMessageIter sub; - dbus_message - if (dbus_message_iter_init_dict_iterator(it, &dictIt)) { - do { - map[QString::fromUtf8(dbus_message_iter_get_dict_key(&dictIt))] = - qFetchParameter(&dictIt); - } while (dbus_message_iter_next(&dictIt)); - } - return map; - break; } - case DBUS_TYPE_CUSTOM: - return qGetCustomValue(it); - break; -#endif + default: qWarning("Don't know how to handle type %d '%c'", dbus_message_iter_get_arg_type(it), dbus_message_iter_get_arg_type(it)); return QVariant(); @@ -153,148 +158,305 @@ void QDBusMarshall::messageToList(QList<QVariant> &list, DBusMessage *message) } while (dbus_message_iter_next(&it)); } -#define DBUS_APPEND(type,dtype,var) \ -type dtype##v=(var); \ -dbus_message_append_args(msg, dtype, &dtype##v, DBUS_TYPE_INVALID) -#define DBUS_APPEND_LIST(type,dtype,var,size) \ -type dtype##v=(var); \ -dbus_message_append_args(msg, DBUS_TYPE_ARRAY, dtype, &dtype##v, size, DBUS_TYPE_INVALID) - - -static void qAppendToMessage(DBusMessageIter *it, const QString &str) -{ - QByteArray ba = str.toUtf8(); - const char *cdata = ba.constData(); - dbus_message_iter_append_basic(it, DBUS_TYPE_STRING, &cdata); -} - -static QVariant::Type qVariantListType(const QList<QVariant> &list) +// convert the variant to the given type and return true if it worked. +// if the type is not known, guess it from the variant and set. +// return false if conversion failed. +static bool checkType(QVariant &var, QDBusType &type) { - // TODO - catch lists that have a list as first parameter - QVariant::Type tp = list.value(0).type(); - if (tp < QVariant::Int || tp > QVariant::Double) - return QVariant::Invalid; - - for (int i = 1; i < list.count(); ++i) { - const QVariant &var = list.at(i); - if (var.type() != tp - && (var.type() != QVariant::List || qVariantListType(var.toList()) != tp)) - return QVariant::Invalid; + if (!type.isValid()) { + // guess it from the variant + type = QDBusType::guessFromVariant(var); + return true; } - return tp; -} -static const char *qDBusListType(const QList<QVariant> &list) -{ - static const char *DBusArgs[] = { 0, 0, DBUS_TYPE_INT32_AS_STRING, DBUS_TYPE_UINT32_AS_STRING, - DBUS_TYPE_INT64_AS_STRING, DBUS_TYPE_UINT64_AS_STRING, DBUS_TYPE_DOUBLE_AS_STRING }; - - return DBusArgs[qVariantListType(list)]; -} - -static void qListToIterator(DBusMessageIter *it, const QList<QVariant> &list); + // only catch the conversions that won't work + // let QVariant do the hard work + + // QVariant can't convert QDBusVariant: + if (var.userType() == qMetaTypeId<QDBusVariant>()) { + if (type.dbusType() == DBUS_TYPE_VARIANT) + return true; // no change -static void qVariantToIterator(DBusMessageIter *it, const QVariant &var) -{ - static const int Variant2DBus[] = { DBUS_TYPE_INVALID, - DBUS_TYPE_BOOLEAN, DBUS_TYPE_INT32, DBUS_TYPE_UINT32, - DBUS_TYPE_INT64, DBUS_TYPE_UINT64, DBUS_TYPE_DOUBLE }; - - // these really are static asserts - Q_ASSERT(QVariant::Invalid == 0); - Q_ASSERT(QVariant::Int == 2); - Q_ASSERT(QVariant::Double == 6); + // convert manually + QDBusVariant dvariant = qvariant_cast<QDBusVariant>(var); + var = dvariant.value; + return checkType(var, type); + } + + if (type.dbusType() == DBUS_TYPE_VARIANT) { + // variant can handle anything. Let it pass + return true; + } - switch (var.type()) { + switch (var.userType()) { + case QMetaType::Short: + case QMetaType::UShort: + case QMetaType::UChar: case QVariant::Int: case QVariant::UInt: case QVariant::LongLong: case QVariant::ULongLong: case QVariant::Double: - dbus_message_iter_append_basic(it, Variant2DBus[var.type()], - var.constData()); - break; case QVariant::String: - qAppendToMessage(it, var.toString()); - break; + if (type.isBasic()) + // QVariant can handle this on its own + return true; + + // cannot handle this + qWarning("Invalid conversion from %s to '%s'", var.typeName(), + type.dbusSignature().constData()); + var.clear(); + return false; + + case QVariant::ByteArray: + // make sure it's an "ARRAY of BYTE" + if (type.qvariantType() != QVariant::ByteArray) { + qWarning("Invalid conversion from %s to '%s'", var.typeName(), + type.dbusSignature().constData()); + var.clear(); + return false; + } + return true; + + case QVariant::StringList: + // make sure it's "ARRAY of STRING" + if (type.qvariantType() != QVariant::StringList) { + qWarning("Invalid conversion from %s to '%s'", var.typeName(), + type.dbusSignature().constData()); + var.clear(); + return false; + } + return true; + + case QVariant::List: + // could be either struct or array + if (type.dbusType() != DBUS_TYPE_ARRAY && type.dbusType() != DBUS_TYPE_STRUCT) { + qWarning("Invalid conversion from %s to '%s'", var.typeName(), + type.dbusSignature().constData()); + var.clear(); + return false; + } + + return true; + + case QVariant::Map: + if (!type.isMap()) { + qWarning("Invalid conversion from %s to '%s'", var.typeName(), + type.dbusSignature().constData()); + var.clear(); + return false; + } + + return true; + + case QVariant::Invalid: + // create an empty variant + var.convert(type.qvariantType()); + break; + } + + qWarning("Found unknown QVariant type %d (%s) when converting to DBus", (int)var.type(), + var.typeName()); + var.clear(); + return false; +} + +static void qVariantToIteratorInternal(DBusMessageIter *it, const QVariant &var, + const QDBusType &type); + +static void qListToIterator(DBusMessageIter *it, const QList<QVariant> &list, + const QDBusTypeList &list); + +template<typename T> +static void qIterAppend(DBusMessageIter *it, const QDBusType &type, T arg) +{ + dbus_message_iter_append_basic(it, type.dbusType(), &arg); +} + +static void qAppendArrayToMessage(DBusMessageIter *it, const QDBusType &subType, + const QVariant &var) +{ + DBusMessageIter sub; + dbus_message_iter_open_container(it, DBUS_TYPE_ARRAY, subType.dbusSignature(), &sub); + + switch (var.type()) + { case QVariant::StringList: { const QStringList list = var.toStringList(); - DBusMessageIter sub; - dbus_message_iter_open_container(it, DBUS_TYPE_ARRAY, - DBUS_TYPE_STRING_AS_STRING, &sub); - for (int s = 0; s < list.count(); ++s) - qAppendToMessage(&sub, list.at(s)); - dbus_message_iter_close_container(it, &sub); + foreach (QString str, list) + qIterAppend(&sub, subType, str.toUtf8().constData()); break; } - case QVariant::List: { - const QList<QVariant> &list = var.toList(); - const char *listType = qDBusListType(list); - if (!listType) { - qWarning("Don't know how to marshall list."); - break; - } - DBusMessageIter sub; - dbus_message_iter_open_container(it, DBUS_TYPE_ARRAY, listType, &sub); - qListToIterator(&sub, list); - dbus_message_iter_close_container(it, &sub); + + case QVariant::ByteArray: { + const QByteArray array = var.toByteArray(); + const char* cdata = array.constData(); + dbus_message_iter_append_fixed_array(&sub, DBUS_TYPE_BYTE, &cdata, array.length()); break; } + case QVariant::Map: { - // ### TODO - marshall more than qstring/qstring maps - const QMap<QString, QVariant> &map = var.toMap(); - DBusMessageIter sub; - QVarLengthArray<char, 16> sig; - sig.append(DBUS_DICT_ENTRY_BEGIN_CHAR); - sig.append(DBUS_TYPE_STRING); - sig.append(DBUS_TYPE_STRING); - sig.append(DBUS_DICT_ENTRY_END_CHAR); - sig.append('\0'); - qDebug() << QString::fromAscii(sig.constData()); - dbus_message_iter_open_container(it, DBUS_TYPE_ARRAY, sig.constData(), &sub); + const QVariantMap map = var.toMap(); + const QDBusTypeList& subTypes = subType.subTypes(); for (QMap<QString, QVariant>::const_iterator mit = map.constBegin(); mit != map.constEnd(); ++mit) { DBusMessageIter itemIterator; dbus_message_iter_open_container(&sub, DBUS_TYPE_DICT_ENTRY, 0, &itemIterator); - qAppendToMessage(&itemIterator, mit.key()); - qAppendToMessage(&itemIterator, mit.value().toString()); + + // let the string be converted to QVariant + qVariantToIteratorInternal(&itemIterator, mit.key(), subTypes[0]); + qVariantToIteratorInternal(&itemIterator, mit.value(), subTypes[1]); + dbus_message_iter_close_container(&sub, &itemIterator); } - dbus_message_iter_close_container(it, &sub); break; } - case QVariant::UserType: { - if (var.userType() == QMetaTypeId<QDBusVariant>::qt_metatype_id()) { - DBusMessageIter sub; - QDBusVariant dvariant = qvariant_cast<QDBusVariant>(var); - dbus_message_iter_open_container(it, DBUS_TYPE_VARIANT, - dvariant.signature.toUtf8().constData(), &sub); - qVariantToIterator(&sub, dvariant.value); - dbus_message_iter_close_container(it, &sub); - break; - } + + case QVariant::List: { + const QVariantList list = var.toList(); + foreach (QVariant v, list) + qVariantToIteratorInternal(&sub, v, subType); + break; } - // fall through + default: - qWarning("Don't know how to handle type %s", var.typeName()); + qFatal("qAppendArrayToMessage got unknown type!"); break; } + + dbus_message_iter_close_container(it, &sub); } -void qListToIterator(DBusMessageIter *it, const QList<QVariant> &list) +static void qAppendStructToMessage(DBusMessageIter *it, const QDBusTypeList &typeList, + const QVariantList &list) { - if (list.isEmpty()) - return; + DBusMessageIter sub; + dbus_message_iter_open_container(it, DBUS_TYPE_STRUCT, NULL, &sub); + qListToIterator(&sub, list, typeList); + dbus_message_iter_close_container(it, &sub); +} + +static void qAppendVariantToMessage(DBusMessageIter *it, const QDBusType & /* type */, + const QVariant &var) +{ + QVariant v; + QDBusType t; + + if (var.userType() == qMetaTypeId<QDBusVariant>()) { + QDBusVariant dvariant = qvariant_cast<QDBusVariant>(var); + v = dvariant.value; + t = dvariant.type; + } + else { + v = var; + t = QDBusType::guessFromVariant(v); + } + + // now add this variant + DBusMessageIter sub; + dbus_message_iter_open_container(it, DBUS_TYPE_VARIANT, t.dbusSignature(), &sub); + qVariantToIteratorInternal(&sub, v, t); + dbus_message_iter_close_container(it, &sub); +} + +static void qVariantToIterator(DBusMessageIter *it, QVariant var, QDBusType type) +{ + if (var.isNull() && !type.isValid()) + return; // cannot add a null like this + if (!checkType(var, type)) + return; // type checking failed + + qVariantToIteratorInternal(it, var, type); +} + +static void qVariantToIteratorInternal(DBusMessageIter *it, const QVariant &var, + const QDBusType &type) +{ + switch (type.dbusType()) { + case DBUS_TYPE_BYTE: + qIterAppend( it, type, static_cast<unsigned char>(var.toUInt()) ); + break; + case DBUS_TYPE_BOOLEAN: + qIterAppend( it, type, static_cast<dbus_bool_t>(var.toBool()) ); + break; + case DBUS_TYPE_INT16: + qIterAppend( it, type, static_cast<dbus_int16_t>(var.toInt()) ); + break; + case DBUS_TYPE_UINT16: + qIterAppend( it, type, static_cast<dbus_uint16_t>(var.toUInt()) ); + break; + case DBUS_TYPE_INT32: + qIterAppend( it, type, static_cast<dbus_int32_t>(var.toInt()) ); + break; + case DBUS_TYPE_UINT32: + qIterAppend( it, type, static_cast<dbus_uint32_t>(var.toUInt()) ); + break; + case DBUS_TYPE_INT64: + qIterAppend( it, type, static_cast<dbus_int64_t>(var.toLongLong()) ); + break; + case DBUS_TYPE_UINT64: + qIterAppend( it, type, static_cast<dbus_uint64_t>(var.toULongLong()) ); + break; + case DBUS_TYPE_DOUBLE: + qIterAppend( it, type, var.toDouble() ); + break; + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + case DBUS_TYPE_SIGNATURE: + qIterAppend( it, type, var.toString().toUtf8().constData() ); + break; + + // compound types: + case DBUS_TYPE_ARRAY: + // could be many things + qAppendArrayToMessage( it, type.arrayElement(), var ); + break; + + case DBUS_TYPE_VARIANT: + qAppendVariantToMessage( it, type, var ); + break; + case DBUS_TYPE_STRUCT: + qAppendStructToMessage( it, type.subTypes(), var.toList() ); + break; + + case DBUS_TYPE_DICT_ENTRY: + qFatal("qVariantToIterator got a DICT_ENTRY!"); + break; + + default: + qWarning("Found unknown DBus type '%s'", type.dbusSignature().constData()); + break; + } +} + +void qListToIterator(DBusMessageIter *it, const QList<QVariant> &list) +{ for (int i = 0; i < list.count(); ++i) - qVariantToIterator(it, list.at(i)); + qVariantToIterator(it, list.at(i), QDBusType()); } -void QDBusMarshall::listToMessage(const QList<QVariant> &list, DBusMessage *msg) +void qListToIterator(DBusMessageIter *it, const QList<QVariant> &list, const QDBusTypeList &types) +{ + int min = qMin(list.count(), types.count()); + for (int i = 0; i < min; ++i) + qVariantToIterator(it, list.at(i), types.at(i)); + + for (int i = min; i < types.count(); ++i) + // we're missing a few arguments, so add default parameters + qVariantToIterator(it, QVariant(), types.at(i)); +} + +void QDBusMarshall::listToMessage(const QList<QVariant> &list, DBusMessage *msg, + const QString &signature) { Q_ASSERT(msg); DBusMessageIter it; dbus_message_iter_init_append(msg, &it); - qListToIterator(&it, list); + + if (signature.isEmpty()) + qListToIterator(&it, list); + else + qListToIterator(&it, list, QDBusTypeList(signature.toUtf8())); } diff --git a/qt/qdbusmarshall.h b/qt/qdbusmarshall.h index 4fc1a1a..6ec8cae 100644 --- a/qt/qdbusmarshall.h +++ b/qt/qdbusmarshall.h @@ -1,6 +1,8 @@ /* qdbusmarshall.h QDBusMarshall object * * Copyright (C) 2005 Harald Fernengel <harry@kdevelop.org> + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> * * Licensed under the Academic Free License version 2.1 * @@ -15,8 +17,8 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, write to the Free Software Foundation + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ @@ -25,14 +27,15 @@ struct DBusMessage; +template <typename T> class QList; class QVariant; -template <typename T> -class QList; +class QString; class QDBusMarshall { public: - static void listToMessage(const QList<QVariant> &list, DBusMessage *message); + static void listToMessage(const QList<QVariant> &list, DBusMessage *message, + const QString& signature); static void messageToList(QList<QVariant> &list, DBusMessage *message); }; diff --git a/qt/qdbusmessage.cpp b/qt/qdbusmessage.cpp index a77c22f..5c604c1 100644 --- a/qt/qdbusmessage.cpp +++ b/qt/qdbusmessage.cpp @@ -1,6 +1,8 @@ /* qdbusmessage.cpp * * Copyright (C) 2005 Harald Fernengel <harry@kdevelop.org> + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> * * Licensed under the Academic Free License version 2.1 * @@ -15,19 +17,20 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, write to the Free Software Foundation + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "qdbusmessage.h" -#include <QtCore/qdebug.h> -#include <QtCore/qstringlist.h> +#include <qdebug.h> +#include <qstringlist.h> #include <dbus/dbus.h> #include "qdbusmarshall.h" +#include "qdbuserror.h" #include "qdbusmessage_p.h" QDBusMessagePrivate::QDBusMessagePrivate(QDBusMessage *qq) @@ -44,8 +47,31 @@ QDBusMessagePrivate::~QDBusMessagePrivate() } /////////////// +/*! + \class QDBusMessage + \brief Represents one message sent or received over the DBus bus. + + This object can represent any of four different types of messages possible on the bus + (see MessageType) + - Method calls + - Method return values + - Signal emissions + - Error codes + Objects of this type are created with the four static functions signal, methodCall, + methodReply and error. +*/ +/*! + Constructs a new DBus message 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. + + \param path the path of the object that is emitting the signal + \param interface the interface that is emitting the signal + \param name the name of the signal (a.k.a. method name) + \returns a QDBusMessage object that can be sent with with QDBusConnection::send +*/ QDBusMessage QDBusMessage::signal(const QString &path, const QString &interface, const QString &name) { @@ -58,19 +84,58 @@ QDBusMessage QDBusMessage::signal(const QString &path, const QString &interface, return message; } +/*! + Constructs a new DBus message representing a method call. A method call always informs + its destination address (service, path, interface and 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 service parameter is + optional. + + Optionally, a signature parameter can be passed, indicating the type of the parameters to + be marshalled over the bus. If there are more arguments thanentries in the signature, the + tailing arguments will be silently dropped and not sent. If there are less arguments, + default values will be inserted (default values are those created by QVariant::convert + when a variant of type QVariant::Invalid is converted to the type). + + The QDBusObject and QDBusInterface classes provide a simpler abstraction to synchronous + method calling. + + \param service the remote service to be called (can be a well-known name, a bus + address or null) + \param path the path of the object on the remote service to be called + \param interface the remote interface that is wanted (can be null) + \param method the remote method to be called (a.k.a., name) + \param sig the DBus signature (set to null to discard processing and guess the + method signature from the arguments; empty means no arguments) + \returns a QDBusMessage object that can be sent with QDBusConnection::send, + QDBusConnection::sendWithReply, or QDBusConnection::sendWithReplyAsync +*/ QDBusMessage QDBusMessage::methodCall(const QString &service, const QString &path, - const QString &interface, const QString &method) + const QString &interface, const QString &method, + const QString &sig) { QDBusMessage message; message.d->type = DBUS_MESSAGE_TYPE_METHOD_CALL; message.d->service = service; message.d->path = path; message.d->interface = interface; - message.d->method = method; + message.d->name = method; + message.d->signature = sig; return message; } +/*! + Constructs a new DBus message representing the return values from a called method. + + \param other the method call DBus message that this is a reply to + \returns a QDBusMessage object that can be sent with QDBusConnection::send +*/ QDBusMessage QDBusMessage::methodReply(const QDBusMessage &other) { Q_ASSERT(other.d->msg); @@ -82,11 +147,63 @@ QDBusMessage QDBusMessage::methodReply(const QDBusMessage &other) return message; } +/*! + Constructs a DBus message representing an error condition. + + \param other the QDBusMessage object that generated this error + \param name the DBus error name (error names must follow the same convention that + interface names do) + \param msg the error message + \return a QDBusMessage object that can be sent with QDBusMessage::send +*/ +QDBusMessage QDBusMessage::error(const QDBusMessage &other, const QString &name, + const QString &msg) +{ + Q_ASSERT(other.d->msg); + + QDBusMessage message; + message.d->type = DBUS_MESSAGE_TYPE_ERROR; + message.d->name = name; + message.d->message = msg; + message.d->reply = dbus_message_ref(other.d->msg); + + return message; +} + +/*! + \overload + Constructs a DBus message representing an error condition. + + \param other the QDBusMessage object that generated this error + \param error the QDBusError object representing this error + \return a QDBusMessage object that can be sent with QDBusMessage::send +*/ +QDBusMessage QDBusMessage::error(const QDBusMessage &other, const QDBusError &error) +{ + Q_ASSERT(other.d->msg); + + QDBusMessage message; + message.d->type = DBUS_MESSAGE_TYPE_ERROR; + message.d->name = error.name(); + message.d->message = error.message(); + message.d->reply = dbus_message_ref(other.d->msg); + + return message; +} + +/*! + Constructs an empty, invalid QDBusMessage object. + + \sa methodCall, methodReply, signal, error +*/ QDBusMessage::QDBusMessage() { d = new QDBusMessagePrivate(this); } +/*! + Constructs a copy of the other object. +*/ QDBusMessage::QDBusMessage(const QDBusMessage &other) : QList<QVariant>(other) { @@ -94,12 +211,18 @@ QDBusMessage::QDBusMessage(const QDBusMessage &other) d->ref.ref(); } +/*! + Disposes of the object and frees any resources that were being held. +*/ QDBusMessage::~QDBusMessage() { if (!d->ref.deref()) delete d; } +/*! + Copies the contents of the other object. +*/ QDBusMessage &QDBusMessage::operator=(const QDBusMessage &other) { QList<QVariant>::operator=(other); @@ -107,14 +230,20 @@ QDBusMessage &QDBusMessage::operator=(const QDBusMessage &other) return *this; } +/*! + \internal + Constructs a DBusMessage object from this object. The returned value must be de-referenced + with dbus_message_unref. +*/ DBusMessage *QDBusMessage::toDBusMessage() const { DBusMessage *msg = 0; + switch (d->type) { case DBUS_MESSAGE_TYPE_METHOD_CALL: msg = dbus_message_new_method_call(d->service.toUtf8().constData(), d->path.toUtf8().constData(), d->interface.toUtf8().constData(), - d->method.toUtf8().constData()); + d->name.toUtf8().constData()); break; case DBUS_MESSAGE_TYPE_SIGNAL: msg = dbus_message_new_signal(d->path.toUtf8().constData(), @@ -123,14 +252,22 @@ DBusMessage *QDBusMessage::toDBusMessage() const case DBUS_MESSAGE_TYPE_METHOD_RETURN: msg = dbus_message_new_method_return(d->reply); break; + case DBUS_MESSAGE_TYPE_ERROR: + msg = dbus_message_new_error(d->reply, d->name.toUtf8().constData(), + d->message.toUtf8().constData()); + break; } if (!msg) return 0; - QDBusMarshall::listToMessage(*this, msg); + QDBusMarshall::listToMessage(*this, msg, d->signature); return msg; } +/*! + \internal + Constructs a QDBusMessage by parsing the given DBusMessage object. +*/ QDBusMessage QDBusMessage::fromDBusMessage(DBusMessage *dmsg) { QDBusMessage message; @@ -140,40 +277,94 @@ QDBusMessage QDBusMessage::fromDBusMessage(DBusMessage *dmsg) message.d->type = dbus_message_get_type(dmsg); message.d->path = QString::fromUtf8(dbus_message_get_path(dmsg)); message.d->interface = QString::fromUtf8(dbus_message_get_interface(dmsg)); - message.d->name = QString::fromUtf8(dbus_message_get_member(dmsg)); - message.d->sender = QString::fromUtf8(dbus_message_get_sender(dmsg)); + message.d->name = message.d->type == DBUS_MESSAGE_TYPE_ERROR ? + QString::fromUtf8(dbus_message_get_error_name(dmsg)) : + QString::fromUtf8(dbus_message_get_member(dmsg)); + message.d->service = QString::fromUtf8(dbus_message_get_sender(dmsg)); + message.d->signature = QString::fromUtf8(dbus_message_get_signature(dmsg)); message.d->msg = dbus_message_ref(dmsg); QDBusMarshall::messageToList(message, dmsg); + return message; +} +/*! + Creates a QDBusMessage that represents the same error as the QDBusError object. +*/ +QDBusMessage QDBusMessage::fromError(const QDBusError &error) +{ + QDBusMessage message; + message.d->type = DBUS_MESSAGE_TYPE_ERROR; + message.d->name = error.name(); + message << error.message(); return message; } +/*! + 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->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->interface; } +/*! + Returns the name of the signal that was emitted or the name of the error that was + received. + \sa member +*/ QString QDBusMessage::name() const { return d->name; } -QString QDBusMessage::sender() const +/*! + \fn QDBusMessage::member + Returns the name of the method being called. +*/ + +/*! + \fn QDBusMessage::method + \overload + Returns the name of the method being called. +*/ + +/*! + Returns the name of the service or the bus address of the remote method call. +*/ +QString QDBusMessage::service() const { - return d->sender; + return d->service; } +/*! + \fn QDBusMessage::sender + Returns the unique name of the remote sender. +*/ + +/*! + Returns the timeout (in milliseconds) for this message to be processed. +*/ int QDBusMessage::timeout() const { return d->timeout; } +/*! + Sets the timeout for this message to be processed. + + \param ms the time, in milliseconds +*/ void QDBusMessage::setTimeout(int ms) { d->timeout = ms; @@ -205,6 +396,18 @@ int QDBusMessage::replySerialNumber() const return dbus_message_get_reply_serial(d->msg); } +/*! + Returns the signature of the signal that was received or for the output arguments + of a method call. +*/ +QString QDBusMessage::signature() const +{ + return d->signature; +} + +/*! + Returns the message type. +*/ QDBusMessage::MessageType QDBusMessage::type() const { switch (d->type) { @@ -222,11 +425,32 @@ QDBusMessage::MessageType QDBusMessage::type() const } #ifndef QT_NO_DEBUG +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"; + } +} + QDebug operator<<(QDebug dbg, const QDBusMessage &msg) { - dbg.nospace() << "QDBusMessage(" << msg.path() << ", " << msg.interface() << ", " - << msg.name() << ", " << msg.sender() << ", " - << static_cast<QList<QVariant> >(msg) << ")"; + dbg.nospace() << "QDBusMessage(type=" << msg.type() + << ", service=" << msg.service() + << ", path=" << msg.path() + << ", interface=" << msg.interface() + << ", name=" << msg.name() + << ", signature=" << msg.signature() + << ", contents=" << static_cast<QList<QVariant> >(msg) << ")"; return dbg.space(); } #endif diff --git a/qt/qdbusmessage.h b/qt/qdbusmessage.h index 03a475e..6c48044 100644 --- a/qt/qdbusmessage.h +++ b/qt/qdbusmessage.h @@ -1,6 +1,8 @@ /* qdbusmessage.h QDBusMessage object * * Copyright (C) 2005 Harald Fernengel <harry@kdevelop.org> + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> * * Licensed under the Academic Free License version 2.1 * @@ -15,22 +17,22 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, write to the Free Software Foundation + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #ifndef QDBUSMESSAGE_H #define QDBUSMESSAGE_H -#include "dbus/qdbus.h" - +#include "qdbusmacros.h" #include <QtCore/qlist.h> #include <QtCore/qvariant.h> #include <limits.h> class QDBusMessagePrivate; +class QDBusError; struct DBusMessage; class QDBUS_EXPORT QDBusMessage: public QList<QVariant> @@ -49,23 +51,32 @@ public: static QDBusMessage signal(const QString &path, const QString &interface, const QString &name); - static QDBusMessage methodCall(const QString &service, const QString &path, - const QString &interface, const QString &method); + static QDBusMessage methodCall(const QString &destination, const QString &path, + const QString &interface, const QString &method, + const QString &signature = QString()); static QDBusMessage methodReply(const QDBusMessage &other); + static QDBusMessage error(const QDBusMessage &other, const QString &name, + const QString &message = QString()); + static QDBusMessage error(const QDBusMessage &other, const QDBusError &error); QString path() const; QString interface() const; - QString name() const; //rename to member? - QString sender() const; //rename to service? + QString name() const; + inline QString member() const { return name(); } + inline QString method() const { return name(); } + QString service() const; + inline QString sender() const { return service(); } MessageType type() const; int timeout() const; void setTimeout(int ms); + QString signature() const; //protected: DBusMessage *toDBusMessage() const; static QDBusMessage fromDBusMessage(DBusMessage *dmsg); + static QDBusMessage fromError(const QDBusError& error); int serialNumber() const; int replySerialNumber() const; diff --git a/qt/qdbusmessage_p.h b/qt/qdbusmessage_p.h index 9655c9d..9c48b08 100644 --- a/qt/qdbusmessage_p.h +++ b/qt/qdbusmessage_p.h @@ -1,6 +1,8 @@ /* qdbusmessage.h QDBusMessage private object * * Copyright (C) 2005 Harald Fernengel <harry@kdevelop.org> + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> * * Licensed under the Academic Free License version 2.1 * @@ -15,17 +17,16 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, write to the Free Software Foundation + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #ifndef QDBUSMESSAGE_P_H #define QDBUSMESSAGE_P_H -#include <QtCore/qatomic.h> -#include <QtCore/qstring.h> - +#include <qatomic.h> +#include <qstring.h> struct DBusMessage; class QDBusMessagePrivate @@ -34,7 +35,7 @@ public: QDBusMessagePrivate(QDBusMessage *qq); ~QDBusMessagePrivate(); - QString path, interface, name, service, method, sender; + QString service, path, interface, name, message, signature; DBusMessage *msg; DBusMessage *reply; QDBusMessage *q; diff --git a/qt/qdbusobject.cpp b/qt/qdbusobject.cpp new file mode 100644 index 0000000..139154e --- /dev/null +++ b/qt/qdbusobject.cpp @@ -0,0 +1,173 @@ +/* -*- C++ -*- + * + * Copyright (C) 2005 Thiago Macieira <thiago@kde.org> + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "qdbusmessage.h" +#include "qdbusconnection.h" +#include "qdbusobject.h" +#include "qdbusinterface.h" +#include "qdbusstandardinterfaces.h" +#include "qdbuserror.h" + +#include "qdbusxmlparser_p.h" +#include "qdbusobject_p.h" +#include "qdbusutil.h" + +QDBusObject::QDBusObject(QDBusObjectPrivate* p, const QDBusConnection& conn) + :d(p), m_conn(conn) +{ +} + +QDBusObject::QDBusObject(const QDBusConnection& conn, const QString& service, const QString& path) + : m_conn(conn) +{ + *this = m_conn.findObject(service, path); +} + +QDBusObject::QDBusObject(const QDBusInterface& iface) + : m_conn(iface.connection()) +{ + *this = m_conn.findObject(iface.service(), iface.path()); +} + +QDBusObject::QDBusObject(const QDBusObject& other) + : d(other.d), m_conn(other.m_conn) +{ +} + +QDBusObject::~QDBusObject() +{ +} + +QDBusObject& QDBusObject::operator=(const QDBusObject& other) +{ +#if 0 + if (other.d) + other.d->ref.ref(); + + QDBusObjectPrivate* old = qAtomicSetPtr(&d, other.d); + if (old && !old->ref.deref()) + m_conn.d->disposeOf(d); +#endif + d = other.d; + + return *this; +} + +QDBusConnection QDBusObject::connection() const +{ + return m_conn; +} + +QString QDBusObject::service() const +{ + return d ? d->data->service : QString(); +} + +QString QDBusObject::path() const +{ + return d ? d->data->path : QString(); +} + +QString QDBusObject::introspect() const +{ + if (!d) + // not connected + return QString(); + + if (d->data->introspection.isNull()) { + // Try to introspect + QDBusIntrospectableInterface iface = *this; + QString xml = iface.introspect(); + + if (!m_conn.lastError().isValid()) { + // this will change the contents of d->data + QDBusXmlParser::parse(d, xml); + } + } + return d->data->introspection; +} + +QSharedDataPointer<QDBusIntrospection::Object> QDBusObject::introspectionData() const +{ + QSharedDataPointer<QDBusIntrospection::Object> retval; + if (d) + retval = const_cast<QDBusIntrospection::Object*>(d->data); + return retval; +} + +QStringList QDBusObject::interfaces() const +{ + introspect(); + return d ? d->data->interfaces : QStringList(); +} + +QMap<QString, QDBusObject> QDBusObject::children() const +{ + QMap<QString, QDBusObject> retval; +#if 0 + if (!d) + return retval; + + QString prefix = d->path; + if (!prefix.endsWith('/')) + prefix.append('/'); + foreach (QString sub, d->childObjects) + retval.insert(sub, QDBusObject( m_conn.d->findObject(d->path, prefix + sub), m_conn )); + + return retval; +#endif + qFatal("fixme!"); + return retval; +} + +bool QDBusObject::isValid() const +{ + return d && m_conn.isConnected() && QDBusUtil::isValidBusName(d->data->service) && + QDBusUtil::isValidObjectPath(d->data->path); +} + +#if 0 // we don't have a way of determining if an object exists or not +bool QDBusObject::exists() const +{ + if (!isValid()) + return false; + + // call a non-existant interface/method + QDBusMessage msg = QDBusMessage::methodCall(d->service, d->path, + "org.freedesktop.DBus.NonExistant", "NonExistant"); + QDBusMessage reply = m_conn.sendWithReply(msg); + // ignore the reply + + QDBusError err = m_conn.lastError(); + if (!err.isValid()) { + qWarning("D-Bus call to %s:%s on a supposedly non-existant interface worked!", + qPrintable(d->service), qPrintable(d->path)); + return true; + } + + if (err.name == DBUS_ERROR_SERVICE_UNKNOWN || + err.name == DBUS_ERROR_BAD_ADDRESS + return !m_conn.lastError().isValid(); +} +#endif diff --git a/qt/qdbusobject.h b/qt/qdbusobject.h new file mode 100644 index 0000000..ffb41ad --- /dev/null +++ b/qt/qdbusobject.h @@ -0,0 +1,161 @@ +/* -*- C++ -*- + * + * Copyright (C) 2005 Thiago Macieira <thiago@kde.org> + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef QDBUSOBJECT_H +#define QDBUSOBJECT_H + +#include <QtCore/qstring.h> +#include <QtCore/qvariant.h> +#include <QtCore/qlist.h> +#include <QtCore/qshareddata.h> + +#include "qdbusconnection.h" +#include "qdbusintrospection.h" + +class QDBusInterface; +class QDBusObject; + +template<class Interface> +inline Interface qdbus_cast(QDBusObject& obj, Interface * = 0); + +template<class Interface> +inline const Interface qdbus_cast(const QDBusObject& obj, Interface * = 0); + +class QDBusObjectPrivate; +/** + * QDBusObject + * Base object for DBUS objects imported and exported. + */ +class QDBUS_EXPORT QDBusObject +{ + friend class QDBusConnection; +public: + // public constructors + /** + * Construct a QDBusObject referencing the remote object given. + */ + QDBusObject(const QDBusConnection& conn, const QString& service, const QString& path); + + /** + * Copy constructor. + */ + QDBusObject(const QDBusObject& other); + + /** + * Construct from an interface. + */ + QDBusObject(const QDBusInterface& iface); + + // public destructors + /** + * Destructor. + */ + ~QDBusObject(); + +public: + // public functions + + /** + * Assignment operator + */ + QDBusObject& operator=(const QDBusObject&); + + /** + * Returns the connection this object is bound to. + */ + QDBusConnection connection() const; + + /** + * Returns the service this object is associated to. + */ + QString service() const; + + /** + * Returns the path on the service this object is on. + */ + QString path() const; + + /** + * Returns the introspection XML data of this object node. + */ + QString introspect() const; + + /** + * Returns the introspection data for this object node. + */ + QSharedDataPointer<QDBusIntrospection::Object> introspectionData() const; + + /** + * Returns all the interfaces in this object. + */ + QStringList interfaces() const; + + /** + * Returns all the children object in this object. + */ + QMap<QString, QDBusObject> children() const; + + /** + * Returns true if the object being referenced exists. + */ + //bool exists() const; + + /** + * Returns true if we're referencing a valid object. + */ + bool isValid() const; + + /** + * Cast this object to an interface, if possible. + */ + template<typename Interface> + inline operator Interface() + { return qdbus_cast<Interface>(*this); } + + /** + * Cast this object to an interface, if possible. + */ + template<typename Interface> + inline operator const Interface() const + { return qdbus_cast<Interface>(*this); } + +private: + QDBusObject(QDBusObjectPrivate*, const QDBusConnection& conn); + QSharedDataPointer<QDBusObjectPrivate> d; + QDBusConnection m_conn; +}; + +template<class Interface> +inline Interface qdbus_cast(QDBusObject& obj, Interface *) +{ + return Interface(obj); +} + +template<class Interface> +inline const Interface qdbus_cast(const QDBusObject& obj, Interface *) +{ + return Interface(obj); +} + +#endif // QDBUSOBJECT_H diff --git a/qt/qdbusobject_p.h b/qt/qdbusobject_p.h new file mode 100644 index 0000000..181fceb --- /dev/null +++ b/qt/qdbusobject_p.h @@ -0,0 +1,69 @@ +/* + * + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +// +// 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 QDBUSOBJECTPRIVATE_H +#define QDBUSOBJECTPRIVATE_H + +#include "QtCore/qatomic.h" +#include "QtCore/qstringlist.h" +#include "qdbusobject.h" +#include "qdbusinterface.h" +#include "qdbusconnection_p.h" + +class QDBusObject; +class QDBusInterface; +class QDBusXmlParser; + +class QDBusObjectPrivate: public QSharedData +{ +public: + inline QDBusObjectPrivate(QDBusConnectionPrivate* ptr, const QString &service, + const QString &path) + : parent(ptr), + data( ) + { + QDBusIntrospection::Object * d = ptr->findObject(service, path); + d->ref.ref(); + data = d; + } + + inline ~QDBusObjectPrivate() + { parent->disposeOf(this); } + + QDBusConnectionPrivate* parent; + const QDBusIntrospection::Object* data; +}; + +#endif diff --git a/qt/qdbusserver.cpp b/qt/qdbusserver.cpp index 08fea26..b3b9835 100644 --- a/qt/qdbusserver.cpp +++ b/qt/qdbusserver.cpp @@ -1,6 +1,8 @@ /* qdbusserver.cpp * * Copyright (C) 2005 Harald Fernengel <harry@kdevelop.org> + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> * * Licensed under the Academic Free License version 2.1 * @@ -15,8 +17,8 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, write to the Free Software Foundation + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ diff --git a/qt/qdbusserver.h b/qt/qdbusserver.h index bc191a2..5560786 100644 --- a/qt/qdbusserver.h +++ b/qt/qdbusserver.h @@ -23,7 +23,7 @@ #ifndef QDBUSSERVER_H #define QDBUSSERVER_H -#include "dbus/qdbus.h" +#include "qdbusmacros.h" #include <QtCore/qobject.h> #include <QtCore/qstring.h> diff --git a/qt/qdbusstandardinterfaces.cpp b/qt/qdbusstandardinterfaces.cpp new file mode 100644 index 0000000..f8ea7e1 --- /dev/null +++ b/qt/qdbusstandardinterfaces.cpp @@ -0,0 +1,114 @@ +/* -*- C++ -*- + * + * Copyright (C) 2005 Thiago Macieira <thiago@kde.org> + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "qdbusstandardinterfaces.h" + +QDBusPeerInterface::~QDBusPeerInterface() +{ +} + +QDBusIntrospectableInterface::~QDBusIntrospectableInterface() +{ +} + +QDBusPropertiesInterface::~QDBusPropertiesInterface() +{ +} + +QDBusBusInterface::~QDBusBusInterface() +{ +} + +const char* QDBusBusInterface::staticIntrospectionData() +{ + // FIXME! + // This should be auto-generated! + + return + "<interface name=\"org.freedesktop.DBus\">" + "<method name=\"RequestName\">" + "<arg direction=\"in\" type=\"s\"/>" + "<arg direction=\"in\" type=\"u\"/>" + "<arg direction=\"out\" type=\"u\"/>" + "</method>" + "<method name=\"ReleaseName\">" + "<arg direction=\"in\" type=\"s\"/>" + "<arg direction=\"out\" type=\"u\"/>" + "</method>" + "<method name=\"StartServiceByName\">" + "<arg direction=\"in\" type=\"s\"/>" + "<arg direction=\"in\" type=\"u\"/>" + "<arg direction=\"out\" type=\"u\"/>" + "</method>" + "<method name=\"Hello\">" + "<arg direction=\"out\" type=\"s\"/>" + "</method>" + "<method name=\"NameHasOwner\">" + "<arg direction=\"in\" type=\"s\"/>" + "<arg direction=\"out\" type=\"b\"/>" + "</method>" + "<method name=\"ListNames\">" + "<arg direction=\"out\" type=\"as\"/>" + "</method>" + "<method name=\"AddMatch\">" + "<arg direction=\"in\" type=\"s\"/>" + "</method>" + "<method name=\"RemoveMatch\">" + "<arg direction=\"in\" type=\"s\"/>" + "</method>" + "<method name=\"GetNameOwner\">" + "<arg direction=\"in\" type=\"s\"/>" + "<arg direction=\"out\" type=\"s\"/>" + "</method>" + "<method name=\"ListQueuedOwners\">" + "<arg direction=\"in\" type=\"s\"/>" + "<arg direction=\"out\" type=\"as\"/>" + "</method>" + "<method name=\"GetConnectionUnixUser\">" + "<arg direction=\"in\" type=\"s\"/>" + "<arg direction=\"out\" type=\"u\"/>" + "</method>" + "<method name=\"GetConnectionUnixProcessID\">" + "<arg direction=\"in\" type=\"s\"/>" + "<arg direction=\"out\" type=\"u\"/>" + "</method>" + "<method name=\"GetConnectionSELinuxSecurityContext\">" + "<arg direction=\"in\" type=\"s\"/>" + "<arg direction=\"out\" type=\"ay\"/>" + "</method>" + "<method name=\"ReloadConfig\">" + "</method>" + "<signal name=\"NameOwnerChanged\">" + "<arg type=\"s\"/>" + "<arg type=\"s\"/>" + "<arg type=\"s\"/>" + "</signal>" + "<signal name=\"NameLost\">" + "<arg type=\"s\"/>" + "</signal>" + "<signal name=\"NameAcquired\">" + "<arg type=\"s\"/>" + "</signal>" + "</interface>"; +} diff --git a/qt/qdbusstandardinterfaces.h b/qt/qdbusstandardinterfaces.h new file mode 100644 index 0000000..af2c8dd --- /dev/null +++ b/qt/qdbusstandardinterfaces.h @@ -0,0 +1,218 @@ +/* -*- C++ -*- + * + * Copyright (C) 2005 Thiago Macieira <thiago@kde.org> + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef QDBUS_STANDARD_INTERFACES_H +#define QDBUS_STANDARD_INTERFACES_H + +#include "qdbusinterface.h" +#include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> +#include <dbus/dbus.h> + +class QDBusConnection; + +class QDBUS_EXPORT QDBusPeerInterface: public QDBusInterface +{ +public: + static inline const char* staticInterfaceName() + { return DBUS_INTERFACE_PEER; } + + static inline const char* staticIntrospectionData() + { + return + "<interface name=\"org.freedesktop.DBus.Peer\">" + "<method name=\"Ping\" />" + "</interface>"; + } + +public: + explicit QDBusPeerInterface(const QDBusObject& obj) + : QDBusInterface(obj, staticInterfaceName()) + { } + + QDBusPeerInterface(QDBusConnection& conn, const QString& service, const QString& path) + : QDBusInterface(conn, service, path, staticInterfaceName()) + { } + + ~QDBusPeerInterface(); + + inline virtual QString introspectionData() const + { return staticIntrospectionData(); } + + inline void ping() + { call(QLatin1String("Ping")); } +}; + +class QDBUS_EXPORT QDBusIntrospectableInterface: public QDBusInterface +{ +public: + static inline const char* staticInterfaceName() + { return DBUS_INTERFACE_INTROSPECTABLE; } + + static inline const char* staticIntrospectionData() + { + return + "<interface name=\"org.freedesktop.DBus.Introspectable\">" + "<method name=\"Introspect\">" + "<arg name=\"xml_data\" type=\"s\" direction=\"out\" />" + "</method>" + "</interface>"; + } +public: + explicit QDBusIntrospectableInterface(const QDBusObject& obj) + : QDBusInterface(obj, staticInterfaceName()) + { } + + QDBusIntrospectableInterface(QDBusConnection& conn, const QString& service, const QString& path) + : QDBusInterface(conn, service, path, staticInterfaceName()) + { } + + ~QDBusIntrospectableInterface(); + + inline virtual QString introspectionData() const + { return staticIntrospectionData(); } + + inline QString introspect() + { return call(QLatin1String("Introspect")).at(0).toString(); } +}; + +class QDBUS_EXPORT QDBusPropertiesInterface: public QDBusInterface +{ +public: + static inline const char* staticInterfaceName() + { return DBUS_INTERFACE_PROPERTIES; } + + static inline const char* staticIntrospectionData() + { + return + "<interface name=\"org.freedesktop.DBus.Properties\">" + "<method name=\"Get\">" + "<arg name=\"interface_name\" type=\"s\" direction=\"in\"/>" + "<arg name=\"property_name\" type=\"s\" direction=\"in\"/>" + "<arg name=\"value\" type=\"v\" direction=\"out\"/>" + "</method>" + "<method name=\"Set\">" + "<arg name=\"interface_name\" type=\"s\" direction=\"in\"/>" + "<arg name=\"property_name\" type=\"s\" direction=\"in\"/>" + "<arg name=\"value\" type=\"v\" direction=\"in\"/>" + "</method>"; + } +public: + explicit QDBusPropertiesInterface(const QDBusObject& obj) + : QDBusInterface(obj, staticInterfaceName()) + { } + + QDBusPropertiesInterface(QDBusConnection& conn, const QString& service, const QString& path) + : QDBusInterface(conn, service, path, staticInterfaceName()) + { } + + ~QDBusPropertiesInterface(); + + inline virtual QString introspectionData() const + { return staticIntrospectionData(); } + + inline void set(const QString& interfaceName, const QString& propertyName, QVariant value) + { call(QLatin1String("Set.ssv"), interfaceName, propertyName, value); } + + inline QVariant get(const QString& interfaceName, const QString& propertyName) + { return call(QLatin1String("Get.ss"), interfaceName, propertyName).at(0); } +}; + +class QDBUS_EXPORT QDBusBusInterface: public QDBusInterface +{ +public: + static inline const char* staticInterfaceName() + { return DBUS_INTERFACE_DBUS; } + + static const char* staticIntrospectionData(); + +public: + explicit QDBusBusInterface(const QDBusObject& obj) + : QDBusInterface(obj, staticInterfaceName()) + { } + + QDBusBusInterface(QDBusConnection& conn, const QString& service, const QString& path) + : QDBusInterface(conn, service, path, staticInterfaceName()) + { } + + ~QDBusBusInterface(); + + inline virtual QString introspectionData() const + { return staticIntrospectionData(); } + + inline unsigned requestName(const QString& name, unsigned flags) + { return call(QLatin1String("RequestName.su"), name, flags).at(0).toUInt(); } + + inline unsigned releaseName(const QString& name) + { return call(QLatin1String("ReleaseName.s"), name).at(0).toUInt(); } + + inline unsigned startServiceByName(const QString& name, unsigned flags) + { return call(QLatin1String("StartServiceByName.su"), name, flags).at(0).toUInt(); } + + inline QString Hello() + { return call(QLatin1String("Hello")).at(0).toString(); } + + inline bool nameHasOwner(const QString& name) + { return call(QLatin1String("NameHasOwner.s"), name).at(0).toBool(); } + + inline QStringList listNames() + { return call(QLatin1String("ListNames")).at(0).toStringList(); } + + inline void addMatch(const QString& rule) + { call(QLatin1String("AddMatch"), rule); } + + inline void removeMatch(const QString& rule) + { call(QLatin1String("RemoveMatch"), rule); } + + inline QString getNameOwner(const QString& name) + { return call(QLatin1String("GetNameOwner.s"), name).at(0).toString(); } + + inline QStringList listQueuedOwners(const QString& name) + { return call(QLatin1String("ListQueuedOwners.s"), name).at(0).toStringList(); } + + inline quint32 getConnectionUnixUser(const QString& connectionName) + { return call(QLatin1String("GetConnectionUnixUser.s"), connectionName).at(0).toUInt(); } + + inline quint32 getConnectionUnixProcessID(const QString& connectionName) + { return call(QLatin1String("GetConnectionUnixProcessID.s"), connectionName).at(0).toUInt(); } + + inline QByteArray getConnectionSELinuxSecurityContext(const QString& connectionName) + { return call(QLatin1String("GetConnectionSELinuxSecurityContext.s"), connectionName).at(0).toByteArray(); } + + inline void reloadConfig() + { call(QLatin1String("ReloadConfig")); } +}; + + +namespace org { + namespace freedesktop { + namespace DBus { + typedef ::QDBusPeerInterface Peer; + typedef ::QDBusIntrospectableInterface Introspectable; + typedef ::QDBusPropertiesInterface Properties; + } + } +} + +#endif diff --git a/qt/qdbusthread.cpp b/qt/qdbusthread.cpp new file mode 100644 index 0000000..f45a009 --- /dev/null +++ b/qt/qdbusthread.cpp @@ -0,0 +1,116 @@ +/* qdbusintegrator.cpp QDBusConnection private implementation + * + * Copyright (C) 2005 Harald Fernengel <harry@kdevelop.org> + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include <QtCore/qmutex.h> +#include <QtCore/qwaitcondition.h> + +#include <dbus/dbus.h> + +struct DBusMutex: public QMutex +{ + inline DBusMutex() + : QMutex( QMutex::NonRecursive ) + { } + + static DBusMutex* mutex_new() + { + return new DBusMutex; + } + + static void mutex_free(DBusMutex *mutex) + { + delete mutex; + } + + static dbus_bool_t mutex_lock(DBusMutex *mutex) + { + mutex->lock(); + return true; + } + + static dbus_bool_t mutex_unlock(DBusMutex *mutex) + { + mutex->unlock(); + return true; + } +}; + +struct DBusCondVar: public QWaitCondition +{ + inline DBusCondVar() + { } + + static DBusCondVar* condvar_new() + { + return new DBusCondVar; + } + + static void condvar_free(DBusCondVar *cond) + { + delete cond; + } + + static void condvar_wait(DBusCondVar *cond, DBusMutex *mutex) + { + cond->wait(mutex); + } + + static dbus_bool_t condvar_wait_timeout(DBusCondVar *cond, DBusMutex *mutex, int msec) + { + return cond->wait(mutex, msec); + } + + static void condvar_wake_one(DBusCondVar *cond) + { + cond->wakeOne(); + } + + static void condvar_wake_all(DBusCondVar *cond) + { + cond->wakeAll(); + } +}; + +bool qDBusInitThreads() +{ + static DBusThreadFunctions fcn = { + DBUS_THREAD_FUNCTIONS_ALL_MASK, + DBusMutex::mutex_new, + DBusMutex::mutex_free, + DBusMutex::mutex_lock, + DBusMutex::mutex_unlock, + DBusCondVar::condvar_new, + DBusCondVar::condvar_free, + DBusCondVar::condvar_wait, + DBusCondVar::condvar_wait_timeout, + DBusCondVar::condvar_wake_one, + DBusCondVar::condvar_wake_all, + 0, 0, 0, 0, 0, 0, 0, 0 + }; + + dbus_threads_init(&fcn); + return true; +} + + diff --git a/qt/qdbustype.cpp b/qt/qdbustype.cpp new file mode 100644 index 0000000..036bbe1 --- /dev/null +++ b/qt/qdbustype.cpp @@ -0,0 +1,1151 @@ +/* -*- C++ -*- + * + * Copyright (C) 2005 Thiago Macieira <thiago@kde.org> + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "qdbustype.h" +#include "qdbusvariant.h" +#include <dbus/dbus.h> + +#include <QtCore/qstringlist.h> + +/// \internal +class QDBusPrettyTypeBase +{ +public: + struct Entry + { + const char* prettyName; + char signature; + }; + + enum Direction + { + In, + Out + }; + + enum Access + { + Read, + Write, + ReadWrite + }; + + // so that the compiler doesn't complain + virtual ~QDBusPrettyTypeBase() { } + + virtual QString addElementsToArray(const QString& subType) = 0; + virtual QString addElementsToMap(const QString& key, const QString& value) = 0; + virtual QString addElementsToStruct(const QStringList& subTypes) = 0; + virtual const Entry* entryMap() = 0; + + QString toString(const QDBusType& type); + QString toString(const QDBusTypeList& list); +}; + +/// \internal +class QDBusConventionalNames: public QDBusPrettyTypeBase +{ +public: + virtual QString addElementsToArray(const QString& subType); + virtual QString addElementsToMap(const QString& key, const QString& value); + virtual QString addElementsToStruct(const QStringList& subTypes) ; + virtual const Entry* entryMap(); +}; + +/// \internal +class QDBusQtNames: public QDBusPrettyTypeBase +{ +public: + virtual QString addElementsToArray(const QString& subType); + virtual QString addElementsToMap(const QString& key, const QString& value); + virtual QString addElementsToStruct(const QStringList& subTypes) ; + virtual const Entry* entryMap(); +}; + +//! \internal +class QDBusQVariantNames: public QDBusQtNames +{ +public: + virtual QString addElementsToArray(const QString& subType); + virtual QString addElementsToMap(const QString& key, const QString& value); + virtual QString addElementsToStruct(const QStringList& subTypes) ; +}; + +#if 0 +/* + * Parse the signature and return the max length that is valid + */ +static int parse(const char* signature) +{ + if (!signature || !*signature) + return 0; // not valid + + switch (signature[0]) { + case DBUS_TYPE_BOOLEAN: + case DBUS_TYPE_BYTE: + case DBUS_TYPE_INT16: + case DBUS_TYPE_INT32: + case DBUS_TYPE_UINT16: + case DBUS_TYPE_UINT32: + case DBUS_TYPE_INT64: + case DBUS_TYPE_UINT64: + case DBUS_TYPE_DOUBLE: + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + case DBUS_TYPE_SIGNATURE: + case DBUS_TYPE_VARIANT: + return 1; + + case DBUS_TYPE_ARRAY: { + // check if it's a dict-entry array + if (signature[1] == DBUS_DICT_ENTRY_BEGIN_CHAR) { + // the first type must be ok and primitive (length 1) + char c[2] = { signature[2], 0 }; + if (parse(c) != 1) + return 0; // not valid + + // the rest must be a valid type too + int len = parse(signature + 3); + if (len == 0) + return 0; // not valid + + // check the closing brace + if (signature[len + 3] != DBUS_DICT_ENTRY_END_CHAR) + return 0; // not valid + + // it's valid + return len + 4; + } + + // it's not a dict-entry, so it's ok as long as the internal type is ok too + int len = parse(signature + 1); + return len ? len + 1 : 0; + } + + case DBUS_STRUCT_BEGIN_CHAR: { + // check that each entry is valid + int i = 1; + while (i) { + if (i > 1 && signature[i] == DBUS_STRUCT_END_CHAR) + break; // this is valid + + int len = parse(signature + i); + if (len) + i += len; + else + i = 0; + } + return i; + } + + default: + return 0; // not valid + } +} +#endif + +static QString findInMap(char type, const QDBusPrettyTypeBase::Entry* map) +{ + for ( ; map->signature; ++map) + if (type == map->signature) + return QLatin1String(map->prettyName); + return QString(); +} + +// +// Input MUST be valid +// +inline QString QDBusPrettyTypeBase::toString(const QDBusType& type) +{ + const Entry* map = entryMap(); + + const QDBusTypeList subTypes = type.subTypes(); + switch (type.dbusType()) { + case DBUS_TYPE_STRUCT: { + // handle a struct + // find its sub-types + + QStringList subStrings; + QDBusTypeList subTypes = type.subTypes(); + foreach (QDBusType t, subTypes) + subStrings << toString( t ); + + return addElementsToStruct(subStrings); + } + + case DBUS_TYPE_DICT_ENTRY: { + Q_ASSERT_X(subTypes.size() == 2, "QDBusType::toString", + "maps must have exactly two elements"); + + QString key = findInMap( subTypes.at(0).dbusType(), map ); + QString value = toString( subTypes.at(1) ); + + Q_ASSERT(!key.isNull()); + + return addElementsToMap( key, value ); + } + case DBUS_TYPE_ARRAY: { + Q_ASSERT_X(subTypes.size() == 1, "QDBusType::toString", + "more than one element in array"); + + if (type.qvariantType() == QVariant::Map) + return toString( subTypes.first() ); + return addElementsToArray( toString( subTypes.at(0) ) ); + } + + default: { + // normal, non-compound type + QString name = findInMap(type.dbusType(), map); + Q_ASSERT(!name.isNull()); + return name; + } + } +} + +const QDBusPrettyTypeBase::Entry* QDBusConventionalNames::entryMap() +{ + static QDBusPrettyTypeBase::Entry translation[] = { + { "BYTE", DBUS_TYPE_BYTE }, + { "BOOLEAN", DBUS_TYPE_BOOLEAN }, + { "INT16", DBUS_TYPE_INT16 }, + { "UINT16", DBUS_TYPE_UINT16 }, + { "INT32", DBUS_TYPE_INT32 }, + { "UINT32", DBUS_TYPE_UINT32 }, + { "INT64", DBUS_TYPE_INT64 }, + { "UINT64", DBUS_TYPE_UINT64 }, + { "DOUBLE", DBUS_TYPE_DOUBLE }, + { "STRING", DBUS_TYPE_STRING }, + { "OBJECT_PATH", DBUS_TYPE_OBJECT_PATH }, + { "SIGNATURE", DBUS_TYPE_SIGNATURE }, + { "VARIANT", DBUS_TYPE_VARIANT } + }; + return translation; +} + +QString QDBusConventionalNames::addElementsToStruct(const QStringList& subTypes) +{ + return QString( QLatin1String("STRUCT of (%1)") ) + .arg( subTypes.join( QLatin1String(",") ) ); +} + +QString QDBusConventionalNames::addElementsToMap(const QString& key, const QString& value) +{ + return QString( QLatin1String("ARRAY of DICT_ENTRY of (%1,%2)") ) + .arg(key).arg(value); +} + +QString QDBusConventionalNames::addElementsToArray(const QString& subType) +{ + return QString( QLatin1String("ARRAY of %1") ) + .arg(subType); +} + +const QDBusPrettyTypeBase::Entry* QDBusQtNames::entryMap() +{ + static QDBusPrettyTypeBase::Entry translation[] = { + { "quint8", DBUS_TYPE_BYTE }, + { "bool", DBUS_TYPE_BOOLEAN }, + { "qint16", DBUS_TYPE_INT16 }, + { "quint16", DBUS_TYPE_UINT16 }, + { "qint32", DBUS_TYPE_INT32 }, + { "quint32", DBUS_TYPE_UINT32 }, + { "qint64", DBUS_TYPE_INT64 }, + { "quint64", DBUS_TYPE_UINT64 }, + { "double", DBUS_TYPE_DOUBLE }, + { "QString", DBUS_TYPE_STRING }, + { "QString", DBUS_TYPE_OBJECT_PATH }, + { "QString", DBUS_TYPE_SIGNATURE }, + { "QDBusVariant", DBUS_TYPE_VARIANT } + }; + return translation; +} + +static inline QString templateArg(const QString& input) +{ + if (input.endsWith('>')) + return input + ' '; + return input; +} + +QString QDBusQtNames::addElementsToStruct(const QStringList& subTypes) +{ + Q_UNUSED(subTypes); + + return QLatin1String("QList"); // CHANGEME in the future +} + +QString QDBusQtNames::addElementsToMap(const QString& key, const QString& value) +{ + return QString( QLatin1String("QMap<%1, %2>") ) + .arg(key) + .arg( templateArg(value) ); +} + +QString QDBusQtNames::addElementsToArray(const QString& subType) +{ + if (subType == QLatin1String("quint8")) + // special case + return QLatin1String("QByteArray"); + + return QString( QLatin1String("QList<%1>") ) + .arg( templateArg(subType) ); +} + +QString QDBusQVariantNames::addElementsToStruct(const QStringList& subTypes) +{ + Q_UNUSED(subTypes); + + return QLatin1String("QVariantList"); +} + +QString QDBusQVariantNames::addElementsToMap(const QString& key, const QString& value) +{ + Q_UNUSED(key); + Q_UNUSED(value); + + return QLatin1String("QVariantMap"); +} + +QString QDBusQVariantNames::addElementsToArray(const QString& subType) +{ + if (subType == QLatin1String("quint8")) + // special case + return QLatin1String("QByteArray"); + + return QLatin1String("QVariantList"); +} + +/*! + \internal +*/ +class QDBusTypePrivate: public QSharedData +{ +public: + int code; + mutable QVariant::Type qvariantType; + mutable QByteArray signature; + QDBusTypeList subTypes; + + inline QDBusTypePrivate() + : code(0), qvariantType(QVariant::Invalid) + { } +}; + +/*! + \class QDBusType + + Represents one single DBus type. +*/ + +/*! + \enum QDBusType::StringFormat + + This enum is used in QDBusType::toString to determine which type of formatting + to apply to the DBus types: + + \value ConventionalNames Use the DBus conventional names, such as STRING, BOOLEAN or + ARRAY of BYTE. + \value QtNames Use the Qt type names, such as QString, bool and QList<quint32> + \value QVariantNames Same as QtNames, but for containers, use QVariantList and QVariantMap +*/ + +/*! + Constructs an empty (invalid) type. +*/ +QDBusType::QDBusType() + : d(0) +{ +} + +/*! + Constructs the type based on the given DBus type. + + \param type the type +*/ +QDBusType::QDBusType(int type) +{ + char c[2] = { type, 0 }; + *this = QDBusType(c); +} + +/*! + Constructs the type based on the given QVariant type. + + \param type the type + \sa QVariant::Type +*/ +QDBusType::QDBusType(QVariant::Type type) +{ + const char *sig = dbusSignature(type); + + // it never returns NULL + if (sig[0] == '\0') + return; + + d = new QDBusTypePrivate; + d->qvariantType = type; + d->code = sig[0]; + if (sig[1] == '\0') + // single-letter type + return; + else if (sig[2] == '\0') { + // two-letter type + // must be an array + d->code = sig[0]; + QDBusType t; + t.d = new QDBusTypePrivate; + t.d->code = sig[1]; + d->subTypes << t; + } + else { + // the only longer type is "a{sv}" + Q_ASSERT(sig[1] == '{' && sig[5] == '\0'); + + static QDBusType map("a{sv}"); + d->subTypes = map.d->subTypes; + } +} + +/*! + Parses the given DBus signature and constructs the type it represents. + + \param signature the signature to parse. It must represent one single type, but can + a container type. +*/ +QDBusType::QDBusType(const char* signature) +{ + if ( !dbus_signature_validate_single(signature, 0) ) + return; + + DBusSignatureIter iter; + dbus_signature_iter_init(&iter, signature); + *this = QDBusType(&iter); + if (d) + d->signature = signature; +} + +/*! + Parses the given DBus signature and constructs the type it represents. + + \param signature the signature to parse. It must represent one single type, but can + a container type. +*/ +QDBusType::QDBusType(const QString& str) +{ + *this = QDBusType( str.toUtf8().constData() ); +} + +/*! + Parses the given DBus signature and constructs the type it represents. + + \param signature the signature to parse. It must represent one single type, but can + a container type. +*/ +QDBusType::QDBusType(const QByteArray& str) +{ + *this = QDBusType( str.constData() ); +} + +/*! + Creates a QDBusType object based on the current element pointed to by \a iter. + + \param iter the iterator. Can be pointing to container types. +*/ +QDBusType::QDBusType(DBusSignatureIter* iter) + : d(new QDBusTypePrivate) +{ + if ( dbus_type_is_container( d->code = dbus_signature_iter_get_current_type(iter) ) ) { + // we have to recurse + if ( d->code == DBUS_TYPE_VARIANT ) + return; // no we don't. dbus_type_is_container lies to us + + // we have to recurse + DBusSignatureIter subiter; + dbus_signature_iter_recurse(iter, &subiter); + + d->subTypes = QDBusTypeList(&subiter); + + // sanity checking: + if ( d->code == DBUS_TYPE_ARRAY ) + Q_ASSERT_X(d->subTypes.size() == 1, "QDBusType", + "more than one element in array"); + else if (d->code == DBUS_TYPE_DICT_ENTRY ) + Q_ASSERT_X(d->subTypes.size() == 2, "QDBusType", + "maps must have exactly two elements"); + } +} + +/*! + Copies the type from the other object. +*/ +QDBusType::QDBusType(const QDBusType& other) + : d(other.d) +{ +} + +/*! + Release the resources associated with this type. +*/ +QDBusType::~QDBusType() +{ +} + +/*! + Copies the type from the other object. +*/ +QDBusType& QDBusType::operator=(const QDBusType& other) +{ + d = other.d; + return *this; +} + +/*! + Returns the DBus type for this type. +*/ +int QDBusType::dbusType() const +{ + return d ? d->code : DBUS_TYPE_INVALID; +} + +/*! + Returns the DBus signature for this type and subtypes. +*/ +QByteArray QDBusType::dbusSignature() const +{ + if (!d) + return QByteArray(); + + if (!d->signature.isEmpty()) + return d->signature; + + if (d->subTypes.isEmpty()) + return d->signature = QByteArray(1, d->code); + + QByteArray retval; + switch (d->code) { + // can only be array, map or struct + + case DBUS_TYPE_ARRAY: + Q_ASSERT_X(d->subTypes.size() == 1, "QDBusType::dbusSignature", + "more than one element in array"); + + retval += DBUS_TYPE_ARRAY; + retval += d->subTypes.at(0).dbusSignature(); + break; + + case DBUS_TYPE_DICT_ENTRY: { + Q_ASSERT_X(d->subTypes.size() == 2, "QDBusType::dbusSignature", + "maps must have exactly two elements"); + + QByteArray value = d->subTypes.at(1).dbusSignature(); + char key = d->subTypes.at(0).dbusType(); + + Q_ASSERT(key != DBUS_TYPE_INVALID); + Q_ASSERT(!value.isEmpty()); + + retval.reserve(value.length() + 3); + retval = DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING; + retval += key; + retval += value; + retval += DBUS_DICT_ENTRY_END_CHAR; + break; + } + + case DBUS_TYPE_STRUCT: + retval = d->subTypes.dbusSignature(); + retval.prepend(DBUS_STRUCT_BEGIN_CHAR); + retval.append(DBUS_STRUCT_END_CHAR); + break; + + default: + Q_ASSERT_X(false, "QDBusType::dbusSignature", "invalid container type"); + } + + d->signature = retval; + return retval; +} + +/*! + Returns the QVariant::Type for this entry. +*/ +QVariant::Type QDBusType::qvariantType() const +{ + if (d && d->qvariantType != QVariant::Invalid) + return d->qvariantType; + + // check the special array cases: + if (isArray()) { + QDBusType t = arrayElement(); + + if (t.dbusType() == DBUS_TYPE_BYTE) + return QVariant::ByteArray; + else if (t.dbusType() == DBUS_TYPE_DICT_ENTRY) + return QVariant::Map; + else if (t.isBasic() && t.qvariantType() == QVariant::String) + return QVariant::StringList; + } + + return qvariantType(dbusType()); +} + +/*! + Returns true if this type is a valid one. +*/ +bool QDBusType::isValid() const +{ + return d && d->code != DBUS_TYPE_INVALID; +} + +/*! + Returns true if this type is a basic one. + + \sa dbus_type_is_basic +*/ +bool QDBusType::isBasic() const +{ + return d && dbus_type_is_basic(d->code); +} + +/*! + Returns true if this type is a container. + + \sa dbus_type_is_container +*/ +bool QDBusType::isContainer() const +{ + return d && dbus_type_is_container(d->code); +} + +/*! + Returns the subtypes of this type, if this is a container. + + \sa isContainer +*/ +QDBusTypeList QDBusType::subTypes() const +{ + if (d) + return d->subTypes; + return QDBusTypeList(); +} + +/*! + Returns true if this type is an array. + + \sa isContainer, arrayElement +*/ +bool QDBusType::isArray() const +{ + return dbusType() == DBUS_TYPE_ARRAY; +} + +/*! + This is a convenience function that returns the element type of an array. + If this object is not an array, it returns an invalid QDBusType. + + \sa isArray +*/ +QDBusType QDBusType::arrayElement() const +{ + if (isArray() && d->subTypes.count() == 1) + return d->subTypes.first(); + return QDBusType(); +} + +/*! + Returns true if this type is a map (i.e., an array of dictionary entries). + + \sa isContainer, isArray, arrayElement +*/ +bool QDBusType::isMap() const +{ + return arrayElement().dbusType() == DBUS_TYPE_DICT_ENTRY; +} + +/*! + If this object is a map, returns the (basic) type that corresponds to the key type. + If this object is not a map, returns an invalid QDBusType. + + \sa isMap +*/ +QDBusType QDBusType::mapKey() const +{ + if (isMap()) + return arrayElement().d->subTypes.first(); + return QDBusType(); +} + +/*! + If this object is a map, returns the type that corresponds to the value type. + If this object is not a map, returns an invalid QDBusType. + + \sa isMap +*/ +QDBusType QDBusType::mapValue() const +{ + if (isMap()) + return arrayElement().d->subTypes.at(1); + return QDBusType(); +} + +/*! + Returns true if the two types match. +*/ +bool QDBusType::operator==(const QDBusType& other) const +{ + if (!d && !other.d) + return true; + if (!d || !other.d) + return false; + return d->code == other.d->code && d->subTypes == other.d->subTypes; +} + +/*! + Returns a string representation of this type. +*/ +QString QDBusType::toString(StringFormat sf) const +{ + switch (sf) { + case ConventionalNames: + return QDBusConventionalNames().toString(*this); + + case QtNames: + return QDBusQtNames().toString(*this); + + case QVariantNames: + return QDBusQVariantNames().toString(*this); + } + + return QString(); // invalid +} + +/*! + Converts the DBus type to QVariant::Type +*/ +QVariant::Type QDBusType::qvariantType(int type) +{ + char c[2] = { type, 0 }; + return qvariantType(c); +} + +/*! + Converts the DBus type signature to QVariant::Type. +*/ +QVariant::Type QDBusType::qvariantType(const char* signature) +{ + if (!signature) + return QVariant::Invalid; + + // three special cases that don't validate as single: + if (qstrlen(signature) == 1) { + if (signature[0] == DBUS_TYPE_STRUCT) + return QVariant::List; + else if (signature[0] == DBUS_TYPE_DICT_ENTRY) + return QVariant::Map; + else if (signature[0] == DBUS_TYPE_ARRAY) + return QVariant::List; + } + + // now we can validate + if ( !dbus_signature_validate_single(signature, 0) ) + return QVariant::Invalid; + + switch (signature[0]) + { + case DBUS_TYPE_BOOLEAN: + return QVariant::Bool; + + case DBUS_TYPE_INT16: + case DBUS_TYPE_INT32: + return QVariant::Int; + + case DBUS_TYPE_BYTE: + case DBUS_TYPE_UINT16: + 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: + case DBUS_TYPE_OBJECT_PATH: + case DBUS_TYPE_SIGNATURE: + return QVariant::String; + + case DBUS_STRUCT_BEGIN_CHAR: + return QVariant::List; // change to QDBusStruct in the future + + case DBUS_TYPE_VARIANT: + return QVariant::UserType; // must set user-type too + + case DBUS_TYPE_ARRAY: // special case + // check if it's a string list + if (qvariantType(signature + 1) == QVariant::String) + return QVariant::StringList; + + // maybe it's a byte array + if (DBUS_TYPE_BYTE == signature[1]) + return QVariant::ByteArray; + + // check if it's a dict + if (DBUS_DICT_ENTRY_BEGIN_CHAR == signature[1]) + return QVariant::Map; + + return QVariant::List; + + default: + return QVariant::Invalid; + + } +} + +/*! + Converts the QVariant::Type to a DBus type code. + + \param t the type to convert +*/ +int QDBusType::dbusType(QVariant::Type t) +{ + switch (t) + { + case QVariant::Bool: + return DBUS_TYPE_BOOLEAN; + + case QVariant::Int: + return DBUS_TYPE_INT32; + + case QVariant::UInt: + case QVariant::Char: + return DBUS_TYPE_UINT32; + + case QVariant::LongLong: + return DBUS_TYPE_INT64; + + case QVariant::ULongLong: + return DBUS_TYPE_UINT64; + + case QVariant::Double: + return DBUS_TYPE_DOUBLE; + + // from QMetaType: + case QMetaType::Short: + return DBUS_TYPE_INT16; + + case QMetaType::UShort: + return DBUS_TYPE_UINT16; + + case QMetaType::UChar: + return DBUS_TYPE_BYTE; + + case QVariant::String: + case QVariant::Date: + case QVariant::Time: + case QVariant::DateTime: + return DBUS_TYPE_STRING; + + case QVariant::Map: + // internal type information has been lost + return DBUS_TYPE_DICT_ENTRY; + + case QVariant::List: + case QVariant::StringList: + case QVariant::ByteArray: + // could also be a struct... + return DBUS_TYPE_ARRAY; + + case QVariant::UserType: + return DBUS_TYPE_VARIANT; + + default: + break; // avoid compiler warnings + } + + if (int(t) == QMetaTypeId<QDBusVariant>::qt_metatype_id()) + return DBUS_TYPE_VARIANT; + + return DBUS_TYPE_INVALID; +} + +/*! + Converts the QVariant::Type to a DBus type signature. + + \param t the type to convert +*/ +const char* QDBusType::dbusSignature(QVariant::Type t) +{ + switch (t) + { + case QVariant::Bool: + return DBUS_TYPE_BOOLEAN_AS_STRING; + + case QVariant::Int: + return DBUS_TYPE_INT32_AS_STRING; + + case QVariant::UInt: + case QVariant::Char: + return DBUS_TYPE_UINT32_AS_STRING; + + case QMetaType::Short: + return DBUS_TYPE_INT16_AS_STRING; + + case QMetaType::UShort: + return DBUS_TYPE_UINT16_AS_STRING; + + case QMetaType::UChar: + return DBUS_TYPE_BYTE_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: + case QVariant::Date: + case QVariant::Time: + case QVariant::DateTime: + return DBUS_TYPE_STRING_AS_STRING; + + case QVariant::Map: + // internal type information has been lost + return DBUS_TYPE_ARRAY_AS_STRING + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING; // a{sv} + + 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 + + case QVariant::List: + // not a string list + // internal list data has been lost + // could also be a struct... + return DBUS_TYPE_ARRAY_AS_STRING + DBUS_TYPE_VARIANT_AS_STRING; // av + + default: + if (int(t) == qMetaTypeId<QDBusVariant>()) + return DBUS_TYPE_VARIANT_AS_STRING; + + return DBUS_TYPE_INVALID_AS_STRING; + } +} + +/*! + Guesses the DBus type from the given variant. +*/ +QDBusType QDBusType::guessFromVariant(const QVariant& variant, VariantListMode mode) +{ + if (variant.type() == QVariant::List) { + // investigate deeper + QDBusType t; + t.d = new QDBusTypePrivate; + + if (mode == ListIsArray) { + t.d->code = DBUS_TYPE_ARRAY; + + const QVariantList list = variant.toList(); + if (!list.isEmpty()) { + // check if all elements have the same type + QVariant::Type type = list.first().type(); + foreach (const QVariant& v, list) + if (type != v.type()) { + // at least one is different + type = QVariant::Invalid; + break; + } + + if (type != QVariant::Invalid) { + // all are of the same type + t.d->subTypes << guessFromVariant(list.first()); + return t; + } + } + + // internal information has been lost or there are many types + QDBusType nested; + nested.d = new QDBusTypePrivate; + nested.d->code = DBUS_TYPE_VARIANT; + t.d->subTypes << nested; + return t; + } + else { + // treat it as a struct + t.d->code = DBUS_TYPE_STRUCT; + + // add the elements: + const QVariantList list = variant.toList(); + foreach (const QVariant& v, list) + t.d->subTypes << guessFromVariant(v, mode); + + return t; + } + } + else if (variant.type() == QVariant::Map) { + // investigate deeper + QDBusType t, t2, t3; + t2.d = new QDBusTypePrivate; + t2.d->code = DBUS_TYPE_DICT_ENTRY; + + // the key + t3.d = new QDBusTypePrivate; + t3.d->code = DBUS_TYPE_STRING; + t2.d->subTypes << t3; + + const QVariantMap map = variant.toMap(); + if (!map.isEmpty()) { + // check if all elements have the same type + QVariantMap::const_iterator it = map.constBegin(), + end = map.constEnd(); + QVariant::Type type = it.value().type(); + for ( ; it != end; ++it) + if (type != it.value().type()) { + // at least one is different + type = QVariant::Invalid; + break; + } + + if (type != QVariant::Invalid) + t2.d->subTypes << guessFromVariant(map.constBegin().value()); + else { + // multiple types + t3.d->code = DBUS_TYPE_VARIANT; + t2.d->subTypes << t3; + } + } + else { + // information lost + t3.d->code = DBUS_TYPE_VARIANT; + t2.d->subTypes << t3; + } + + t.d = new QDBusTypePrivate; + t.d->code = DBUS_TYPE_ARRAY; + t.d->subTypes << t2; + return t; + } + else + return QDBusType(variant.type()); +} + +/*! + \class QDBusTypeList + \brief A list of DBus types. + + Represents zero or more DBus types in sequence, such as those used in argument lists + or in subtypes of structs and maps. +*/ + +/*! + \fn QDBusTypeList::QDBusTypeList() + + Default constructor. + */ + +/*! + \fn QDBusTypeList::QDBusTypeList(const QDBusTypeList& other) + + Copy constructor. + \param other the list to copy + */ + +/*! + \fn QDBusTypeList::QDBusTypeList(const QList<QDBusType>& other) + + Copy constructor. + \param other the list to copy + */ + +/*! + Constructs a type list by parsing the signature given. + \param signature the signature to be parsed + */ +QDBusTypeList::QDBusTypeList(const char* signature) +{ + if (!signature || !*signature) + return; // empty + + // validate it first + if ( !dbus_signature_validate(signature, 0) ) + return; + + // split it into components + DBusSignatureIter iter; + dbus_signature_iter_init(&iter, signature); + + do { + *this << QDBusType(&iter); + } while (dbus_signature_iter_next(&iter)); +} + +/*! + Constructs a type list by parsing the elements on this iterator level. + + \param iter the iterator containing the elements on this level +*/ +QDBusTypeList::QDBusTypeList(DBusSignatureIter* iter) +{ + do { + QDBusType item(iter); + if (!item.isValid()) { + clear(); + return; + } + + *this << item; + } while (dbus_signature_iter_next(iter)); +} + +/*! + Returns true if this type list can represent the inner components of a map. +*/ +bool QDBusTypeList::canBeMap() const +{ + return size() == 2 && at(0).isBasic(); +} + +/*! + Reconstructs the type signature that this type list represents. +*/ +QByteArray QDBusTypeList::dbusSignature() const +{ + QByteArray retval; + foreach (QDBusType t, *this) + retval += t.dbusSignature(); + return retval; +} diff --git a/qt/qdbustype.h b/qt/qdbustype.h new file mode 100644 index 0000000..c26d7c8 --- /dev/null +++ b/qt/qdbustype.h @@ -0,0 +1,119 @@ +/* -*- C++ -*- + * + * Copyright (C) 2005 Thiago Macieira <thiago@kde.org> + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef QDBUSTYPE_H +#define QDBUSTYPE_H + +#include <QtCore/qvariant.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qshareddata.h> +#include <QtCore/qlist.h> +#include "qdbusmacros.h" +#include <dbus/dbus.h> + +// forward declaration +class QDBusTypeList; + +class QDBusTypePrivate; +class QDBUS_EXPORT QDBusType +{ +public: + enum StringFormat + { + ConventionalNames, + QtNames, + QVariantNames + }; + + QDBusType(); + explicit QDBusType(int type); + explicit QDBusType(QVariant::Type type); + explicit QDBusType(const char* signature); + explicit QDBusType(DBusSignatureIter*); + explicit QDBusType(const QString& str); + explicit QDBusType(const QByteArray& str); + QDBusType(const QDBusType& other); + + ~QDBusType(); + + QDBusType& operator=(const QDBusType& other); + + QVariant::Type qvariantType() const; + + int dbusType() const; + QByteArray dbusSignature() const; + bool isValid() const; + bool isBasic() const; + bool isContainer() const; + + QDBusTypeList subTypes() const; + + bool isArray() const; + QDBusType arrayElement() const; + + bool isMap() const; + QDBusType mapKey() const; + QDBusType mapValue() const; + + bool operator==(const QDBusType& other) const; + + QString toString(StringFormat = QtNames) const; + + static QVariant::Type qvariantType(int type); + static QVariant::Type qvariantType(const char* signature); + static int dbusType(QVariant::Type); + static const char* dbusSignature(QVariant::Type); + + enum VariantListMode { + ListIsArray, + ListIsStruct + }; + static QDBusType guessFromVariant(const QVariant &variant, VariantListMode = ListIsArray); + +private: + QSharedDataPointer<QDBusTypePrivate> d; +}; + +class QDBUS_EXPORT QDBusTypeList: public QList<QDBusType> +{ +public: + inline QDBusTypeList() { } + inline QDBusTypeList(const QDBusTypeList& other) + : QList<QDBusType>(other) + { } + inline QDBusTypeList(const QList<QDBusType>& other) + : QList<QDBusType>(other) + { } + QDBusTypeList(const char* signature); + QDBusTypeList(DBusSignatureIter*); + + bool canBeMap() const; + + inline QDBusTypeList& operator<<(const QDBusType& item) + { QList<QDBusType>::operator<<(item); return *this; } + + QByteArray dbusSignature() const; +}; + +#endif // QDBUSTYPE_H diff --git a/qt/qdbusutil.cpp b/qt/qdbusutil.cpp new file mode 100644 index 0000000..e10b302 --- /dev/null +++ b/qt/qdbusutil.cpp @@ -0,0 +1,133 @@ +/* -*- C++ -*- + * + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "qdbusutil.h" + +#include <dbus/dbus.h> + +#include <QtCore/qstringlist.h> +#include <QtCore/qregexp.h> + +namespace QDBusUtil +{ + + bool isValidInterfaceName(const QString& ifaceName) + { + if (ifaceName.isEmpty() || ifaceName.length() > DBUS_MAXIMUM_NAME_LENGTH) + return false; + + QStringList parts = ifaceName.split('.'); + if (parts.count() < 2) + return false; // at least two parts + + foreach (QString part, parts) + if (!isValidMemberName(part)) + return false; + + return true; + } + + bool isValidUniqueConnectionName(const QString &connName) + { + if (connName.isEmpty() || connName.length() > DBUS_MAXIMUM_NAME_LENGTH || + !connName.startsWith(':')) + return false; + + QStringList parts = connName.mid(1).split('.'); + if (parts.count() < 1) + return false; + + QRegExp regex("[a-zA-Z0-9_-]+"); + foreach (QString part, parts) + if (!regex.exactMatch(part)) + return false; + + return true; + } + + bool isValidBusName(const QString &busName) + { + if (busName.isEmpty() || busName.length() > DBUS_MAXIMUM_NAME_LENGTH) + return false; + + if (busName.startsWith(':')) + return isValidUniqueConnectionName(busName); + + QStringList parts = busName.split('.'); + if (parts.count() < 1) + return false; + + QRegExp regex("[a-zA-Z_-][a-zA-Z0-9_-]*"); + foreach (QString part, parts) + if (!regex.exactMatch(part)) + return false; + + return true; + } + + bool isValidMemberName(const QString &memberName) + { + if (memberName.isEmpty() || memberName.length() > DBUS_MAXIMUM_NAME_LENGTH) + return false; + + QRegExp regex("[a-zA-Z0-9_]+"); + return regex.exactMatch(memberName); + } + + bool isValidErrorName(const QString &errorName) + { + return isValidInterfaceName(errorName); + } + + bool isValidObjectPath(const QString &path) + { + if (path == QLatin1String("/")) + return true; + + if (!path.startsWith('/') || path.indexOf(QLatin1String("//")) != -1 || + path.endsWith('/')) + return false; + + QStringList parts = path.split('/'); + Q_ASSERT(parts.count() >= 1); + parts.removeFirst(); // it starts with /, so we get an empty first part + + QRegExp regex("[a-zA-Z0-9_]+"); + foreach (QString part, parts) + if (!regex.exactMatch(part)) + return false; + + return true; + } + + bool isValidSignature(const QString &signature) + { + return dbus_signature_validate(signature.toUtf8(), 0); + } + + bool isValidSingleSignature(const QString &signature) + { + return dbus_signature_validate_single(signature.toUtf8(), 0); + } + +} // namespace QDBusUtil diff --git a/qt/qdbusutil.h b/qt/qdbusutil.h new file mode 100644 index 0000000..4c3bac6 --- /dev/null +++ b/qt/qdbusutil.h @@ -0,0 +1,50 @@ +/* -*- C++ -*- + * + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef QDBUSUTIL_H +#define QDBUSUTIL_H + +#include <QtCore/qstring.h> + +#include "qdbusmacros.h" + +namespace QDBusUtil +{ + 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 isValidObjectPath(const QString &path) QDBUS_EXPORT; + + bool isValidSignature(const QString &signature) QDBUS_EXPORT; + + bool isValidSingleSignature(const QString &signature) QDBUS_EXPORT; +} + +#endif diff --git a/qt/qdbusvariant.h b/qt/qdbusvariant.h index bd18bef..eb7e3aa 100644 --- a/qt/qdbusvariant.h +++ b/qt/qdbusvariant.h @@ -1,6 +1,8 @@ /* qdbusvariant.h DBUS variant struct * * Copyright (C) 2005 Harald Fernengel <harry@kdevelop.org> + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> * * Licensed under the Academic Free License version 2.1 * @@ -15,23 +17,21 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, write to the Free Software Foundation + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #ifndef QDBUSVARIANT_H #define QDBUSVARIANT_H -#include "dbus/qdbus.h" - -#include <QtCore/qmetatype.h> -#include <QtCore/qstring.h> -#include <QtCore/qvariant.h> +#include "qdbusmacros.h" +#include "qdbustype.h" +#include <qvariant.h> struct QDBUS_EXPORT QDBusVariant { - QString signature; + QDBusType type; QVariant value; }; Q_DECLARE_METATYPE(QDBusVariant) diff --git a/qt/qdbusxmlparser.cpp b/qt/qdbusxmlparser.cpp new file mode 100644 index 0000000..9d80fe2 --- /dev/null +++ b/qt/qdbusxmlparser.cpp @@ -0,0 +1,347 @@ +/* -*- C++ -*- + * + * Copyright (C) 2005 Thiago Macieira <thiago@kde.org> + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "qdbusxmlparser_p.h" +#include "qdbusinterface.h" +#include "qdbusinterface_p.h" +#include "qdbusconnection_p.h" +#include "qdbusobject_p.h" + +#include <QtXml/qdom.h> +#include <QtCore/qmap.h> +#include <QtCore/qvariant.h> +#include <QtCore/qtextstream.h> + +static QDBusIntrospection::Annotations +parseAnnotations(const QDomElement& elem) +{ + QDBusIntrospection::Annotations retval; + QDomNodeList list = elem.elementsByTagName("annotation"); + for (int i = 0; i < list.count(); ++i) + { + QDomElement ann = list.item(i).toElement(); + if (ann.isNull()) + continue; + + QString name = ann.attribute("name"), + value = ann.attribute("value"); + + if (name.isEmpty()) + continue; + + retval.insert(name, value); + } + + return retval; +} + +static QDBusType +parseType(const QString& type) +{ + if (type.isEmpty()) + return QDBusType(); + return QDBusType(type); +} + +static QDBusIntrospection::Arguments +parseArgs(const QDomElement& elem, const QLatin1String& direction, bool acceptEmpty = false) +{ + QDBusIntrospection::Arguments retval; + QDomNodeList list = elem.elementsByTagName("arg"); + for (int i = 0; i < list.count(); ++i) + { + QDomElement arg = list.item(i).toElement(); + if (arg.isNull()) + continue; + + if ((acceptEmpty && !arg.hasAttribute("direction")) || + arg.attribute("direction") == direction) { + + QDBusIntrospection::Argument argData; + if (arg.hasAttribute("name")) + argData.name = arg.attribute("name"); // can be empty + argData.type = parseType(arg.attribute("type")); + + if (!argData.type.isValid()) + continue; + + retval << argData; + } + } + return retval; +} + +QDBusXmlParser::QDBusXmlParser(const QString& service, const QString& path, + const QString& xmlData, QDBusConnectionPrivate* store) + : m_service(service), m_path(path), m_store(store) +{ + QDomDocument doc; + doc.setContent(xmlData); + m_node = doc.firstChildElement("node"); +} + +QDBusXmlParser::QDBusXmlParser(const QString& service, const QString& path, + const QDomElement& node, QDBusConnectionPrivate* store) + : m_service(service), m_path(path), m_node(node), m_store(store) +{ +} + +void QDBusXmlParser::parse(const QDBusObjectPrivate* d, const QString &xml) +{ + QDBusXmlParser parser(d->data->service, d->data->path, xml, d->parent); + parser.object(); + parser.interfaces(); +} + +QDBusIntrospection::Interfaces +QDBusXmlParser::interfaces() const +{ + QDBusIntrospection::Interfaces retval; + + if (m_node.isNull()) + return retval; + + QDomNodeList interfaces = m_node.elementsByTagName("interface"); + for (int i = 0; i < interfaces.count(); ++i) + { + QDomElement iface = interfaces.item(i).toElement(); + QString ifaceName = iface.attribute("name"); + if (iface.isNull() || ifaceName.isEmpty()) + continue; // for whatever reason + + QDBusIntrospection::Interface *ifaceData; + if (m_store) { + QSharedDataPointer<QDBusIntrospection::Interface> knownData = + m_store->findInterface(ifaceName); + if (!knownData.constData()->introspection.isEmpty()) { + // it's already known + // we don't have to re-parse + retval.insert(ifaceName, knownData); + continue; + } + + // ugly, but ok + // we don't want to detach + // we *WANT* to modify the shared data + ifaceData = const_cast<QDBusIntrospection::Interface*>( knownData.constData() ); + } + else { + 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("method"); + for (int j = 0; j < list.count(); ++j) + { + QDomElement method = list.item(j).toElement(); + QString methodName = method.attribute("name"); + if (method.isNull() || methodName.isEmpty()) + continue; + + QDBusIntrospection::Method methodData; + methodData.name = methodName; + + // parse arguments + methodData.inputArgs = parseArgs(method, QLatin1String("in")); + methodData.outputArgs = parseArgs(method, QLatin1String("out")); + methodData.annotations = parseAnnotations(method); + + // add it + ifaceData->methods.insert(methodName, methodData); + } + + // parse signals + list = iface.elementsByTagName("signal"); + for (int j = 0; j < list.count(); ++j) + { + QDomElement signal = list.item(j).toElement(); + QString signalName = signal.attribute("name"); + if (signal.isNull() || signalName.isEmpty()) + 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("property"); + for (int j = 0; j < list.count(); ++j) + { + QDomElement property = list.item(j).toElement(); + QString propertyName = property.attribute("name"); + if (property.isNull() || propertyName.isEmpty()) + continue; + + QDBusIntrospection::Property propertyData; + + // parse data + propertyData.name = propertyName; + propertyData.type = parseType(property.attribute("type")); + propertyData.annotations = parseAnnotations(property); + + if (!propertyData.type.isValid()) + // cannot be! + continue; + + QString access = property.attribute("access"); + if (access.isEmpty()) + // can't be empty either! + continue; + else 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 + 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 +{ + QSharedDataPointer<QDBusIntrospection::Object> retval; + + if (m_node.isNull()) + return retval; + + // check if the store knows about this one + QDBusIntrospection::Object* objData; + if (m_store) { + retval = objData = m_store->findObject(m_service, m_path); + } + else { + 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("node"); + for (int i = 0; i < objects.count(); ++i) { + QDomElement obj = objects.item(i).toElement(); + QString objName = obj.attribute("name"); + if (obj.isNull() || objName.isEmpty()) + continue; // for whatever reason + + objData->childObjects.append(objName); + } + + QDomNodeList interfaces = m_node.elementsByTagName("interface"); + for (int i = 0; i < interfaces.count(); ++i) { + QDomElement iface = interfaces.item(i).toElement(); + QString ifaceName = iface.attribute("name"); + if (iface.isNull() || ifaceName.isEmpty()) + continue; + + objData->interfaces.append(ifaceName); + } + } + + 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("node"); + for (int i = 0; i < objects.count(); ++i) { + QDomElement obj = objects.item(i).toElement(); + QString objName = obj.attribute("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 ts(&xml); + obj.save(ts,0); + + // parse it + QString objAbsName = m_path; + if (!objAbsName.endsWith('/')) + objAbsName.append('/'); + objAbsName += objName; + + QDBusXmlParser parser(m_service, objAbsName, obj, m_store); + retval->childObjectData.insert(objName, parser.objectTree()); + } + + retval->childObjects << objName; + } + + return QSharedDataPointer<QDBusIntrospection::ObjectTree>( retval ); +} + diff --git a/qt/qdbusxmlparser_p.h b/qt/qdbusxmlparser_p.h new file mode 100644 index 0000000..c4c45be --- /dev/null +++ b/qt/qdbusxmlparser_p.h @@ -0,0 +1,59 @@ +/* -*- C++ -*- + * + * Copyright (C) 2005 Thiago Macieira <thiago@kde.org> + * Copyright (C) 2006 Trolltech AS. All rights reserved. + * Author: Thiago Macieira <thiago.macieira@trolltech.com> + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef QDBUSXMLPARSER_H +#define QDBUSXMLPARSER_H + +#include <QtCore/qmap.h> +#include <QtXml/qdom.h> +#include "qdbusmacros.h" +#include "qdbusintrospection.h" + +class QDBusConnectionPrivate; +class QDBusObjectPrivate; + +/** + * @internal + */ +class QDBusXmlParser +{ + QString m_service; + QString m_path; + QDomElement m_node; + QDBusConnectionPrivate* m_store; + +public: + QDBusXmlParser(const QString& service, const QString& path, + const QString& xmlData, QDBusConnectionPrivate* store = 0); + QDBusXmlParser(const QString& service, const QString& path, + const QDomElement& node, QDBusConnectionPrivate* store = 0); + + QDBusIntrospection::Interfaces interfaces() const; + QSharedDataPointer<QDBusIntrospection::Object> object() const; + QSharedDataPointer<QDBusIntrospection::ObjectTree> objectTree() const; + + static void parse(const QDBusObjectPrivate* d, const QString &xml); +}; + +#endif |