Merge the changes to the bindings from the KDE Subversion server.
authorThiago Macieira <thiago@kde.org>
Wed, 15 Feb 2006 16:25:12 +0000 (16:25 +0000)
committerThiago Macieira <thiago@kde.org>
Wed, 15 Feb 2006 16:25:12 +0000 (16:25 +0000)
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.

36 files changed:
qt/Makefile.am
qt/README [new file with mode: 0644]
qt/qdbusabstractadaptor.cpp [new file with mode: 0644]
qt/qdbusabstractadaptor.h [new file with mode: 0644]
qt/qdbusconnection.cpp
qt/qdbusconnection.h
qt/qdbusconnection_p.h
qt/qdbuserror.cpp
qt/qdbuserror.h
qt/qdbusintegrator.cpp
qt/qdbusinterface.cpp [new file with mode: 0644]
qt/qdbusinterface.h [new file with mode: 0644]
qt/qdbusinterface_p.h [new file with mode: 0644]
qt/qdbusintrospection.cpp [new file with mode: 0644]
qt/qdbusintrospection.h [new file with mode: 0644]
qt/qdbusmacros.h [new file with mode: 0644]
qt/qdbusmarshall.cpp
qt/qdbusmarshall.h
qt/qdbusmessage.cpp
qt/qdbusmessage.h
qt/qdbusmessage_p.h
qt/qdbusobject.cpp [new file with mode: 0644]
qt/qdbusobject.h [new file with mode: 0644]
qt/qdbusobject_p.h [new file with mode: 0644]
qt/qdbusserver.cpp
qt/qdbusserver.h
qt/qdbusstandardinterfaces.cpp [new file with mode: 0644]
qt/qdbusstandardinterfaces.h [new file with mode: 0644]
qt/qdbusthread.cpp [new file with mode: 0644]
qt/qdbustype.cpp [new file with mode: 0644]
qt/qdbustype.h [new file with mode: 0644]
qt/qdbusutil.cpp [new file with mode: 0644]
qt/qdbusutil.h [new file with mode: 0644]
qt/qdbusvariant.h
qt/qdbusxmlparser.cpp [new file with mode: 0644]
qt/qdbusxmlparser_p.h [new file with mode: 0644]

index 9247ba1..9117d27 100644 (file)
@@ -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 (file)
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 (file)
index 0000000..335469e
--- /dev/null
@@ -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 (file)
index 0000000..49535f5
--- /dev/null
@@ -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
index f5e1bf3..8ac13e4 100644 (file)
@@ -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
  *
  * 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"
index bbab0ec..c6913b2 100644 (file)
@@ -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
  *
  * 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
index 3f78dad..fa0fdd8 100644 (file)
@@ -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.
  *
  */
 
 #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
index 0f7dc92..64b68b3 100644 (file)
@@ -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
  *
  * 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)
 {
index 07d2c56..9450088 100644 (file)
@@ -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
  *
  * 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; }
index 4a19d33..79baa10 100644 (file)
@@ -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
  *
  * 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 (file)
index 0000000..36354fc
--- /dev/null
@@ -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 (file)
index 0000000..2b96cae
--- /dev/null
@@ -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 (file)
index 0000000..52fa800
--- /dev/null
@@ -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 (file)
index 0000000..e1183b7
--- /dev/null
@@ -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 (file)
index 0000000..4e1a874
--- /dev/null
@@ -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 (file)
index 0000000..e36bfb7
--- /dev/null
@@ -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
index 31c3589..19de0d9 100644 (file)
@@ -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
  *
  * 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()));
 }
 
index 4fc1a1a..6ec8cae 100644 (file)
@@ -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.
  *
  */
 
 
 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);
 };
 
index a77c22f..5c604c1 100644 (file)
@@ -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
  *
  * 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
index 03a475e..6c48044 100644 (file)
@@ -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
  *
  * 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;
 
index 9655c9d..9c48b08 100644 (file)
@@ -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
  *
  * 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 (file)
index 0000000..139154e
--- /dev/null
@@ -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 (file)
index 0000000..ffb41ad
--- /dev/null
@@ -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 (file)
index 0000000..181fceb
--- /dev/null
@@ -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
index 08fea26..b3b9835 100644 (file)
@@ -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.
  *
  */
 
index bc191a2..5560786 100644 (file)
@@ -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 (file)
index 0000000..f8ea7e1
--- /dev/null
@@ -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 (file)
index 0000000..af2c8dd
--- /dev/null
@@ -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 (file)
index 0000000..f45a009
--- /dev/null
@@ -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 (file)
index 0000000..036bbe1
--- /dev/null
@@ -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 (file)
index 0000000..c26d7c8
--- /dev/null
@@ -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 (file)
index 0000000..e10b302
--- /dev/null
@@ -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 (file)
index 0000000..4c3bac6
--- /dev/null
@@ -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
index bd18bef..eb7e3aa 100644 (file)
@@ -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
  *
  * 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 (file)
index 0000000..9d80fe2
--- /dev/null
@@ -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 (file)
index 0000000..c4c45be
--- /dev/null
@@ -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