dbus/qt/src .cvsignore, NONE, 1.1 Makefile.am, NONE, 1.1 qdbus.h, NONE, 1.1 qdbusabstractadaptor.cpp, NONE, 1.1 qdbusabstractadaptor.h, NONE, 1.1 qdbusabstractadaptor_p.h, NONE, 1.1 qdbusabstractinterface.cpp, NONE, 1.1 qdbusabstractinterface.h, NONE, 1.1 qdbusabstractinterface_p.h, NONE, 1.1 qdbusbus.cpp, NONE, 1.1 qdbusbus.h, NONE, 1.1 qdbusconnection.cpp, NONE, 1.1 qdbusconnection.h, NONE, 1.1 qdbusconnection_p.h, NONE, 1.1 qdbuserror.cpp, NONE, 1.1 qdbuserror.h, NONE, 1.1 qdbusintegrator.cpp, NONE, 1.1 qdbusinterface.cpp, NONE, 1.1 qdbusinterface.h, NONE, 1.1 qdbusinterface_p.h, NONE, 1.1 qdbusinternalfilters.cpp, NONE, 1.1 qdbusintrospection.cpp, NONE, 1.1 qdbusintrospection_p.h, NONE, 1.1 qdbusmacros.h, NONE, 1.1 qdbusmarshall.cpp, NONE, 1.1 qdbusmarshall_p.h, NONE, 1.1 qdbusmessage.cpp, NONE, 1.1 qdbusmessage.h, NONE, 1.1 qdbusmessage_p.h, NONE, 1.1 qdbusmetaobject.cpp, NONE, 1.1 qdbusmetaobject_p.h, NONE, 1.1 qdbusmisc.cpp, NONE, 1.1 qdbusreply.h, NONE, 1.1 qdbusserver.cpp, NONE, 1.1 qdbusserver.h, NONE, 1.1 qdbusthread.cpp, NONE, 1.1 qdbustype.cpp, NONE, 1.1 qdbustype_p.h, NONE, 1.1 qdbustypehelper_p.h, NONE, 1.1 qdbusutil.cpp, NONE, 1.1 qdbusutil.h, NONE, 1.1 qdbusxmlgenerator.cpp, NONE, 1.1 qdbusxmlparser.cpp, NONE, 1.1 qdbusxmlparser_p.h, NONE, 1.1

Thiago J. Macieira thiago at kemper.freedesktop.org
Sun Jun 4 08:52:07 PDT 2006


Update of /cvs/dbus/dbus/qt/src
In directory kemper:/tmp/cvs-serv5955/qt/src

Added Files:
	.cvsignore Makefile.am qdbus.h qdbusabstractadaptor.cpp 
	qdbusabstractadaptor.h qdbusabstractadaptor_p.h 
	qdbusabstractinterface.cpp qdbusabstractinterface.h 
	qdbusabstractinterface_p.h qdbusbus.cpp qdbusbus.h 
	qdbusconnection.cpp qdbusconnection.h qdbusconnection_p.h 
	qdbuserror.cpp qdbuserror.h qdbusintegrator.cpp 
	qdbusinterface.cpp qdbusinterface.h qdbusinterface_p.h 
	qdbusinternalfilters.cpp qdbusintrospection.cpp 
	qdbusintrospection_p.h qdbusmacros.h qdbusmarshall.cpp 
	qdbusmarshall_p.h qdbusmessage.cpp qdbusmessage.h 
	qdbusmessage_p.h qdbusmetaobject.cpp qdbusmetaobject_p.h 
	qdbusmisc.cpp qdbusreply.h qdbusserver.cpp qdbusserver.h 
	qdbusthread.cpp qdbustype.cpp qdbustype_p.h 
	qdbustypehelper_p.h qdbusutil.cpp qdbusutil.h 
	qdbusxmlgenerator.cpp qdbusxmlparser.cpp qdbusxmlparser_p.h 
Log Message:
	* qt/: Update to Subversion r548032.
	This includes a big reorganisation of the files inside the
	subdir.

We really need a version control system that supports moving of
files. I'm not bothering with history anyways anymore, since the
bindings will be moved out to git. The history should be restored from
Subversion when that happens.


--- NEW FILE: .cvsignore ---
.deps
.libs
Makefile
Makefile.in
*.lo
*.la
*.bb
*.bbg
*.da
*.gcov
*.moc

--- NEW FILE: Makefile.am ---
INCLUDES=-I$(top_srcdir) $(DBUS_CLIENT_CFLAGS) $(DBUS_QT_CFLAGS) -DDBUS_COMPILATION

lib_LTLIBRARIES=libdbus-qt4-1.la

dbusincludedir=$(includedir)/dbus-1.0/dbus
dbusinclude_HEADERS=    \
        qdbusbus.h      \
        qdbusmacros.h   \
        qdbuserror.h    \
        qdbusmessage.h  \
        qdbusserver.h   \
        qdbusconnection.h \
        qdbusabstractinterface.h \
        qdbusinterface.h \
        qdbusutil.h     \
        qdbusabstractadaptor.h \
        qdbusreply.h    \
        qdbustypehelper_p.h

noinst_HEADERS=		\
	qdbusabstractadaptor_p.h \
	qdbusabstractinterface_p.h \
	qdbusconnection_p.h    \
	qdbusinterface_p.h     \
	qdbusintrospection_p.h \
	qdbusmarshall_p.h      \
	qdbusmessage_p.h       \
	qdbusmetaobject_p.h    \
	qdbustype_p.h		\
	qdbusxmlparser_p.h

MOCS = qdbusabstractadaptor.moc qdbusserver.moc qdbusconnection_p.moc qdbusconnection_p.moc qdbusabstractadaptor_p.moc qdbusbus.moc qdbusabstractinterface.moc
CLEANFILES = $(MOCS)
BUILT_SOURCES = $(MOCS)

libdbus_qt4_1_la_SOURCES = \
        qdbusbus.cpp            \
        qdbusconnection.cpp     \
        qdbuserror.cpp          \
        qdbusintegrator.cpp     \
        qdbusmarshall.cpp       \
        qdbusmessage.cpp        \
        qdbusserver.cpp         \
        qdbustype.cpp           \
        qdbusabstractinterface.cpp	\
        qdbusinterface.cpp      \
        qdbusxmlparser.cpp      \
        qdbusutil.cpp           \
        qdbusintrospection.cpp  \
        qdbusabstractadaptor.cpp	\
        qdbusthread.cpp         \
        qdbusinternalfilters.cpp	\
        qdbusmetaobject.cpp	\
	qdbusmisc.cpp		\
	qdbusxmlgenerator.cpp

qdbusabstractadaptor.lo: qdbusabstractadaptor.moc qdbusabstractadaptor_p.moc
qdbusabstractinterface.lo: qdbusabstractinterface.moc
qdbusbus.lo: qdbusbus.moc
qdbusserver.lo: qdbusserver.moc
qdbusintegrator.lo: qdbusconnection_p.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
libdbus_qt4_1_la_CPPFLAGS= -DQDBUS_MAKEDLL

EXTRA_DIST = qt-dbus.qdocconf

%.moc: %.h
	$(QT_MOC) $< > $@

--- NEW FILE: qdbus.h ---
/* qdbus.h precompiled header
 *
 * Copyright (C) 2005 Harald Fernengel <harry at 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 QDBUS_H
#define QDBUS_H

#include <QtCore/qglobal.h>

#if !defined(DBUS_COMPILATION)
# include <dbus/qdbusabstractadaptor.h>
# include <dbus/qdbusabstractinterface.h>
# include <dbus/qdbusbus.h>
# include <dbus/qdbusconnection.h>
# include <dbus/qdbuserror.h>
# include <dbus/qdbusinterface.h>
# include <dbus/qdbusmessage.h>
# include <dbus/qdbusreply.h>
# include <dbus/qdbusserver.h>
# include <dbus/qdbusutil.h>
#else
# include "qdbusabstractadaptor.h"
# include "qdbusabstractinterface.h"
# include "qdbusbus.h"
# include "qdbusconnection.h"
# include "qdbuserror.h"
# include "qdbusinterface.h"
# include "qdbusmessage.h"
# include "qdbusreply.h"
# include "qdbusserver.h"
# include "qdbusutil.h"
#endif

#endif

--- NEW FILE: qdbusabstractadaptor.cpp ---
/* -*- mode: C++ -*-
 *
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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>

#include "qdbusconnection.h"

#include "qdbusconnection_p.h"  // for qDBusParametersForMethod
#include "qdbusabstractadaptor_p.h"

struct QDBusAdaptorInit
{
    QSignalSpyCallbackSet callbacks;
    QDBusAdaptorInit()
    {
        extern void qt_register_signal_spy_callbacks(const QSignalSpyCallbackSet &callback_set);
        callbacks.signal_begin_callback = QDBusAdaptorConnector::signalBeginCallback;
        callbacks.signal_end_callback = QDBusAdaptorConnector::signalEndCallback;
        callbacks.slot_begin_callback = 0;
        callbacks.slot_end_callback = 0;
        qt_register_signal_spy_callbacks(callbacks);
        
        //QDBusAdaptorConnector::id = QObject::registerUserData();
    }
};

Q_GLOBAL_STATIC(QDBusAdaptorInit, qAdaptorInit)

QDBusAdaptorConnector *qDBusFindAdaptorConnector(QObject *obj)
{
    (void)qAdaptorInit();

    if (!obj)
        return 0;
    QDBusAdaptorConnector *connector = qFindChild<QDBusAdaptorConnector *>(obj);
    if (connector)
        connector->polish();
    return connector;
}

QDBusAdaptorConnector *qDBusFindAdaptorConnector(QDBusAbstractAdaptor *adaptor)
{
    return qDBusFindAdaptorConnector(adaptor->parent());
}

QDBusAdaptorConnector *qDBusCreateAdaptorConnector(QObject *obj)
{
    (void)qAdaptorInit();

    QDBusAdaptorConnector *connector = qDBusFindAdaptorConnector(obj);
    if (connector)
        return connector;
    return new QDBusAdaptorConnector(obj);
}

QString QDBusAbstractAdaptorPrivate::retrieveIntrospectionXml(QDBusAbstractAdaptor *adaptor)
{
    return adaptor->d->xml;
}

void QDBusAbstractAdaptorPrivate::saveIntrospectionXml(QDBusAbstractAdaptor *adaptor,
                                                       const QString &xml)
{
    adaptor->d->xml = xml;
}

/*!
    \page usingannotations.html
    \title Using annotations in adaptors

    It is currently not possible to specify arbitrary annotations in adaptors.
*/

/*!
    \class QDBusAbstractAdaptor
    \brief Abstract adaptor for D-Bus adaptor classes.

    The QDBusAbstractAdaptor class is the starting point for all objects intending to provide
    interfaces to the external world using D-Bus. This is accomplished by attaching a one or more
    classes derived from QDBusAbstractAdaptor to a normal QObject and then registering that QObject
    with QDBusConnection::registerObject. QDBusAbstractAdaptor objects are intended to be
    light-weight wrappers, mostly just relaying calls into the real object (see object()) and the
    signals from it.

    Each QDBusAbstractAdaptor-derived class should define the D-Bus interface it is implementing
    using the Q_CLASSINFO macro in the class definition.

    QDBusAbstractAdaptor uses the standard QObject mechanism of signals, slots and properties to
    determine what signals, methods and properties to export to the bus. Any signal emitted by
    QDBusAbstractAdaptor-derived classes will be automatically be relayed through any D-Bus
    connections the object is registered on.

    Classes derived from QDBusAbstractAdaptor must be created on the heap using the \a new operator
    and must not be deleted by the user (they will be deleted automatically when the object they are
    connected to is also deleted).

    \sa {usingadaptors.html}{Using adaptors}, QDBusConnection
*/

/*!
    Constructs a QDBusAbstractAdaptor with \a parent as the object we refer to.
*/
QDBusAbstractAdaptor::QDBusAbstractAdaptor(QObject* parent)
    : QObject(parent), d(new QDBusAbstractAdaptorPrivate)
{
    QDBusAdaptorConnector *connector = qDBusCreateAdaptorConnector(parent);

    connector->waitingForPolish = true;
    QTimer::singleShot(0, connector, SLOT(polish()));
}

/*!
    Destroys the adaptor.

    \warning Adaptors are destroyed automatically when the real object they refer to is
             destroyed. Do not delete the adaptors yourself.
*/
QDBusAbstractAdaptor::~QDBusAbstractAdaptor()
{
    delete d;
}

/*!
    Returns the QObject that we're the adaptor for. This is the same object that was passed as an
    argument to the QDBusAbstractAdaptor constructor.
*/
QObject* QDBusAbstractAdaptor::object() const
{
    return parent();
}

/*!
    Toggles automatic signal relaying from the real object (see object()).

    Automatic signal relaying consists of signal-to-signal connection of the signals on the parent
    that have the exact same method signatue in both classes.

    If \a enable is set to true, connect the signals; if set to false, disconnect all signals.
*/
void QDBusAbstractAdaptor::setAutoRelaySignals(bool enable)
{
    const QMetaObject *us = metaObject();
    const QMetaObject *them = parent()->metaObject();
    for (int idx = staticMetaObject.methodCount(); idx < us->methodCount(); ++idx) {
        QMetaMethod mm = us->method(idx);

        if (mm.methodType() != QMetaMethod::Signal)
            continue;
        
        // try to connect/disconnect to a signal on the parent that has the same method signature
        QByteArray sig = QMetaObject::normalizedSignature(mm.signature());
        if (them->indexOfSignal(sig) == -1)
            continue;
        sig.prepend(QSIGNAL_CODE + '0');
        parent()->disconnect(sig, this, sig);
        if (enable)
            connect(parent(), sig, sig);
    }
}

QDBusAdaptorConnector::QDBusAdaptorConnector(QObject *parent)
    : QObject(parent), waitingForPolish(false), lastSignalIdx(0), argv(0)
{
}

QDBusAdaptorConnector::~QDBusAdaptorConnector()
{
}

void QDBusAdaptorConnector::addAdaptor(QDBusAbstractAdaptor *adaptor)
{
    // find the interface name
    const QMetaObject *mo = adaptor->metaObject();
    while (mo != &QDBusAbstractAdaptor::staticMetaObject) {
        int ciend = mo->classInfoCount();
        for (int i = mo->classInfoOffset(); i < ciend; ++i) {
            QMetaClassInfo mci = mo->classInfo(i);
            if (strcmp(mci.name(), QCLASSINFO_DBUS_INTERFACE) == 0 && *mci.value()) {
                // find out if this interface exists first
                QString interface = QString::fromUtf8(mci.value());
                AdaptorMap::Iterator it = qLowerBound(adaptors.begin(), adaptors.end(), interface);
                if (it != adaptors.end() && it->interface == interface) {
                    // exists. Replace it (though it's probably the same)
                    it->adaptor = adaptor;
                    it->metaObject = mo;
                } else {
                    // create a new one
                    AdaptorData entry;
                    entry.interface = interface;
                    entry.adaptor = adaptor;
                    entry.metaObject = mo;
                    adaptors << entry;
                }
            }
        }

        mo = mo->superClass();
    }
        
    // connect the adaptor's signals to our relaySlot slot
    mo = adaptor->metaObject();
    for (int i = QDBusAbstractAdaptor::staticMetaObject.methodCount();
         i < mo->methodCount(); ++i) {
        QMetaMethod mm = mo->method(i);

        if (mm.methodType() != QMetaMethod::Signal)
            continue;

        QByteArray sig = mm.signature();
        sig.prepend(QSIGNAL_CODE + '0');
        disconnect(adaptor, sig, this, SLOT(relaySlot()));
        connect(adaptor, sig, this, SLOT(relaySlot()));
    }
}

void QDBusAdaptorConnector::polish()
{
    if (!waitingForPolish)
        return;                 // avoid working multiple times if multiple adaptors were added

    waitingForPolish = false;
    const QObjectList &objs = parent()->children();
    foreach (QObject *obj, objs) {
        QDBusAbstractAdaptor *adaptor = qobject_cast<QDBusAbstractAdaptor *>(obj);
        if (adaptor)
            addAdaptor(adaptor);
    }

    // sort the adaptor list
    qSort(adaptors);
}

void QDBusAdaptorConnector::relaySlot()
{
    relay(sender());
}

void QDBusAdaptorConnector::relay(QObject *sender)
{
    // we're being called because there is a signal being emitted that we must relay
    Q_ASSERT(lastSignalIdx);
    Q_ASSERT(argv);
    Q_ASSERT(senderMetaObject);

    if (senderMetaObject != sender->metaObject()) {
        qWarning("Inconsistency detected: QDBusAdaptorConnector::relay got called with unexpected sender object!");
    } else {
        QMetaMethod mm = senderMetaObject->method(lastSignalIdx);
        QObject *object = static_cast<QDBusAbstractAdaptor *>(sender)->parent();

        // break down the parameter list
        QList<int> types;
        int inputCount = qDBusParametersForMethod(mm, types);
        if (inputCount == -1)
            // invalid signal signature
            // qDBusParametersForMethod has already complained
            return;
        if (inputCount + 1 != types.count() ||
            types.at(inputCount) == QDBusConnectionPrivate::messageMetaType) {
            // invalid signal signature
            // qDBusParametersForMethod has not yet complained about this one
            qWarning("Cannot relay signal %s::%s", senderMetaObject->className(), mm.signature());
            return;
        }

        QByteArray signature = QMetaObject::normalizedSignature(mm.signature());
        signature.truncate(signature.indexOf('(')); // remove parameter decoration

        QVariantList args;
        for (int i = 1; i < types.count(); ++i)
            args << QVariant(types.at(i), argv[i]);

        // find all the interfaces this signal belongs to
        for (const QMetaObject *mo = senderMetaObject; mo != &QDBusAbstractAdaptor::staticMetaObject;
             mo = mo->superClass()) {
            if (lastSignalIdx < mo->methodOffset())
                break;

            for (int i = mo->classInfoOffset(); i < mo->classInfoCount(); ++i) {
                QMetaClassInfo mci = mo->classInfo(i);
                if (qstrcmp(mci.name(), QCLASSINFO_DBUS_INTERFACE) == 0 && *mci.value())
                    // now emit the signal with all the information
                    emit relaySignal(object, mci.value(), signature.constData(), args);
            }
        }
    }
}

void QDBusAdaptorConnector::signalBeginCallback(QObject *caller, int method_index, void **argv)
{
    QDBusAbstractAdaptor *adaptor = qobject_cast<QDBusAbstractAdaptor *>(caller);
    if (adaptor) {
        QDBusAdaptorConnector *data = qDBusFindAdaptorConnector(adaptor);
        data->lastSignalIdx = method_index;
        data->argv = argv;
        data->senderMetaObject = caller->metaObject();
        data->polish();         // make sure it's polished
    }
}

void QDBusAdaptorConnector::signalEndCallback(QObject *caller, int)
{
    QDBusAbstractAdaptor *adaptor = qobject_cast<QDBusAbstractAdaptor *>(caller);
    if (adaptor) {
        QDBusAdaptorConnector *data = qDBusFindAdaptorConnector(adaptor);
        data->lastSignalIdx = 0;
        data->argv = 0;
        data->senderMetaObject = 0;
    }
}

#include "qdbusabstractadaptor.moc"
#include "qdbusabstractadaptor_p.moc"

--- NEW FILE: qdbusabstractadaptor.h ---
/* -*- mode: C++ -*-
 *
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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 QDBusAbstractAdaptorPrivate;
class QDBUS_EXPORT QDBusAbstractAdaptor: public QObject
{
    Q_OBJECT
protected:
    QDBusAbstractAdaptor(QObject *parent);

public:
    ~QDBusAbstractAdaptor();

    Q_DECL_DEPRECATED QObject *object() const;

protected:
    void setAutoRelaySignals(bool enable);

private:
    friend class QDBusAbstractAdaptorPrivate;
    QDBusAbstractAdaptorPrivate *d;
};

#endif

--- NEW FILE: qdbusabstractadaptor_p.h ---
/* -*- mode: C++; set-fill-width: 100 -*-
 *
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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 QDBUSABSTRACTADAPTORPRIVATE_H
#define QDBUSABSTRACTADAPTORPRIVATE_H

#include <QtCore/qobject.h>
#include <QtCore/qmap.h>
#include <QtCore/qhash.h>
#include <QtCore/qreadwritelock.h>
#include <QtCore/qvariant.h>
#include <QtCore/qvector.h>

#define QCLASSINFO_DBUS_INTERFACE       "D-Bus Interface"
#define QCLASSINFO_DBUS_INTROSPECTION   "D-Bus Introspection"

class QDBusAbstractAdaptor;
class QDBusAdaptorConnector;
class QDBusAdaptorManager;
class QDBusConnectionPrivate;

#if QT_VERSION < 0x040200
/* mirrored in qobject_p.h, DON'T CHANGE without prior warning */
struct QSignalSpyCallbackSet
{
    typedef void (*BeginCallback)(QObject *caller, int method_index, void **argv);
    typedef void (*EndCallback)(QObject *caller, int method_index);
    BeginCallback signal_begin_callback,
                    slot_begin_callback;
    EndCallback signal_end_callback,
                slot_end_callback;
};
#else
# error Qt 4.2.0 is supposed to have a better solution!
  CHOKE!
#endif  // Qt 4.2.0

class QDBusAbstractAdaptorPrivate
{
public:
    QString xml;

    static QString retrieveIntrospectionXml(QDBusAbstractAdaptor *adaptor);
    static void saveIntrospectionXml(QDBusAbstractAdaptor *adaptor, const QString &xml);
};

class QDBusAdaptorConnector: public QObject
{
    Q_OBJECT
public: // typedefs
    struct AdaptorData
    {
        QString interface;
        QDBusAbstractAdaptor *adaptor;
        const QMetaObject *metaObject;

        inline bool operator<(const AdaptorData &other) const
        { return interface < other.interface; }
        inline bool operator<(const QString &other) const
        { return interface < other; }
    };
    typedef QVector<AdaptorData> AdaptorMap;

public: // methods
    explicit QDBusAdaptorConnector(QObject *parent);
    ~QDBusAdaptorConnector();

    void addAdaptor(QDBusAbstractAdaptor *adaptor);
    void relay(QObject *sender);

public slots:
    void relaySlot();
    void polish();

signals:
    void relaySignal(QObject *obj, const char *interface, const char *name, const QVariantList &args);

public: // member variables
    AdaptorMap adaptors;
    bool waitingForPolish : 1;

    int lastSignalIdx;
    void **argv;
    const QMetaObject *senderMetaObject;

public: // static members
    static void signalBeginCallback(QObject *caller, int method_index, void **argv);
    static void signalEndCallback(QObject *caller, int method_index);
    //static int id;
};

extern QDBusAdaptorConnector *qDBusFindAdaptorConnector(QObject *object);
extern QDBusAdaptorConnector *qDBusCreateAdaptorConnector(QObject *object);

#endif // QDBUSABSTRACTADAPTORPRIVATE_H

--- NEW FILE: qdbusabstractinterface.cpp ---
/* -*- C++ -*-
 *
 * Copyright (C) 2005 Thiago Macieira <thiago at kde.org>
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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 "qdbusabstractinterface.h"

#include "qdbusabstractinterface_p.h"
#include "qdbusmetaobject_p.h"
#include "qdbusconnection_p.h"

QVariant QDBusAbstractInterfacePrivate::property(const QMetaProperty &mp) const
{
    // try to read this property
    QDBusMessage msg = QDBusMessage::methodCall(service, path, DBUS_INTERFACE_PROPERTIES,
                                                QLatin1String("Get"));
    msg << interface << QString::fromUtf8(mp.name());
    QDBusMessage reply = connp->sendWithReply(msg, QDBusConnection::NoUseEventLoop);

    if (reply.type() == QDBusMessage::ReplyMessage && reply.count() == 1 &&
        reply.signature() == QLatin1String("v")) {
        QVariant value = QDBusTypeHelper<QVariant>::fromVariant(reply.at(0));

        // make sure the type is right
        if (qstrcmp(mp.typeName(), value.typeName()) == 0) {
            if (mp.type() == QVariant::LastType)
                // QVariant is special in this context
                return QDBusTypeHelper<QVariant>::fromVariant(value);

            return value;
        }
    }

    // there was an error...
    if (reply.type() == QDBusMessage::ErrorMessage)
        lastError = reply;
    else if (reply.signature() != QLatin1String("v")) {
        QString errmsg = QLatin1String("Invalid signature `%1' in return from call to "
                                       DBUS_INTERFACE_PROPERTIES);
        lastError = QDBusError(QDBusError::InvalidSignature, errmsg.arg(reply.signature()));
    } else {
        QString errmsg = QLatin1String("Unexpected type `%1' when retrieving property "
                                       "`%2 %3.%4'");
        lastError = QDBusError(QDBusError::InvalidSignature,
                               errmsg.arg(QLatin1String(reply.at(0).typeName()),
                                          QLatin1String(mp.typeName()),
                                          interface, QString::fromUtf8(mp.name())));
    }

    return QVariant();
}

void QDBusAbstractInterfacePrivate::setProperty(const QMetaProperty &mp, const QVariant &value)
{
    // send the value
    QDBusMessage msg = QDBusMessage::methodCall(service, path, DBUS_INTERFACE_PROPERTIES,
                                                QLatin1String("Set"));
    msg.setSignature(QLatin1String("ssv"));
    msg << interface << QString::fromUtf8(mp.name()) << value;
    QDBusMessage reply = connp->sendWithReply(msg, QDBusConnection::NoUseEventLoop);

    if (reply.type() != QDBusMessage::ReplyMessage)
        lastError = reply;
}    

/*!
    \class QDBusAbstractInterface
    \brief Base class for all D-Bus interfaces in the QtDBus binding, allowing access to remote interfaces.

    Generated-code classes also derive from QDBusAbstractInterface, all methods described here are also
    valid for generated-code classes. In addition to those described here, generated-code classes
    provide member functions for the remote methods, which allow for compile-time checking of the
    correct parameters and return values, as well as property type-matching and signal
    parameter-matching.

    \sa {dbusidl2cpp.html}{The dbusidl2cpp compiler}, QDBusInterface
*/

/*!
    \enum QDBusAbstractInterface::CallMode

    Specifies how a call should be placed. The valid options are:
    \value NoWaitForReply       place the call but don't wait for the reply (the reply's contents
                                will be discarded)
    \value NoUseEventLoop       don't use an event loop to wait for a reply, but instead block on
                                network operations while waiting. This option means the
                                user-interface may not be updated for the duration of the call.
    \value UseEventLoop         use the Qt event loop to wait for a reply. This option means the
                                user-interface will update, but it also means other events may
                                happen, like signal delivery and other D-Bus method calls.

    When using UseEventLoop, applications must be prepared for reentrancy in any function.
*/

/*!
    \internal
*/
QDBusAbstractInterface::QDBusAbstractInterface(QDBusAbstractInterfacePrivate* d)
#if QT_VERSION < 0x040200
    : d_ptr(d)
{
    d_ptr->q_ptr = this;
}
#endif

/*!
    Releases this object's resources.
*/
QDBusAbstractInterface::~QDBusAbstractInterface()
{
    delete d_ptr;
}

/*!
    Returns true if this is a valid reference to a remote object. It returns false if
    there was an error during the creation of this interface (for instance, if the remote
    application does not exist).

    Note: when dealing with remote objects, it is not always possible to determine if it
    exists when creating a QDBusInterface or QDBusInterfacePtr object.
*/
bool QDBusAbstractInterface::isValid() const
{
    return d_func()->isValid;
}

/*!
    Returns the connection this interface is assocated with.
*/
QDBusConnection QDBusAbstractInterface::connection() const
{
    return d_func()->conn;
}

/*!
    Returns the name of the service this interface is associated with.
*/
QString QDBusAbstractInterface::service() const
{
    return d_func()->service;
}

/*!
    Returns the object path that this interface is associated with.
*/
QString QDBusAbstractInterface::path() const
{
    return d_func()->path;
}

/*!
    Returns the name of this interface.
*/
QString QDBusAbstractInterface::interface() const
{
    return d_func()->interface;
}

/*!
    Returns the error the last operation produced, or an invalid error if the last operation did not
    produce an error.
*/
QDBusError QDBusAbstractInterface::lastError() const
{
    return d_func()->lastError;
}

/*!
    \threadsafe
    Places a call to the remote method specified by \a method on this interface, using \a args as
    arguments. This function returns the message that was received as a reply, which can be a normal
    QDBusMessage::ReplyMessage (indicating success) or QDBusMessage::ErrorMessage (if the call
    failed). The \a mode parameter specifies how this call should be placed.

    If the call succeeds, lastError() will be cleared; otherwise, it will contain the error this
    call produced.

    Normally, you should place calls using call().

    \warning If you use \c UseEventLoop, your code must be prepared to deal with any reentrancy:
             other method calls and signals may be delivered before this function returns, as well
             as other Qt queued signals and events.
*/
QDBusMessage QDBusAbstractInterface::callWithArgs(const QString& method, const QList<QVariant>& args,
                                          CallMode mode)
{
    Q_D(QDBusAbstractInterface);

    QString m = method, sig;
    // split out the signature from the method
    int pos = method.indexOf(QLatin1Char('.'));
    if (pos != -1) {
        m.truncate(pos);
        sig = method.mid(pos + 1);
    }

    if (mode == AutoDetect) {
        // determine if this a sync or async call
        mode = NoUseEventLoop;
        const QMetaObject *mo = metaObject();
        QByteArray match = method.toLatin1() + '(';

        for (int i = staticMetaObject.methodCount(); i < mo->methodCount(); ++i) {
            QMetaMethod mm = mo->method(i);
            if (QByteArray(mm.signature()).startsWith(match)) {
                // found a method with the same name as what we're looking for
                // hopefully, nobody is overloading asynchronous and synchronous methods with
                // the same name

                QList<QByteArray> tags = QByteArray(mm.tag()).split(' ');
                if (tags.contains("async") || tags.contains("Q_ASYNC"))
                    mode = NoWaitForReply;

                break;
            }
        }
    }

    QDBusMessage msg = QDBusMessage::methodCall(service(), path(), interface(), m);
    msg.setSignature(sig);
    msg.QList<QVariant>::operator=(args);

    QDBusMessage reply;
    if (mode != NoWaitForReply)
        reply = d->conn.sendWithReply(msg, mode == UseEventLoop ?
                                      QDBusConnection::UseEventLoop : QDBusConnection::NoUseEventLoop);
    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;
}

/*!
    \overload
    Places a call to the remote method specified by \a method on this interface, using \a args as
    arguments. This function will return immediately after queueing the call. The reply from the
    remote function or any errors emitted by it will be delivered to the \a slot slot on object \a
    receiver.

    This function returns true if the queueing succeeded: it does not indicate that the call
    succeeded. If it failed, the slot will be called with an error message. lastError() will not be
    set under those circumstances.

    \sa QDBusError, QDBusMessage
*/
bool QDBusAbstractInterface::callWithArgs(const QString &method, QObject *receiver, const char *slot,
                                          const QList<QVariant> &args)
{
    Q_D(QDBusAbstractInterface);
    
    QString m = method, sig;
    // split out the signature from the method
    int pos = method.indexOf(QLatin1Char('.'));
    if (pos != -1) {
        m.truncate(pos);
        sig = method.mid(pos + 1);
    }

    QDBusMessage msg = QDBusMessage::methodCall(service(), path(), interface(), m);
    msg.setSignature(sig);
    msg.QList<QVariant>::operator=(args);

    d->lastError = 0;           // clear
    return d->conn.sendWithReplyAsync(msg, receiver, slot);
}

/*!
    \internal
    Catch signal connections.
*/
void QDBusAbstractInterface::connectNotify(const char *signal)
{
    // someone connecting to one of our signals
    Q_D(QDBusAbstractInterface);

    d->connp->connectRelay(d->service, d->path, d->interface, this, signal);
}

/*!
    \internal
    Catch signal disconnections.
*/
void QDBusAbstractInterface::disconnectNotify(const char *signal)
{
    // someone disconnecting from one of our signals
    Q_D(QDBusAbstractInterface);

    d->connp->disconnectRelay(d->service, d->path, d->interface, this, signal);
}

/*!
    \internal
    Get the value of the property \a propname.
*/
QVariant QDBusAbstractInterface::internalPropGet(const char *propname) const
{
    // assume this property exists and is readable
    // we're only called from generated code anyways

    int idx = metaObject()->indexOfProperty(propname);
    if (idx != -1)
        return d_func()->property(metaObject()->property(idx));
    qWarning("QDBusAbstractInterface::internalPropGet called with unknown property '%s'", propname);
    return QVariant();          // error
}

/*!
    \internal
    Set the value of the property \a propname to \a value.
*/
void QDBusAbstractInterface::internalPropSet(const char *propname, const QVariant &value)
{
    Q_D(QDBusAbstractInterface);

    // assume this property exists and is writeable
    // we're only called from generated code anyways

    int idx = metaObject()->indexOfProperty(propname);
    if (idx != -1)
        d->setProperty(metaObject()->property(idx), value);
    else
        qWarning("QDBusAbstractInterface::internalPropGet called with unknown property '%s'", propname);
}

/*!
    \overload
    \fn QDBusMessage QDBusAbstractInterface::call(const QString &method)

    Calls the method \a method on this interface and passes the parameters to this function to the
    method.

    The parameters to \c call are passed on to the remote function via D-Bus as input
    arguments. Output arguments are returned in the QDBusMessage reply. If the reply is an error
    reply, lastError() will also be set to the contents of the error message.

    This function is implemented by actually 9 different function overloads called \c call, so you
    can pass up to 8 parameters to your function call, which can be of any type accepted by QtDBus
    (see the \l {allowedparameters.html}{allowed parameters} page for information on what types are
    accepted).

    It can be used the following way:

    \code
      QString value = retrieveValue();
      QDBusMessage reply;

      QDBusReply<int> api = interface->call(QLatin1String("GetAPIVersion"));
      if (api >= 14)
        reply = interface->call(QLatin1String("ProcessWorkUnicode"), value);
      else
        reply = interface->call(QLatin1String("ProcessWork"), QLatin1String("UTF-8"), value.toUtf8());
    \endcode

    This example illustrates function calling with 0, 1 and 2 parameters and illustrates different
    parameter types passed in each (the first call to \c "ProcessWorkUnicode" will contain one
    Unicode string, the second call to \c "ProcessWork" will contain one string and one byte array).
        
    \warning This function reenters the Qt event loop in order to wait for the reply, excluding user
             input. During the wait, it may deliver signals and other method calls to your
             application. Therefore, it must be prepared to handle a reentrancy whenever a call is
             placed with call().
*/

#include "qdbusabstractinterface.moc"

--- NEW FILE: qdbusabstractinterface.h ---
/* -*- C++ -*-
 *
 * Copyright (C) 2005 Thiago Macieira <thiago at kde.org>
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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 QDBUSABSTRACTINTERFACE_H
#define QDBUSABSTRACTINTERFACE_H

#include <QtCore/qstring.h>
#include <QtCore/qvariant.h>
#include <QtCore/qlist.h>
#include <QtCore/qobject.h>

#include "qdbusmessage.h"
#include "qdbustypehelper_p.h"

class QDBusConnection;
class QDBusError;

class QDBusAbstractInterfacePrivate;
class QDBUS_EXPORT QDBusAbstractInterface: public QObject
{
    Q_OBJECT

public:
    enum CallMode {
        NoWaitForReply,
        UseEventLoop,
        NoUseEventLoop,
        AutoDetect
    };

public:
    virtual ~QDBusAbstractInterface();
    bool isValid() const;

    QDBusConnection connection() const;

    QString service() const;
    QString path() const;
    QString interface() const;

    QDBusError lastError() const;

    QDBusMessage callWithArgs(const QString &method, const QList<QVariant> &args = QList<QVariant>(),
                              CallMode mode = AutoDetect);
    bool callWithArgs(const QString &method, QObject *receiver, const char *slot,
                      const QList<QVariant> &args = QList<QVariant>());

    inline QDBusMessage call(const QString &m)
    {
        return callWithArgs(m);
    }

    inline QDBusMessage call(CallMode mode, const QString &m)
    {
        return callWithArgs(m, QList<QVariant>(), mode);
    }
    
#ifndef Q_QDOC
private:
    template<typename T> inline QVariant qvfv(const T &t)
    { return QDBusTypeHelper<T>::toVariant(t); }
    
public:
    template<typename T1>
    inline QDBusMessage call(const QString &m, const T1 &t1)
    {
        QList<QVariant> args;
        args << qvfv(t1);
        return callWithArgs(m, args);
    }

    template<typename T1, typename T2>
    inline QDBusMessage call(const QString &m, const T1 &t1, const T2 &t2)
    {
        QList<QVariant> args;
        args << qvfv(t1) << qvfv(t2);
        return callWithArgs(m, args);
    }

    template<typename T1, typename T2, typename T3>
    inline QDBusMessage call(const QString &m, const T1 &t1, const T2 &t2, const T3 &t3)
    {
        QList<QVariant> args;
        args << qvfv(t1) << qvfv(t2) << qvfv(t3);
        return callWithArgs(m, args);
    }
      
    template<typename T1, typename T2, typename T3, typename T4>
    inline QDBusMessage call(const QString &m, const T1 &t1, const T2 &t2, const T3 &t3,
                             const T4 &t4)
    {
        QList<QVariant> args;
        args << qvfv(t1) << qvfv(t2) << qvfv(t3)
             << qvfv(t4);
        return callWithArgs(m, args);
    }

    template<typename T1, typename T2, typename T3, typename T4, typename T5>
    inline QDBusMessage call(const QString &m, const T1 &t1, const T2 &t2, const T3 &t3,
                             const T4 &t4, const T5 &t5)
    {
        QList<QVariant> args;
        args << qvfv(t1) << qvfv(t2) << qvfv(t3)
             << qvfv(t4) << qvfv(t5);
        return callWithArgs(m, args);
    }
  
    template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
    inline QDBusMessage call(const QString &m, const T1 &t1, const T2 &t2, const T3 &t3,
                             const T4 &t4, const T5 &t5, const T6 &t6)
    {
        QList<QVariant> args;
        args << qvfv(t1) << qvfv(t2) << qvfv(t3)
             << qvfv(t4) << qvfv(t5) << qvfv(t6);
        return callWithArgs(m, args);
    }

    template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
    inline QDBusMessage call(const QString &m, const T1 &t1, const T2 &t2, const T3 &t3,
                             const T4 &t4, const T5 &t5, const T6 &t6, const T7 &t7)
    {
        QList<QVariant> args;
        args << qvfv(t1) << qvfv(t2) << qvfv(t3)
             << qvfv(t4) << qvfv(t5) << qvfv(t6)
             << qvfv(t7);
        return callWithArgs(m, args);
    }

    template<typename T1, typename T2, typename T3, typename T4, typename T5,
             typename T6, typename T7, typename T8>
    inline QDBusMessage call(const QString &m, const T1 &t1, const T2 &t2, const T3 &t3,
                             const T4 &t4, const T5 &t5, const T6 &t6, const T7 &t7, const T8 &t8)
    {
        QList<QVariant> args;
        args << qvfv(t1) << qvfv(t2) << qvfv(t3)
             << qvfv(t4) << qvfv(t5) << qvfv(t6)
             << qvfv(t7) << qvfv(t8);
        return callWithArgs(m, args);
    }

    template<typename T1>
    inline QDBusMessage call(CallMode mode, const QString &m, const T1 &t1)
    {
        QList<QVariant> args;
        args << qvfv(t1);
        return callWithArgs(m, args, mode);
    }

    template<typename T1, typename T2>
    inline QDBusMessage call(CallMode mode, const QString &m, const T1 &t1, const T2 &t2)
    {
        QList<QVariant> args;
        args << qvfv(t1) << qvfv(t2);
        return callWithArgs(m, args, mode);
    }

    template<typename T1, typename T2, typename T3>
    inline QDBusMessage call(CallMode mode, const QString &m, const T1 &t1, const T2 &t2,
                             const T3 &t3)
    {
        QList<QVariant> args;
        args << qvfv(t1) << qvfv(t2) << qvfv(t3);
        return callWithArgs(m, args, mode);
    }
      
    template<typename T1, typename T2, typename T3, typename T4>
    inline QDBusMessage call(CallMode mode, const QString &m, const T1 &t1, const T2 &t2,
                             const T3 &t3, const T4 &t4)
    {
        QList<QVariant> args;
        args << qvfv(t1) << qvfv(t2) << qvfv(t3)
             << qvfv(t4);
        return callWithArgs(m, args, mode);
    }

    template<typename T1, typename T2, typename T3, typename T4, typename T5>
    inline QDBusMessage call(CallMode mode, const QString &m, const T1 &t1, const T2 &t2,
                             const T3 &t3, const T4 &t4, const T5 &t5)
    {
        QList<QVariant> args;
        args << qvfv(t1) << qvfv(t2) << qvfv(t3)
             << qvfv(t4) << qvfv(t5);
        return callWithArgs(m, args, mode);
    }
  
    template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
    inline QDBusMessage call(CallMode mode, const QString &m, const T1 &t1, const T2 &t2,
                             const T3 &t3, const T4 &t4, const T5 &t5, const T6 &t6)
    {
        QList<QVariant> args;
        args << qvfv(t1) << qvfv(t2) << qvfv(t3)
             << qvfv(t4) << qvfv(t5) << qvfv(t6);
        return callWithArgs(m, args, mode);
    }

    template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
    inline QDBusMessage call(CallMode mode, const QString &m, const T1 &t1, const T2 &t2,
                             const T3 &t3, const T4 &t4, const T5 &t5, const T6 &t6, const T7 &t7)
    {
        QList<QVariant> args;
        args << qvfv(t1) << qvfv(t2) << qvfv(t3)
             << qvfv(t4) << qvfv(t5) << qvfv(t6)
             << qvfv(t7);
        return callWithArgs(m, args, mode);
    }

    template<typename T1, typename T2, typename T3, typename T4, typename T5,
             typename T6, typename T7, typename T8>
    inline QDBusMessage call(CallMode mode, const QString &m, const T1 &t1, const T2 &t2,
                             const T3 &t3, const T4 &t4, const T5 &t5, const T6 &t6, const T7 &t7,
                             const T8 &t8)
    {
        QList<QVariant> args;
        args << qvfv(t1) << qvfv(t2) << qvfv(t3)
             << qvfv(t4) << qvfv(t5) << qvfv(t6)
             << qvfv(t7) << qvfv(t8);
        return callWithArgs(m, args, mode);
    }
#endif

protected:
    QDBusAbstractInterface(QDBusAbstractInterfacePrivate *);
    void connectNotify(const char *signal);
    void disconnectNotify(const char *signal);
    QVariant internalPropGet(const char *propname) const;
    void internalPropSet(const char *propname, const QVariant &value);

private:
    friend class QDBusInterface;
    QDBusAbstractInterfacePrivate *d_ptr; // remove for Qt 4.2.0

    Q_DECLARE_PRIVATE(QDBusAbstractInterface)
    Q_DISABLE_COPY(QDBusAbstractInterface)
};

#endif

--- NEW FILE: qdbusabstractinterface_p.h ---
/* 
 *
 * Copyright (C) 2006 Thiago José Macieira <thiago at kde.org>
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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 QDBUSABSTRACTINTERFACEPRIVATE_H
#define QDBUSABSTRACTINTERFACEPRIVATE_H

#include "qdbusabstractinterface.h"
#include "qdbusconnection.h"
#include "qdbuserror.h"

#define ANNOTATION_NO_WAIT      "org.freedesktop.DBus.Method.NoReply"

class QDBusAbstractInterfacePrivate//: public QObjectPrivate
{
public:
    Q_DECLARE_PUBLIC(QDBusAbstractInterface)
    
    QDBusAbstractInterface *q_ptr; // remove in Qt 4.2
    QDBusConnection conn;
    QDBusConnectionPrivate *connp;
    QString service;
    QString path;
    QString interface;
    mutable QDBusError lastError;
    bool isValid;

    inline QDBusAbstractInterfacePrivate(const QDBusConnection& con, QDBusConnectionPrivate *conp,
                                         const QString &serv, const QString &p, const QString &iface)
        : conn(con), connp(conp), service(serv), path(p), interface(iface), isValid(true)
    { }
    virtual ~QDBusAbstractInterfacePrivate() { }

    // these functions do not check if the property is valid
    QVariant property(const QMetaProperty &mp) const;
    void setProperty(const QMetaProperty &mp, const QVariant &value);
};


#endif

--- NEW FILE: qdbusbus.cpp ---
/* -*- C++ -*-
 *
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at trolltech.com>
 *
 * 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.
 *
 */

/*
 * This file was generated by dbusidl2cpp version 0.3
 * when processing input file -
 *
 * dbusidl2cpp is Copyright (C) 2006 Trolltech AS. All rights reserved.
 *
 * This file has been hand-edited! Be careful when re-generating it!
 *
 */

#include "qdbusbus.h"

#include <QtCore/QByteArray>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QVariant>

/*
 * Implementation of interface class QDBusBusService
 */

/*!
    \class QDBusBusService
    \brief Provides access to the D-Bus bus daemon service.

*/

/*!
    \enum QDBusBusService::RequestNameOption

    Flags for requesting a name on the bus.

    \value AllowReplacingName   Allow another application requesting the same name to take the name
                                from this application.
    \value ReplaceExistingName  If another application already has the name and allows replacing,
                                take the name and assign it to us.
    \value DoNotQueueName       Without this flag, if an application requests a name that is already
                                owned and does not allow replacing, it will be queued until the
                                name is given up. If this flag is given, no queueing will be
                                performed and the requestName() call will simply fail.
*/

/*!
    \enum QDBusBusService::RequestNameReply

    The possible return values from requestName():

    \value PrimaryOwnerReply    The caller is now the primary owner of the name.
    \value InQueueReply         The caller is in queue for the name, but does not own it.
    \value NameExistsReply      The name exists and could not be replaced, or the caller did
                                specify DoNotQueueName.
    \value AlreadyOwnerReply    The caller tried to request a name that it already owns.
*/

/*!
    \enum QDBusBusService::ReleaseNameReply

    The possible return values from releaseName():

    \value NameReleasedReply    The caller released his claim on the name.
    \value NameNonExistentReply The caller tried to release a name that did not exist.
    \value NotOwnerReply        The caller tried to release a name that it did not own or was not in
                                queue for.
*/

/*!
    \enum QDBusBusService::StartServiceReply

    The possible return values from startServiceByName():

    \value Success              The service was successfully started.
    \value AlreadyRunning       The service was already running.
*/

/*!
    \internal
*/
const char *QDBusBusService::staticInterfaceName()
{ return "org.freedesktop.DBus"; }


/*!
    \internal
*/
QDBusBusService::QDBusBusService(QDBusAbstractInterfacePrivate *p)
    : QDBusAbstractInterface(p)
{
    connect(this, SIGNAL(NameAcquired(QString)), this, SIGNAL(nameAcquired(QString)));
    connect(this, SIGNAL(NameLost(QString)), this, SIGNAL(nameLost(QString)));
    connect(this, SIGNAL(NameOwnerChanged(QString,QString,QString)),
            this, SIGNAL(nameOwnerChanged(QString,QString,QString)));
}

/*!
    \internal
*/
QDBusBusService::~QDBusBusService()
{
}

/*!
    \fn QDBusBusService::hello()
    \internal
    Sends a "Hello" request to the bus service. You do not want to call this.
*/
QDBusReply<QString> QDBusBusService::Hello()
{
    return call(QLatin1String("Hello"));
}

/*!
    \fn QDBusBusService::nameOwner(const QString &name)
    Returns the unique connection name of the primary owner of the name \a name. If the requested
    name doesn't have an owner, returns a org.freedesktop.DBus.Error.NameHasNoOwner error.
*/
QDBusReply<QString> QDBusBusService::GetNameOwner(const QString &name)
{
    return call(QLatin1String("GetNameOwner.s"), name);
}

/*!
    \fn QDBusBusService::listNames()
    Lists all names currently existing on the bus.
*/
QDBusReply<QStringList> QDBusBusService::ListNames()
{
    return call(QLatin1String("ListNames"));
}

/*!
    \fn QDBusBusService::listQueuedOwners(const QString &service)
    Returns a list of all unique connection names in queue for the service name \a service.
*/
QDBusReply<QStringList> QDBusBusService::ListQueuedOwners(const QString &service)
{
    return call(QLatin1String("ListQueuedOwners.s"), service);
}

/*!
    \fn QDBusBusService::nameHasOwner(const QString &service)
    Returns true if the service name \a service has an owner.
*/
QDBusReply<bool> QDBusBusService::NameHasOwner(const QString &service)
{
    return call(QLatin1String("NameHasOwner.s"), service);
}

/*!
    \fn QDBusBusService::addMatch(const QString &rule)
    Adds the rule \a rule for requesting messages from the bus.

    \sa removeMatch()
*/
QDBusReply<void> QDBusBusService::AddMatch(const QString &rule)
{
    return call(QLatin1String("AddMatch.s"), rule);
}

/*!
    \fn QDBusBusService::removeMatch(const QString &rule)
    Removes the rule \a rule, that had previously been added with addMatch().
*/
QDBusReply<void> QDBusBusService::RemoveMatch(const QString &rule)
{
    return call(QLatin1String("RemoveMatch.s"), rule);
}

/*!
    \fn QDBusBusService::connectionSELinuxSecurityContext(const QString &service)
    Returns the SELinux security context of the process currently holding the bus service \a
    service.
*/
QDBusReply<QByteArray> QDBusBusService::GetConnectionSELinuxSecurityContext(const QString &service)
{
    return call(QLatin1String("GetConnectionSELinuxSecurityContext.s"), service);
}

/*!
    \fn QDBusBusService::connectionUnixProcessID(const QString &service)
    Returns the Unix Process ID (PID) for the process currently holding the bus service \a service.
*/
QDBusReply<uint> QDBusBusService::GetConnectionUnixProcessID(const QString &service)
{
    return call(QLatin1String("GetConnectionUnixProcessID.s"), service);
}

/*!
    \fn QDBusBusService::connectionUnixUser(const QString &service)
    Returns the Unix User ID (UID) for the process currently holding the bus service \a service.
*/
QDBusReply<uint> QDBusBusService::GetConnectionUnixUser(const QString &service)
{
    return call(QLatin1String("GetConnectionUnixUser.s"), service);
}

/*!
    \fn QDBusBusService::reloadConfig()
    Asks the D-Bus server daemon to reload its configuration.
*/
QDBusReply<void> QDBusBusService::ReloadConfig()
{
    return call(QLatin1String("ReloadConfig"));
}

inline QDBUS_EXPORT int qDBusMetaTypeId(QDBusBusService::StartServiceReply *)
{ return QVariant::Int; }

/*!
    \fn QDBusBusService::startServiceByName(const QString &name, uint flags)
    Requests that the bus start the service given by the name \a name.

    The \a flags parameter is currently not used.
*/
QDBusReply<QDBusBusService::StartServiceReply>
QDBusBusService::StartServiceByName(const QString &name, uint flags)
{
    return call(QLatin1String("StartServiceByName.su"), name, flags);
}

inline QDBUS_EXPORT int qDBusMetaTypeId(QDBusBusService::RequestNameReply *)
{ return QVariant::Int; }

/*!
    \fn QDBusBusService::requestName(const QString &service, RequestNameOptions flags)
    Requests the bus service name \a service from the bus. The \a flags parameter specifies how the
    bus server daemon should act when the same name is requested by two different applications.

    \sa releaseName()
*/
QDBusReply<QDBusBusService::RequestNameReply>
QDBusBusService::RequestName(const QString &service, RequestNameOptions flags)
{
    return call(QLatin1String("RequestName.su"), service, uint(int(flags)));
}

inline QDBUS_EXPORT int qDBusMetaTypeId(QDBusBusService::ReleaseNameReply *)
{ return QVariant::Int; }

/*!
    \fn QDBusBusService::releaseName(const QString &service)
    Releases the claim on the bus service name \a service, that had been previously requested with
    requestName(). If this application had ownership of the name, it will be released for other
    applications to claim. If it only had the name queued, it gives up its position in the queue.
*/
QDBusReply<QDBusBusService::ReleaseNameReply>
QDBusBusService::ReleaseName(const QString &service)
{
    return call(QLatin1String("ReleaseName.s"), service);
}

// signals
/*!
    \fn QDBusBusService::nameAcquired(const QString &service)

    This signal is emitted by the D-Bus bus server when the bus service name (unique connection name
    or well-known service name) given by \a service is acquired by this application.

    Name acquisition happens after the application requested a name using requestName().
*/

/*!
    \fn QDBusBusService::nameLost(const QString &service)

    This signal is emitted by the D-Bus bus server when the application loses ownership of the bus
    service name given by \a service.
*/

/*!
    \fn QDBusBusService::nameOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner)

    This signal is emitted by the D-Bus bus server whenever a name ownership change happens in the
    bus, including apparition and disparition of names.

    This signal means the application \a oldOwner lost ownership of bus name \a name to application
    \a newOwner. If \a oldOwner is an empty string, it means the name \a name has just been created;
    if \a newOwner is empty, the name \a name has no current owner.
*/

#include "qdbusbus.moc"

--- NEW FILE: qdbusbus.h ---
/* -*- C++ -*-
 *
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at trolltech.com>
 *
 * 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.
 *
 */

/*
 * This file was generated by dbusidl2cpp version 0.3
 * when processing input file -
 *
 * dbusidl2cpp is Copyright (C) 2006 Trolltech AS. All rights reserved.
 *
 * This file has been hand-edited! Be careful when re-generating it!
 *
 */

#ifndef QDBUSBUS_H
#define QDBUSBUS_H

#include <QtCore/qstringlist.h>

#include "qdbusabstractinterface.h"
#include "qdbusreply.h"

class QDBusConnection;
class QString;
class QByteArray;

/*
 * Proxy class for interface org.freedesktop.DBus
 */
class QDBUS_EXPORT QDBusBusService: public QDBusAbstractInterface
{
    Q_OBJECT
    friend class QDBusConnection;
    static inline const char *staticInterfaceName();

    explicit QDBusBusService(QDBusAbstractInterfacePrivate *p);

    ~QDBusBusService();

public:
    // taken out of http://dbus.freedesktop.org/doc/dbus-specification.html
    // update if the standard updates
    enum RequestNameOption {
        AllowReplacingName = 0x1,
        ReplaceExistingName = 0x2,
        DoNotQueueName = 0x4
    };
    Q_DECLARE_FLAGS(RequestNameOptions, RequestNameOption)

    enum RequestNameReply {
        PrimaryOwnerReply = 1,
        InQueueReply = 2,
        NameExistsReply = 3,
        AlreadyOwnerReply = 4
    };

    enum ReleaseNameReply {
        NameReleasedReply = 1,
        NameNonExistentReply = 2,
        NotOwnerReply = 3
    };

    enum StartServiceReply {
        Success = 1,
        AlreadyRunning = 2
    };    

#ifndef Q_QDOC
    // D-Bus names
public: // METHODS
    QDBusReply<QString> Hello();
    QDBusReply<void> ReloadConfig();

    QDBusReply<QStringList> ListNames();
        
    QDBusReply<bool> NameHasOwner(const QString &service);
    QDBusReply<QString> GetNameOwner(const QString &name);
    QDBusReply<ReleaseNameReply> ReleaseName(const QString &service);    
    QDBusReply<RequestNameReply> RequestName(const QString &service, RequestNameOptions flags);
    QDBusReply<QStringList> ListQueuedOwners(const QString &service);

    QDBusReply<void> AddMatch(const QString &rule);
    QDBusReply<void> RemoveMatch(const QString &rule);

    QDBusReply<QByteArray> GetConnectionSELinuxSecurityContext(const QString &service);
    QDBusReply<uint> GetConnectionUnixProcessID(const QString &service);
    QDBusReply<uint> GetConnectionUnixUser(const QString &service);

    QDBusReply<StartServiceReply> StartServiceByName(const QString &name, uint flags);

signals: // SIGNALS
    void NameAcquired(const QString &service);
    void NameLost(const QString &service);
    void NameOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner);
#endif

    // Qt-style naming    
public slots:
    QDBusReply<QString> hello()
    { return Hello(); }
    QDBusReply<void> reloadConfig()
    { return ReloadConfig(); }

    QDBusReply<QStringList> listNames()
    { return ListNames(); }
        
    QDBusReply<bool> nameHasOwner(const QString &service)
    { return NameHasOwner(service); }
    QDBusReply<QString> nameOwner(const QString &name)
    { return GetNameOwner(name); }
    QDBusReply<ReleaseNameReply> releaseName(const QString &service)
    { return ReleaseName(service); }
    QDBusReply<RequestNameReply> requestName(const QString &service, RequestNameOptions flags)
    { return RequestName(service, flags); }
    QDBusReply<QStringList> listQueuedOwners(const QString &service)
    { return ListQueuedOwners(service); }

    QDBusReply<void> addMatch(const QString &rule)
    { return AddMatch(rule); }
    QDBusReply<void> removeMatch(const QString &rule)
    { return RemoveMatch(rule); }

    QDBusReply<QByteArray> connectionSELinuxSecurityContext(const QString &service)
    { return GetConnectionSELinuxSecurityContext(service); }
    QDBusReply<uint> connectionUnixProcessID(const QString &service)
    { return GetConnectionUnixProcessID(service); }
    QDBusReply<uint> connectionUnixUser(const QString &service)
    { return GetConnectionUnixUser(service); }

    QDBusReply<StartServiceReply> startServiceByName(const QString &name, uint flags)
    { return StartServiceByName(name, flags); }

signals:
    void nameAcquired(const QString &service);
    void nameLost(const QString &service);
    void nameOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner);
};

Q_DECLARE_OPERATORS_FOR_FLAGS(QDBusBusService::RequestNameOptions)

#endif

--- NEW FILE: qdbusconnection.cpp ---
/* qdbusconnection.cpp
 *
 * Copyright (C) 2005 Harald Fernengel <harry at kdevelop.org>
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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 <qdebug.h>
#include <qcoreapplication.h>
#include <qstringlist.h>

#include "qdbusbus.h"
#include "qdbusconnection.h"
#include "qdbuserror.h"
#include "qdbusmessage.h"
#include "qdbusconnection_p.h"
#include "qdbusinterface_p.h"
#include "qdbusutil.h"

class QDBusConnectionManager
{
public:
    QDBusConnectionManager() {}
    ~QDBusConnectionManager();
    void bindToApplication();
    QDBusConnectionPrivate *connection(const QString &name) const;
    void removeConnection(const QString &name);
    void setConnection(const QString &name, QDBusConnectionPrivate *c);

private:
    mutable QMutex mutex;
    QHash<QString, QDBusConnectionPrivate *> connectionHash;
};

Q_GLOBAL_STATIC(QDBusConnectionManager, manager)

QDBusConnectionPrivate *QDBusConnectionManager::connection(const QString &name) const
{
    QMutexLocker locker(&mutex);
    return connectionHash.value(name, 0);
}

void QDBusConnectionManager::removeConnection(const QString &name)
{
    QMutexLocker locker(&mutex);

    QDBusConnectionPrivate *d = 0;
    d = connectionHash.take(name);
    if (d && !d->ref.deref())
        delete d;
}

QDBusConnectionManager::~QDBusConnectionManager()
{
    for (QHash<QString, QDBusConnectionPrivate *>::const_iterator it = connectionHash.constBegin();
         it != connectionHash.constEnd(); ++it) {
             delete it.value();
    }
    connectionHash.clear();
}

void QDBusConnectionManager::bindToApplication()
{
    QMutexLocker locker(&mutex);
    for (QHash<QString, QDBusConnectionPrivate *>::const_iterator it = connectionHash.constBegin();
         it != connectionHash.constEnd(); ++it) {
             (*it)->bindToApplication();
    }
}

QDBUS_EXPORT void qDBusBindToApplication();
void qDBusBindToApplication()
{
    manager()->bindToApplication();
}

void QDBusConnectionManager::setConnection(const QString &name, QDBusConnectionPrivate *c)
{
    connectionHash[name] = c;
    c->name = name;
}

/*!
    \fn QDBusConnection QDBus::sessionBus()
    \relates QDBusConnection

    Returns a QDBusConnection object opened with the session bus. The object reference returned
    by this function is valid until the QCoreApplication's destructor is run, when the
    connection will be closed and the object, deleted.
*/
/*!
    \fn QDBusConnection QDBus::systemBus()
    \relates QDBusConnection

    Returns a QDBusConnection object opened with the system bus. The object reference returned
    by this function is valid until the QCoreApplication's destructor is run, when the
    connection will be closed and the object, deleted.
*/

/*!
    \class QDBusConnection
    \brief A connection to the D-Bus bus daemon.

    This class is the initial point in a D-Bus session. Using it, you can get access to remote
    objects, interfaces; connect remote signals to your object's slots; register objects, etc.

    D-Bus connections are created using the QDBusConnection::addConnection() function, which opens a
    connection to the server daemon and does the initial handshaking, associating that connection
    with a name. Further attempts to connect using the same name will return the same
    connection.

    The connection is then torn down using the QDBusConnection::closeConnection() function.

    As a convenience for the two most common connection types, the QDBus::sessionBus() and
    QDBus::systemBus() functions return open connections to the session server daemon and the system
    server daemon, respectively. Those connections are opened when first used and are closed when
    the QCoreApplication destructor is run.

    D-Bus also supports peer-to-peer connections, without the need for a bus server daemon. Using
    this facility, two applications can talk to each other and exchange messages. This can be
    achieved by passing an address to QDBusConnection::addConnection()
    function, which was opened by another D-Bus application using QDBusServer.
*/

/*!
    \enum QDBusConnection::BusType
    Specifies the type of the bus connection. The valid bus types are:

    \value SessionBus           the session bus, associated with the running desktop session
    \value SystemBus            the system bus, used to communicate with system-wide processes
    \value ActivationBus        the activation bus, whose purpose I have no idea...

    On the Session Bus, one can find other applications by the same user that are sharing the same
    desktop session (hence the name). On the System Bus, however, processes shared for the whole
    system are usually found.
*/

/*!
    \enum QDBusConnection::WaitMode
    Specifies the call waiting mode.

    \value UseEventLoop         use the Qt Event Loop to wait for the reply
    \value NoUseEventLoop       don't use the event loop

    The \c UseEventLoop option allows for the application to continue to update its UI while the
    call is performed, but it also opens up the possibility for reentrancy: socket notifiers may
    fire, signals may be delivered and other D-Bus calls may be processed. The \c NoUseEventLoop
    does not use the event loop, thus being safe from those problems, but it may block the
    application for a noticeable period of time, in case the remote application fails to respond.

    Also note that calls that go back to the local application can only be placed in \c UseEventLoop
    mode.
*/

/*!
    \enum QDBusConnection::RegisterOption
    Specifies the options for registering objects with the connection. The possible values are:

    \value ExportAdaptors                       export the contents of adaptors found in this object

    \value ExportSlots                          export this object's scriptable slots
    \value ExportSignals                        export this object's scriptable signals
    \value ExportProperties                     export this object's scriptable properties
    \value ExportContents                       shorthand form for ExportSlots | ExportSignals |
                                                ExportProperties

    \value ExportAllSlots                       export all of this object's slots, including
                                                non-scriptable ones
    \value ExportAllSignals                     export all of this object's signals, including
                                                non-scriptable ones
    \value ExportAllProperties                  export all of this object's properties, including
                                                non-scriptable ones
    \value ExportAllContents                    export all of this object's slots, signals and
                                                properties, including non-scriptable ones

    \value ExportChildObjects                   export this object's child objects

    \warning It is currently not possible to export signals from objects. If you pass the flag
    ExportSignals or ExportAllSignals, the registerObject() function will print a warning.

    \sa registerObject(), QDBusAbstractAdaptor, {usingadaptors.html}{Using adaptors}
*/

/*!
    \enum QDBusConnection::UnregisterMode
    The mode for unregistering an object path:

    \value UnregisterNode       unregister this node only: do not unregister child objects
    \value UnregisterTree       unregister this node and all its sub-tree

    Note, however, if this object was registered with the ExportChildObjects option, UnregisterNode
    will unregister the child objects too.
*/

/*!
    Creates a QDBusConnection object attached to the connection with name \a name.

    This does not open the connection. You have to call QDBusConnection::addConnection to open it.
*/
QDBusConnection::QDBusConnection(const QString &name)
{
    d = manager()->connection(name);
    if (d)
        d->ref.ref();
}

/*!
    Creates a copy of the \a other connection.
*/
QDBusConnection::QDBusConnection(const QDBusConnection &other)
{
    d = other.d;
    if (d)
        d->ref.ref();
}

/*!
    Disposes of this object. This does not close the connection: you have to call
    QDBusConnection::closeConnection to do that.
*/
QDBusConnection::~QDBusConnection()
{
    if (d && !d->ref.deref())
        delete d;
}

/*!
    Creates a copy of the connection \a other in this object. The connection this object referenced
    before the copy is not spontaneously disconnected. See QDBusConnection::closeConnection for more
    information.
*/
QDBusConnection &QDBusConnection::operator=(const QDBusConnection &other)
{
    if (other.d)
        other.d->ref.ref();
    QDBusConnectionPrivate *old = static_cast<QDBusConnectionPrivate *>(
            q_atomic_set_ptr(&d, other.d));
    if (old && !old->ref.deref())
        delete old;

    return *this;
}

/*!
    Opens a connection of type \a type to one of the known busses and associate with it the
    connection name \a name. Returns a QDBusConnection object associated with that connection.
*/
QDBusConnection QDBusConnection::addConnection(BusType type, const QString &name)
{
//    Q_ASSERT_X(QCoreApplication::instance(), "QDBusConnection::addConnection",
//               "Cannot create connection without a Q[Core]Application instance");

    QDBusConnectionPrivate *d = manager()->connection(name);
    if (d || name.isEmpty())
        return QDBusConnection(name);

    d = new QDBusConnectionPrivate;
    DBusConnection *c = 0;
    switch (type) {
        case SystemBus:
            c = dbus_bus_get_private(DBUS_BUS_SYSTEM, &d->error);
            break;
        case SessionBus:
            c = dbus_bus_get_private(DBUS_BUS_SESSION, &d->error);
            break;
        case ActivationBus:
            c = dbus_bus_get_private(DBUS_BUS_STARTER, &d->error);
            break;
    }
    d->setConnection(c); //setConnection does the error handling for us

    manager()->setConnection(name, d);

    QDBusConnection retval(name);

    // create the bus service
    QDBusAbstractInterfacePrivate *p;
    p = retval.findInterface_helper(QLatin1String(DBUS_SERVICE_DBUS),
                                    QLatin1String(DBUS_PATH_DBUS),
                                    QLatin1String(DBUS_INTERFACE_DBUS));
    if (p) {
        d->busService = new QDBusBusService(p);
        d->busService->setParent(d); // auto-deletion
        d->ref.deref();              // busService has a increased the refcounting to us
    }

    return retval;
}

/*!
    Opens a peer-to-peer connection on address \a address and associate with it the
    connection name \a name. Returns a QDBusConnection object associated with that connection.
*/
QDBusConnection QDBusConnection::addConnection(const QString &address,
                    const QString &name)
{
//    Q_ASSERT_X(QCoreApplication::instance(), "QDBusConnection::addConnection",
//               "Cannot create connection without a Q[Core]Application instance");

    QDBusConnectionPrivate *d = manager()->connection(name);
    if (d || name.isEmpty())
        return QDBusConnection(name);

    d = new QDBusConnectionPrivate;
    // setConnection does the error handling for us
    d->setConnection(dbus_connection_open(address.toUtf8().constData(), &d->error));

    manager()->setConnection(name, d);

    QDBusConnection retval(name);

    // create the bus service
    QDBusAbstractInterfacePrivate *p;
    p = retval.findInterface_helper(QLatin1String(DBUS_SERVICE_DBUS),
                                    QLatin1String(DBUS_PATH_DBUS),
                                    QLatin1String(DBUS_INTERFACE_DBUS));
    if (p) {
        d->busService = new QDBusBusService(p);
        d->busService->setParent(d); // auto-deletion
        d->ref.deref();              // busService has a increased the refcounting to us
    }

    return retval;
}

/*!
    Closes the connection of name \a name.

    Note that if there are still QDBusConnection objects associated with the same connection, the
    connection will not be closed until all references are dropped. However, no further references
    can be created using the QDBusConnection::QDBusConnection constructor.
*/
void QDBusConnection::closeConnection(const QString &name)
{
    manager()->removeConnection(name);
}

/*!
    Sends the \a message over this connection, without waiting for a reply. This is suitable for
    errors, signals, and return values as well as calls whose return values are not necessary.

    Returns true if the message was queued successfully, false otherwise.
*/
bool QDBusConnection::send(const QDBusMessage &message) const
{
    if (!d || !d->connection)
        return false;
    return d->send(message) != 0;
}

/*!
    Sends the \a message over this connection and returns immediately after queueing it. When the
    reply is received, the slot \a method is called in the object \a receiver. This function is
    suitable for method calls only.

    This function guarantees that the slot will be called exactly once with the reply, as long as
    the parameter types match. If they don't, the reply cannot be delivered.

    Returns the identification of the message that was sent or 0 if nothing was sent.
*/
int QDBusConnection::sendWithReplyAsync(const QDBusMessage &message, QObject *receiver,
        const char *method) const
{
    if (!d || !d->connection)
        return 0;

    return d->sendWithReplyAsync(message, receiver, method);
}

/*!
    Sends the \a message over this connection and blocks, waiting for a reply. This function is
    suitable for method calls only. It returns the reply message as its return value, which will be
    either of type QDBusMessage::ReplyMessage or QDBusMessage::ErrorMessage.

    See the QDBusInterface::call function for a more friendly way of placing calls.

    \warning If \a mode is \c UseEventLoop, this function will reenter the Qt event loop in order to
             wait for the reply. During the wait, it may deliver signals and other method calls to
             your application. Therefore, it must be prepared to handle a reentrancy whenever a call
             is placed with sendWithReply.
*/
QDBusMessage QDBusConnection::sendWithReply(const QDBusMessage &message, WaitMode mode) const
{
    if (!d || !d->connection)
        return QDBusMessage();
    return d->sendWithReply(message, mode);
}

/*!
    Connects the signal specified by the \a service, \a path, \a interface and \a name parameters to
    the slot \a slot in object \a receiver. The arguments \a service and \a path can be empty,
    denoting a connection to any signal of the \a interface - \a name pair, from any remote
    application.

    Returns true if the connection was successful.

    \warning The signal will only be delivered to the slot if the parameters match. This verification
             can be done only when the signal is received, not at connection time.
*/
bool QDBusConnection::connect(const QString &service, const QString &path, const QString& interface,
                              const QString &name, QObject *receiver, const char *slot)
{
    return connect(service, path, interface, name, QString(), receiver, slot);
}

/*!
    \overload
    Connects the signal to the slot \a slot in object \a receiver. Unlike the other
    QDBusConnection::connect overload, this function allows one to specify the parameter signature
    to be connected using the \a signature variable. The function will then verify that this
    signature can be delivered to the slot specified by \a slot and return false otherwise.
*/
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 || !QDBusUtil::isValidInterfaceName(interface))
        return false;

    QString source;
    if (!service.isEmpty()) {
        source = d->getNameOwner(service);
        if (source.isEmpty())
            return false;
    }

    // check the slot
    QDBusConnectionPrivate::SignalHook hook;
    QString key;
    hook.signature = signature;
    if (!d->prepareHook(hook, key, source, path, interface, name, receiver, slot, 0, false))
        return false;           // don't connect

    // avoid duplicating:
    QWriteLocker locker(&d->lock);
    QDBusConnectionPrivate::SignalHookHash::ConstIterator it = d->signalHooks.find(key);
    for ( ; it != d->signalHooks.end() && it.key() == key; ++it) {
        const QDBusConnectionPrivate::SignalHook &entry = it.value();
        if (entry.sender == hook.sender &&
            entry.path == hook.path &&
            entry.signature == hook.signature &&
            entry.obj == hook.obj &&
            entry.midx == hook.midx) {
            // no need to compare the parameters if it's the same slot
            return true;        // already there
        }
    }


    d->connectSignal(key, hook);
    return true;
}

/*!
    Registers the object \a object at path \a path and returns true if the registration was
    successful. The \a options parameter specifies how much of the object \a object will be exposed
    through D-Bus.

    This function does not replace existing objects: if there is already an object registered at
    path \a path, this function will return false. Use unregisterObject() to unregister it first.

    You cannot register an object as a child object of an object that was registered with
    QDBusConnection::ExportChildObjects.
*/
bool QDBusConnection::registerObject(const QString &path, QObject *object, RegisterOptions options)
{
    if (!d || !d->connection || !object || !options || !QDBusUtil::isValidObjectPath(path))
        return false;

    if (options & ExportSignals) {
        qWarning("Cannot export signals from objects. Use an adaptor for that purpose.");
        return false;
    }

    QStringList pathComponents = path.split(QLatin1Char('/'));
    if (pathComponents.last().isEmpty())
        pathComponents.removeLast();
    QWriteLocker locker(&d->lock);

    // lower-bound search for where this object should enter in the tree
    QDBusConnectionPrivate::ObjectTreeNode *node = &d->rootNode;
    int i = 1;
    while (node) {
        if (pathComponents.count() == i) {
            // this node exists
            // consider it free if there's no object here and the user is not trying to
            // replace the object sub-tree
            if ((options & ExportChildObjects && !node->children.isEmpty()) || node->obj)
                return false;

            // we can add the object here
            node->obj = object;
            node->flags = options;

            d->registerObject(node);
            qDebug("REGISTERED FOR %s", path.toLocal8Bit().constData());
            return true;
        }

        // find the position where we'd insert the node
        QVector<QDBusConnectionPrivate::ObjectTreeNode::Data>::Iterator it =
            qLowerBound(node->children.begin(), node->children.end(), pathComponents.at(i));
        if (it != node->children.constEnd() && it->name == pathComponents.at(i)) {
            // match: this node exists
            node = it->node;

            // are we allowed to go deeper?
            if (node->flags & ExportChildObjects) {
                // we're not
                qDebug("Cannot register object at %s because %s exports its own child objects",
                       qPrintable(path), qPrintable(pathComponents.at(i)));
                return false;
            }
        } else {
            // add entry
            QDBusConnectionPrivate::ObjectTreeNode::Data entry;
            entry.name = pathComponents.at(i);
            entry.node = new QDBusConnectionPrivate::ObjectTreeNode;
            node->children.insert(it, entry);

            node = entry.node;
        }

        // iterate
        ++i;
    }

    Q_ASSERT_X(false, "QDBusConnection::registerObject", "The impossible happened");
    return false;
}

/*!
    Unregisters an object that was registered with the registerObject() at the object path given by
    \a path and, if \a mode is QDBusConnection::UnregisterTree, all of its sub-objects too.

    Note that you cannot unregister objects that were not registered with registerObject().
*/
void QDBusConnection::unregisterObject(const QString &path, UnregisterMode mode)
{
    if (!d || !d->connection || !QDBusUtil::isValidObjectPath(path))
        return;

    QStringList pathComponents = path.split(QLatin1Char('/'));
    QWriteLocker locker(&d->lock);
    QDBusConnectionPrivate::ObjectTreeNode *node = &d->rootNode;
    int i = 1;

    // find the object
    while (node) {
        if (pathComponents.count() == i) {
            // found it
            node->obj = 0;
            node->flags = 0;

            if (mode == UnregisterTree) {
                // clear the sub-tree as well
                node->clear();  // can't disconnect the objects because we really don't know if they can
                                // be found somewhere else in the path too
            }

            return;
        }

        QVector<QDBusConnectionPrivate::ObjectTreeNode::Data>::ConstIterator it =
            qLowerBound(node->children.constBegin(), node->children.constEnd(), pathComponents.at(i));
        if (it == node->children.constEnd() || it->name != pathComponents.at(i))
            break;              // node not found

        node = it->node;
        ++i;
    }
}

/*!
    Returns a dynamic QDBusInterface associated with the interface \a interface on object at path \a
    path on service \a service.

    This function creates a new object. It is your resposibility to ensure it is properly deleted
    (you can use all normal QObject deletion mechanisms, including the QObject::deleteLater() slot
    and QObject::setParent()).

    If the searching for this interface on the remote object failed, this function returns 0.
*/
QDBusInterface *QDBusConnection::findInterface(const QString& service, const QString& path,
                                               const QString& interface)
{
    if (!d)
        return 0;
    
    QDBusInterfacePrivate *p = d->findInterface(service, path, interface);
    QDBusInterface *retval = new QDBusInterface(p);
    retval->setParent(d);
    return retval;
}

/*!
    \fn QDBusConnection::findInterface(const QString &service, const QString &path)
    Returns an interface of type \c Interface associated with the object on path \a path at service
    \a service.

    \c Interface must be a class generated by \l {dbusidl2cpp.html}.

    This function creates a new object. It is your resposibility to ensure it is properly deleted
    (you can use all normal QObject deletion mechanisms, including the QObject::deleteLater() slot
    and QObject::setParent()).
*/

/*!
    Returns a QDBusBusService object that represents the D-Bus bus service on this connection.

    This function returns 0 for peer-to-peer connections.
*/
QDBusBusService *QDBusConnection::busService() const
{
    if (!d)
        return 0;
    return d->busService;
}

QDBusAbstractInterfacePrivate *
QDBusConnection::findInterface_helper(const QString &service, const QString &path,
                                      const QString &interface)
{
    if (!d)
        return 0;
    if (!interface.isEmpty() && !QDBusUtil::isValidInterfaceName(interface))
        return 0;
    
    QString owner;
    if (!service.isEmpty()) {
        if (!QDBusUtil::isValidObjectPath(path))
            return 0;

        // check if it's there first -- FIXME: add binding mode
        owner = d->getNameOwner(service);
        if (owner.isEmpty())
            return 0;
    } else if (!path.isEmpty())
        return 0;
    
    return new QDBusAbstractInterfacePrivate(*this, d, owner, path, interface);
}

/*!
    Returns true if this QDBusConnection object is connected.

    If it isn't connected, calling QDBusConnection::addConnection on the same connection name
    will not make be connected. You need to call the QDBusConnection constructor again.
*/
bool QDBusConnection::isConnected( ) const
{
    return d && d->connection && dbus_connection_get_is_connected(d->connection);
}

/*!
    Returns the last error that happened in this connection.

    This function is provided for low-level code. If you're using QDBusInterface::call, error codes are
    reported by its return value.

    \sa QDBusInterface, QDBusMessage
*/
QDBusError QDBusConnection::lastError() const
{
    return d ? d->lastError : QDBusError();
}

/*!
    Returns the unique connection name for this connection, if this QDBusConnection object is
    connected, or an empty QString otherwise.

    A Unique Connection Name is a string in the form ":x.xxx" (where x are decimal digits) that is
    assigned by the D-Bus server daemon upon connection. It uniquely identifies this client in the
    bus.

    This function returns an empty QString for peer-to-peer connections.
*/
QString QDBusConnection::baseService() const
{
    return d && d->connection ?
            QString::fromUtf8(dbus_bus_get_unique_name(d->connection))
            : QString();
}

Q_GLOBAL_STATIC(QMutex, defaultBussesMutex)
static const char sessionBusName[] = "qt_default_session_bus";
static const char systemBusName[] = "qt_default_system_bus";
static QDBusConnection *sessionBus = 0;
static QDBusConnection *systemBus = 0;

static void closeConnections()
{
    QMutexLocker locker(defaultBussesMutex());
    delete sessionBus;
    delete systemBus;
    QDBusConnection::closeConnection(QLatin1String(sessionBusName));
    QDBusConnection::closeConnection(QLatin1String(systemBusName));
    sessionBus = systemBus = 0;
}

static QDBusConnection *openConnection(QDBusConnection::BusType type)
{
    QMutexLocker locker(defaultBussesMutex());
    qAddPostRoutine(closeConnections);
    
    if (type == QDBusConnection::SystemBus) {
        if (systemBus)
            // maybe it got created before we locked the mutex
            return systemBus;
        systemBus = new QDBusConnection(QDBusConnection::addConnection(QDBusConnection::SystemBus,
                           QLatin1String(systemBusName)));
        return systemBus;
    } else {
        if (sessionBus)
            // maybe it got created before we locked the mutex
            return sessionBus;
        sessionBus = new QDBusConnection(QDBusConnection::addConnection(QDBusConnection::SessionBus,
                           QLatin1String(sessionBusName)));
        return sessionBus;
    }
}

namespace QDBus {
    QDBusConnection &sessionBus()
    {
        if (::sessionBus) return *::sessionBus;
        return *openConnection(QDBusConnection::SessionBus);
    }

    QDBusConnection &systemBus()
    {
        if (::systemBus) return *::systemBus;
        return *openConnection(QDBusConnection::SystemBus);
    }
}


--- NEW FILE: qdbusconnection.h ---
/* qdbusconnection.h QDBusConnection object
 *
 * Copyright (C) 2005 Harald Fernengel <harry at kdevelop.org>
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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 QDBUSCONNECTION_H
#define QDBUSCONNECTION_H

#include "qdbusmacros.h"
#include <QtCore/qstring.h>

class QDBusAbstractInterfacePrivate;
class QDBusInterface;
class QDBusError;
class QDBusMessage;
class QDBusBusService;
class QObject;

class QDBusConnectionPrivate;
class QDBUS_EXPORT QDBusConnection
{
public:
    enum BusType { SessionBus, SystemBus, ActivationBus };
    enum WaitMode { UseEventLoop, NoUseEventLoop };
    enum RegisterOption {
        ExportAdaptors = 0x01,

        ExportSlots = 0x10,
        ExportSignals = 0x20,
        ExportProperties = 0x40,
        ExportContents = 0xf0,

        ExportAllSlots = 0x110,
        ExportAllSignals = 0x220,
        ExportAllProperties = 0x440,
        ExportAllContents = 0xff0,

        ExportChildObjects = 0x1000
    };
    enum UnregisterMode {
        UnregisterNode,
        UnregisterTree
    };

    Q_DECLARE_FLAGS(RegisterOptions, RegisterOption)

    QDBusConnection(const QString &name);
    QDBusConnection(const QDBusConnection &other);
    ~QDBusConnection();

    QDBusConnection &operator=(const QDBusConnection &other);

    bool isConnected() const;
    QString baseService() const;
    QDBusError lastError() const;

    bool send(const QDBusMessage &message) const;
    QDBusMessage sendWithReply(const QDBusMessage &message, WaitMode mode = NoUseEventLoop) const;
    int sendWithReplyAsync(const QDBusMessage &message, QObject *receiver,
                           const char *slot) const;

    bool connect(const QString &service, const QString &path, const QString &interface,
                 const QString &name, QObject *receiver, const char *slot);
    bool connect(const QString &service, const QString &path, const QString &interface,
                 const QString &name, const QString& signature,
                 QObject *receiver, const char *slot);

    bool registerObject(const QString &path, QObject *object,
                        RegisterOptions options = ExportAdaptors);
    void unregisterObject(const QString &path, UnregisterMode mode = UnregisterNode);

    template<class Interface>
    inline Interface *findInterface(const QString &service, const QString &path);
    QDBusInterface *findInterface(const QString& service, const QString& path,
                                  const QString& interface = QString());

    QDBusBusService *busService() const;

    static QDBusConnection addConnection(BusType type, const QString &name);
    static QDBusConnection addConnection(const QString &address, const QString &name);
    static void closeConnection(const QString &name);

private:
    QDBusAbstractInterfacePrivate *findInterface_helper(const QString &, const QString &,
                                                        const QString&);
    QDBusConnectionPrivate *d;
};

template<class Interface>
inline Interface *QDBusConnection::findInterface(const QString &service, const QString &path)
{
    register QDBusAbstractInterfacePrivate *d;
    d = findInterface_helper(service, path, Interface::staticInterfaceName());
    if (d)
        return new Interface(d);
    return 0;
}

namespace QDBus {
    QDBUS_EXPORT QDBusConnection &sessionBus();
    QDBUS_EXPORT QDBusConnection &systemBus();
}

Q_DECLARE_OPERATORS_FOR_FLAGS(QDBusConnection::RegisterOptions)
#endif

--- NEW FILE: qdbusconnection_p.h ---
/* qdbusconnection_p.h QDBusConnection private object
 *
 * Copyright (C) 2005 Harald Fernengel <harry at kdevelop.org>
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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 QDBUSCONNECTION_P_H
#define QDBUSCONNECTION_P_H

#include "qdbuserror.h"

#include <QtCore/qatomic.h>
#include <QtCore/qeventloop.h>
#include <QtCore/qhash.h>
#include <QtCore/qmutex.h>
#include <QtCore/qobject.h>
#include <QtCore/qpointer.h>
#include <QtCore/qreadwritelock.h>
#include <QtCore/qvarlengtharray.h>
#include <QtCore/qvector.h>

#include <dbus/dbus.h>

#include "qdbusmessage.h"

class QDBusMessage;
class QSocketNotifier;
class QTimerEvent;
class QDBusObjectPrivate;
class CallDeliveryEvent;
class QMetaMethod;
class QDBusInterfacePrivate;
struct QDBusMetaObject;
class QDBusAbstractInterface;
class QDBusBusService;

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
    {
        inline SignalHook() : obj(0), midx(-1) { }
        QString sender, path, signature;
        QObject* obj;
        int midx;
        QList<int> params;
    };

    struct ObjectTreeNode
    {
        struct Data
        {
            QString name;
            ObjectTreeNode *node;

            inline bool operator<(const QString &other) const
            { return name < other; }
        };

        inline ObjectTreeNode() : obj(0), flags(0) { }
        inline ~ObjectTreeNode() { clear(); }
        inline void clear()
        {
            foreach (const Data &entry, children) {
                entry.node->clear();
                delete entry.node;
            }
            children.clear();
        }

        QObject* obj;
        int flags;
        QVector<Data> children;
    };

public:
    // typedefs
    typedef QMultiHash<int, Watcher> WatcherHash;
    typedef QHash<int, DBusTimeout *> TimeoutHash;
    typedef QMultiHash<QString, SignalHook> SignalHookHash;
    typedef QHash<QString, QDBusMetaObject* > MetaObjectHash;
    
public:
    // public methods
    QDBusConnectionPrivate(QObject *parent = 0);
    ~QDBusConnectionPrivate();

    void bindToApplication();

    void setConnection(DBusConnection *connection);
    void setServer(DBusServer *server);
    void closeConnection();
    void timerEvent(QTimerEvent *e);

    QString getNameOwner(const QString &service);    

    int send(const QDBusMessage &message) const;
    QDBusMessage sendWithReply(const QDBusMessage &message, int mode);
    int sendWithReplyAsync(const QDBusMessage &message, QObject *receiver,
                           const char *method);
    void connectSignal(const QString &key, const SignalHook &hook);
    void registerObject(const ObjectTreeNode *node);
    void connectRelay(const QString &service, const QString &path, const QString &interface,
                      QDBusAbstractInterface *receiver, const char *signal);
    void disconnectRelay(const QString &service, const QString &path, const QString &interface,
                         QDBusAbstractInterface *receiver, const char *signal);
    
    bool handleSignal(const QString &key, const QDBusMessage &msg);
    bool handleSignal(const QDBusMessage &msg);
    bool handleObjectCall(const QDBusMessage &message);
    bool handleError();

    bool activateSignal(const SignalHook& hook, const QDBusMessage &msg);
    bool activateCall(QObject* object, int flags, const QDBusMessage &msg);
    bool activateObject(const ObjectTreeNode *node, const QDBusMessage &msg);
    bool activateInternalFilters(const ObjectTreeNode *node, const QDBusMessage &msg);

    void postCallDeliveryEvent(CallDeliveryEvent *data);
    CallDeliveryEvent *postedCallDeliveryEvent();
    void deliverCall(const CallDeliveryEvent &data) const;

    QDBusInterfacePrivate *findInterface(const QString &service, const QString &path,
                                         const QString &interface);

protected:
    virtual void customEvent(QEvent *event);

private:
    QDBusMetaObject *findMetaObject(const QString &service, const QString &path,
                                    const QString &interface);        

public slots:
    // public slots
    void doDispatch();
    void socketRead(int);
    void socketWrite(int);
    void objectDestroyed(QObject *o);
    void relaySignal(QObject *obj, const char *interface, const char *name, const QVariantList &args);

public:
    // public member variables
    QString name;               // this connection's name
    
    DBusError error;
    QDBusError lastError;

    QAtomic ref;
    QReadWriteLock lock;
    ConnectionMode mode;
    DBusConnection *connection;
    DBusServer *server;
    QDBusBusService *busService;

    WatcherHash watchers;
    TimeoutHash timeouts;
    SignalHookHash signalHooks;
    QList<DBusTimeout *> pendingTimeouts;

    ObjectTreeNode rootNode;
    MetaObjectHash cachedMetaObjects;

    QMutex callDeliveryMutex;
    CallDeliveryEvent *callDeliveryState; // protected by the callDeliveryMutex mutex

public:
    // static methods
    static int messageMetaType;
    static int registerMessageMetaType();
    static int findSlot(QObject *obj, const QByteArray &normalizedName, QList<int>& params);
    static bool prepareHook(QDBusConnectionPrivate::SignalHook &hook, QString &key,
                            const QString &service, const QString &path,
                            const QString &interface, const QString &name,
                            QObject *receiver, const char *signal, int minMIdx,
                            bool buildSignature);
    static DBusHandlerResult messageFilter(DBusConnection *, DBusMessage *, void *);
    static void messageResultReceived(DBusPendingCall *, void *);
};

class QDBusReplyWaiter: public QEventLoop
{
    Q_OBJECT
public:
    QDBusMessage replyMsg;

public slots:
    void reply(const QDBusMessage &msg);
};

// in qdbusmisc.cpp
extern int qDBusParametersForMethod(const QMetaMethod &mm, QList<int>& metaTypes);
extern int qDBusNameToTypeId(const char *name);
extern bool qDBusCheckAsyncTag(const char *tag);

// in qdbusinternalfilters.cpp
extern QString qDBusIntrospectObject(const QDBusConnectionPrivate::ObjectTreeNode *node);
extern void qDBusIntrospectObject(const QDBusConnectionPrivate::ObjectTreeNode *node,
                                  const QDBusMessage &msg);
extern void qDBusPropertyGet(const QDBusConnectionPrivate::ObjectTreeNode *node,
                             const QDBusMessage &msg);
extern void qDBusPropertySet(const QDBusConnectionPrivate::ObjectTreeNode *node,
                             const QDBusMessage &msg);

#endif

--- NEW FILE: qdbuserror.cpp ---
/* qdbuserror.h QDBusError object
 *
 * Copyright (C) 2005 Harald Fernengel <harry at kdevelop.org>
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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 "qdbuserror.h"

#include <qdebug.h>
#include <qvarlengtharray.h>

#include <dbus/dbus.h>
#include "qdbusmessage.h"

struct ErrorMessageMapping
{
    ErrorMessageMapping();
    QVarLengthArray<const char*, QDBusError::qKnownErrorsMax> messages;

    inline const char *get(QDBusError::KnownErrors code) const
    {
        if (code <= QDBusError::Other || code > QDBusError::qKnownErrorsMax)
            return messages[int(QDBusError::Other) - 1];
        return messages[int(code) - 1];
    }

    inline QDBusError::KnownErrors get(const char *name) const
    {
        if (!name || !*name)
            return QDBusError::NoError;
        for (int i = QDBusError::Other; i <= QDBusError::qKnownErrorsMax; ++i)
            if (strcmp(name, messages[i - 1]) == 0)
                return QDBusError::KnownErrors(i);
        return QDBusError::Other;
    }
};

static const char errorMessages_string[] =
    // in the same order as KnownErrors!
    "other\0"                           // Other -- shouldn't happen
    DBUS_ERROR_FAILED "\0"              // Failed
    DBUS_ERROR_NO_MEMORY "\0"           // NoMemory
    DBUS_ERROR_SERVICE_UNKNOWN "\0"     // ServiceUnknown
    DBUS_ERROR_NO_REPLY "\0"            // NoReply
    DBUS_ERROR_BAD_ADDRESS "\0"         // BadAddress
    DBUS_ERROR_NOT_SUPPORTED "\0"       // NotSupported
    DBUS_ERROR_LIMITS_EXCEEDED "\0"     // LimitsExceeded
    DBUS_ERROR_ACCESS_DENIED  "\0"      // AccessDenied
    DBUS_ERROR_NO_SERVER "\0"           // NoServer
    DBUS_ERROR_TIMEOUT "\0"             // Timeout
    DBUS_ERROR_NO_NETWORK "\0"          // NoNetwork
    DBUS_ERROR_ADDRESS_IN_USE "\0"      // AddressInUse
    DBUS_ERROR_DISCONNECTED "\0"        // Disconnected
    DBUS_ERROR_INVALID_ARGS "\0"        // InvalidArgs
    DBUS_ERROR_UNKNOWN_METHOD "\0"      // UnknownMethod
    DBUS_ERROR_TIMED_OUT "\0"           // TimedOut
    DBUS_ERROR_INVALID_SIGNATURE "\0"   // InvalidSignature
    "com.trolltech.QtDBus.Error.UnknownInterface\0" // UnknownInterface
    "com.trolltech.QtDBus.Error.InternalError\0" // InternalError
    "\0";

ErrorMessageMapping::ErrorMessageMapping()
    : messages(int(QDBusError::qKnownErrorsMax))
{
    // create the list:
    const char *p = errorMessages_string;
    int i = 0;
    while (*p) {
        messages[i] = p;
        p += strlen(p) + 1;
        ++i;
    }
}

Q_GLOBAL_STATIC(ErrorMessageMapping, errorMessages)    

/*!
    \class QDBusError
    \brief Represents an error received from the D-Bus bus or from remote applications found in the bus.

    When dealing with the D-Bus bus service or with remote applications over D-Bus, a number of
    error conditions can happen. This error conditions are sometimes signalled by a returned error
    value or by a QDBusError.

    C++ and Java exceptions are a valid analogy for D-Bus errors: instead of returning normally with
    a return value, remote applications and the bus may decide to throw an error condition. However,
    the QtDBus implementation does not use the C++ exception-throwing mechanism, so you will receive
    QDBusErrors in the return reply (see QDBusReply::error()).

    QDBusError objects are used to inspect the error name and message as received from the bus and
    remote applications. You should not create such objects yourself to signal error conditions when
    called from D-Bus: instead, use QDBusMessage::error and QDBusConnection::send.

    \sa QDBusConnection::send(), QDBusMessage, QDBusReply
*/

/*!
    \enum QDBusError::KnownErrors

    In order to facilitate verification of the most common D-Bus errors generated by the D-Bus
    implementation and by the bus daemon itself, QDBusError can be compared to a set of pre-defined
    values:

    \value NoError              QDBusError is invalid (i.e., the call succeeded)
    \value Other                QDBusError contains an error that is one of the well-known ones
    \value Failed               The call failed (\c org.freedesktop.DBus.Error.Failed)
    \value NoMemory             Out of memory (\c org.freedesktop.DBus.Error.NoMemory)
    \value ServiceUnknown       The called service is not known
                                (\c org.freedesktop.DBus.Error.ServiceUnknown)
    \value NoReply              The called method did not reply within the specified timeout
                                (\c org.freedesktop.DBus.Error.NoReply)
    \value BadAddress           The address given is not valid
                                (\c org.freedesktop.DBus.Error.BadAddress)
    \value NotSupported         The call/operation is not supported
                                (\c org.freedesktop.DBus.Error.NotSupported)
    \value LimitsExceeded       The limits allocated to this process/call/connection exceeded the
                                pre-defined values (\c org.freedesktop.DBus.Error.LimitsExceeded)
    \value AccessDenied         The call/operation tried to access a resource it isn't allowed to
                                (\c org.freedesktop.DBus.Error.AccessDenied)
    \value NoServer             \i{Documentation doesn't say what this is for}
                                (\c org.freedesktop.DBus.Error.NoServer)
    \value Timeout              \i{Documentation doesn't say what this is for or how it's used}
                                (\c org.freedesktop.DBus.Error.Timeout)
    \value NoNetwork            \i{Documentation doesn't say what this is for}
                                (\c org.freedesktop.DBus.Error.NoNetwork)
    \value AddressInUse         QDBusServer tried to bind to an address that is already in use
                                (\c org.freedesktop.DBus.Error.AddressInUse)
    \value Disconnected         The call/process/message was sent after QDBusConnection disconnected
                                (\c org.freedesktop.DBus.Error.Disconnected)
    \value InvalidArgs          The arguments passed to this call/operation are not valid
                                (\c org.freedesktop.DBus.Error.InvalidArgs)
    \value UnknownMethod        The method called was not found in this object/interface with the
                                given parameters (\c org.freedesktop.DBus.Error.UnknownMethod)
    \value TimedOut             \i{Documentation doesn't say...}
                                (\c org.freedesktop.DBus.Error.TimedOut)
    \value InvalidSignature     The type signature is not valid or compatible
                                (\c org.freedesktop.DBus.Error.InvalidSignature)
    \value UnknownInterface     The interface is not known
    \value InternalError        An internal error occurred
                                (\c com.trolltech.QtDBus.Error.InternalError)

*/

/*!
    \internal
    Constructs a QDBusError from a DBusError structure.
*/
QDBusError::QDBusError(const DBusError *error)
    : code(NoError)
{
    if (!error || !dbus_error_is_set(error))
        return;

    code = errorMessages()->get(error->name);
    nm = QString::fromUtf8(error->name);
    msg = QString::fromUtf8(error->message);
}

/*!
    \internal
    Constructs a QDBusError from a QDBusMessage.
*/
QDBusError::QDBusError(const QDBusMessage &qdmsg)
    : code(Other)
{
    if (qdmsg.type() != QDBusMessage::ErrorMessage)
        return;

    nm = qdmsg.name();
    if (qdmsg.count())
        msg = qdmsg[0].toString();
    code = errorMessages()->get(nm.toUtf8().constData());
}

/*!
    \internal
    Constructs a QDBusError from a well-known error code
*/
QDBusError::QDBusError(KnownErrors error, const QString &message)
    : code(error)
{
    nm = errorMessages()->get(error);
    msg = message;
}

/*!
    \fn QDBusError::name() const
    Returns this error's name. Error names are similar to D-Bus Interface names, like
    "org.freedesktop.DBus.InvalidArgs".
*/

/*!
    \fn QDBusError::message() const
    Returns the message that the callee associated with this error. Error messages are
    implementation defined and usually contain a human-readable error code, though this does not
    mean it is suitable for your end-users.
*/

/*!
    \fn QDBusError::isValid() const
    Returns true if this is a valid error condition (i.e., if there was an error), false otherwise.
*/

/*!
    \fn QDBusError::operator==(KnownErrors error) const
    Compares this QDBusError against the well-known error code \a error and returns true if they
    match.
*/

/*!
    \fn operator==(QDBusError::KnownErrors p1, const QDBusError &p2)
    \relates QDBusError

    Compares the QDBusError \a p2 against the well-known error code \a p1 and returns true if they
    match.
*/

#ifndef QT_NO_DEBUG
QDebug operator<<(QDebug dbg, const QDBusError &msg)
{
    dbg.nospace() << "QDBusError(" << msg.name() << ", " << msg.message() << ")";
    return dbg.space();
}
#endif



--- NEW FILE: qdbuserror.h ---
/* qdbuserror.h QDBusError object
 *
 * Copyright (C) 2005 Harald Fernengel <harry at kdevelop.org>
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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 QDBUSERROR_H
#define QDBUSERROR_H

#include "qdbusmacros.h"
#include <QtCore/qstring.h>

struct DBusError;
class QDBusMessage;

class QDBUS_EXPORT QDBusError
{
public:
    enum KnownErrors {
        NoError = 0,
        Other = 1,
        Failed,
        NoMemory,
        ServiceUnknown,
        NoReply,
        BadAddress,
        NotSupported,
        LimitsExceeded,
        AccessDenied,
        NoServer,
        Timeout,
        NoNetwork,
        AddressInUse,
        Disconnected,
        InvalidArgs,
        UnknownMethod,
        TimedOut,
        InvalidSignature,
        UnknownInterface,
        InternalError,

#ifndef Q_QDOC        
        // don't use this one!
        qKnownErrorsMax = InternalError
#endif
    };
    
    QDBusError(const DBusError *error = 0);
    QDBusError(const QDBusMessage& msg);
    QDBusError(KnownErrors error, const QString &message);

    inline QString name() const { return nm; }
    inline QString message() const { return msg; }
    inline bool isValid() const { return !nm.isNull() && !msg.isNull(); }

    inline bool operator==(KnownErrors error) const
    { return code == error; }

private:
    KnownErrors code;
    QString nm, msg;
};

inline bool operator==(QDBusError::KnownErrors p1, const QDBusError &p2)
{ return p2 == p1; }
inline bool operator!=(QDBusError::KnownErrors p1, const QDBusError &p2)
{ return !(p2 == p1); }
inline bool operator!=(const QDBusError &p1, QDBusError::KnownErrors p2)
{ return !(p1 == p2); }

#ifndef QT_NO_DEBUG
QDebug operator<<(QDebug, const QDBusError &);
#endif

#endif

--- NEW FILE: qdbusintegrator.cpp ---
/* qdbusintegrator.cpp QDBusConnection private implementation
 *
 * Copyright (C) 2005 Harald Fernengel <harry at kdevelop.org>
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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 <qcoreapplication.h>
#include <qcoreevent.h>
#include <qdebug.h>
#include <qmetaobject.h>
#include <qobject.h>
#include <qsocketnotifier.h>
#include <qstringlist.h>
#include <qtimer.h>

#include "qdbusconnection_p.h"
#include "qdbusinterface_p.h"
#include "qdbusmessage.h"
#include "qdbusabstractadaptor.h"
#include "qdbusabstractadaptor_p.h"
#include "qdbustypehelper_p.h"
#include "qdbusutil.h"
#include "qdbustype_p.h"

#ifndef USE_OUTSIDE_DISPATCH
# define USE_OUTSIDE_DISPATCH    0
#endif

int QDBusConnectionPrivate::messageMetaType = 0;

typedef void (*QDBusSpyHook)(const QDBusMessage&);
typedef QVarLengthArray<QDBusSpyHook, 4> QDBusSpyHookList;
Q_GLOBAL_STATIC(QDBusSpyHookList, qDBusSpyHookList)

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;
};

static dbus_bool_t qDBusAddTimeout(DBusTimeout *timeout, void *data)
{
    Q_ASSERT(timeout);
    Q_ASSERT(data);

  //  qDebug("addTimeout %d", dbus_timeout_get_interval(timeout));

    QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);

    if (!dbus_timeout_get_enabled(timeout))
        return true;

    if (!QCoreApplication::instance()) {
        d->pendingTimeouts.append(timeout);
        return true;
    }
    int timerId = d->startTimer(dbus_timeout_get_interval(timeout));
    if (!timerId)
        return false;

    d->timeouts[timerId] = timeout;
    return true;
}

static void qDBusRemoveTimeout(DBusTimeout *timeout, void *data)
{
    Q_ASSERT(timeout);
    Q_ASSERT(data);

  //  qDebug("removeTimeout");

    QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
    d->pendingTimeouts.removeAll(timeout);

    QDBusConnectionPrivate::TimeoutHash::iterator it = d->timeouts.begin();
    while (it != d->timeouts.end()) {
        if (it.value() == timeout) {
            d->killTimer(it.key());
            it = d->timeouts.erase(it);
        } else {
            ++it;
        }
    }
}

static void qDBusToggleTimeout(DBusTimeout *timeout, void *data)
{
    Q_ASSERT(timeout);
    Q_ASSERT(data);

    //qDebug("ToggleTimeout");

    qDBusRemoveTimeout(timeout, data);
    qDBusAddTimeout(timeout, data);
}

static dbus_bool_t qDBusAddWatch(DBusWatch *watch, void *data)
{
    Q_ASSERT(watch);
    Q_ASSERT(data);

    QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);

    int flags = dbus_watch_get_flags(watch);
    int fd = dbus_watch_get_fd(watch);

    QDBusConnectionPrivate::Watcher watcher;
    if (flags & DBUS_WATCH_READABLE) {
        //qDebug("addReadWatch %d", fd);
        watcher.watch = watch;
        if (QCoreApplication::instance()) {
            watcher.read = new QSocketNotifier(fd, QSocketNotifier::Read, d);
            watcher.read->setEnabled(dbus_watch_get_enabled(watch));
            d->connect(watcher.read, SIGNAL(activated(int)), SLOT(socketRead(int)));
        }
    }
    if (flags & DBUS_WATCH_WRITABLE) {
        //qDebug("addWriteWatch %d", fd);
        watcher.watch = watch;
        if (QCoreApplication::instance()) {
            watcher.write = new QSocketNotifier(fd, QSocketNotifier::Write, d);
            watcher.write->setEnabled(dbus_watch_get_enabled(watch));
            d->connect(watcher.write, SIGNAL(activated(int)), SLOT(socketWrite(int)));
        }
    }
    d->watchers.insertMulti(fd, watcher);

    return true;
}

static void qDBusRemoveWatch(DBusWatch *watch, void *data)
{
    Q_ASSERT(watch);
    Q_ASSERT(data);

    //qDebug("remove watch");

    QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
    int fd = dbus_watch_get_fd(watch);

    QDBusConnectionPrivate::WatcherHash::iterator i = d->watchers.find(fd);
    while (i != d->watchers.end() && i.key() == fd) {
        if (i.value().watch == watch) {
            delete i.value().read;
            delete i.value().write;
            d->watchers.erase(i);
            return;
        }
        ++i;
    }
}

static void qDBusToggleWatch(DBusWatch *watch, void *data)
{
    Q_ASSERT(watch);
    Q_ASSERT(data);

    QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
    int fd = dbus_watch_get_fd(watch);

    QDBusConnectionPrivate::WatcherHash::iterator i = d->watchers.find(fd);
    while (i != d->watchers.end() && i.key() == fd) {
        if (i.value().watch == watch) {
            bool enabled = dbus_watch_get_enabled(watch);
            int flags = dbus_watch_get_flags(watch);

            //qDebug("toggle watch %d to %d (write: %d, read: %d)", dbus_watch_get_fd(watch), enabled, flags & DBUS_WATCH_WRITABLE, flags & DBUS_WATCH_READABLE);

            if (flags & DBUS_WATCH_READABLE && i.value().read)
                i.value().read->setEnabled(enabled);
            if (flags & DBUS_WATCH_WRITABLE && i.value().write)
                i.value().write->setEnabled(enabled);
            return;
        }
        ++i;
    }
}

static void qDBusNewConnection(DBusServer *server, DBusConnection *c, void *data)
{
    Q_ASSERT(data); Q_ASSERT(server); Q_ASSERT(c);
    Q_UNUSED(data); Q_UNUSED(server); Q_UNUSED(c);

    qDebug("SERVER: GOT A NEW CONNECTION"); // TODO
}

extern QDBUS_EXPORT void qDBusAddSpyHook(QDBusSpyHook);
void qDBusAddSpyHook(QDBusSpyHook hook)
{
    qDBusSpyHookList()->append(hook);
}

#if USE_OUTSIDE_DISPATCH
# define HANDLED     DBUS_HANDLER_RESULT_HANDLED_OUTSIDE_DISPATCH
static DBusHandlerResult qDBusSignalFilterOutside(DBusConnection *connection,
                                                  DBusMessage *message, void *data)
{
    Q_ASSERT(data);
    Q_UNUSED(connection);
    Q_UNUSED(message);

    QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
    if (d->mode == QDBusConnectionPrivate::InvalidMode)
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; // internal error, actually

    CallDeliveryEvent *e = d->postedCallDeliveryEvent();

    d->deliverCall(*e);
    delete e;

    return DBUS_HANDLER_RESULT_HANDLED;
}
#else
# define HANDLED     DBUS_HANDLER_RESULT_HANDLED
#endif

extern "C" {
static DBusHandlerResult
qDBusSignalFilter(DBusConnection *connection, DBusMessage *message, void *data)
{
    return QDBusConnectionPrivate::messageFilter(connection, message, data);
}
}

DBusHandlerResult QDBusConnectionPrivate::messageFilter(DBusConnection *connection,
                                                        DBusMessage *message, void *data)
{
    Q_ASSERT(data);
    Q_UNUSED(connection);

    QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
    if (d->mode == QDBusConnectionPrivate::InvalidMode)
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;

    QDBusMessage amsg = QDBusMessage::fromDBusMessage(message, QDBusConnection(d->name));
    qDebug() << "got message:" << amsg;

    const QDBusSpyHookList *list = qDBusSpyHookList();
    for (int i = 0; i < list->size(); ++i) {
        qDebug() << "calling the message spy hook";
        (*(*list)[i])(amsg);
    }

    bool handled = false;
    int msgType = dbus_message_get_type(message);
    if (msgType == DBUS_MESSAGE_TYPE_SIGNAL) {
        handled = d->handleSignal(amsg);
    } else if (msgType == DBUS_MESSAGE_TYPE_METHOD_CALL) {
        handled = d->handleObjectCall(amsg);
    }

    return handled ? HANDLED :
        DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}

static void huntAndDestroy(QObject *needle, QDBusConnectionPrivate::ObjectTreeNode *haystack)
{
    foreach (const QDBusConnectionPrivate::ObjectTreeNode::Data &entry, haystack->children)
        huntAndDestroy(needle, entry.node);

    if (needle == haystack->obj) {
        haystack->obj = 0;
        haystack->flags = 0;
    }
}

static void huntAndEmit(DBusConnection *connection, DBusMessage *msg,
                        QObject *needle, QDBusConnectionPrivate::ObjectTreeNode *haystack,
                        const QString &path = QString())
{
    foreach (const QDBusConnectionPrivate::ObjectTreeNode::Data &entry, haystack->children)
        huntAndEmit(connection, msg, needle, entry.node, path + QLatin1String("/") + entry.name);

    if (needle == haystack->obj && haystack->flags & QDBusConnection::ExportAdaptors) {
        QByteArray p = path.toLatin1();
        if (p.isEmpty())
            p = "/";
        //qDebug() << p;
        DBusMessage *msg2 = dbus_message_copy(msg);
        dbus_message_set_path(msg2, p);
        dbus_connection_send(connection, msg2, 0);
        dbus_message_unref(msg2);
    }
}

static bool typesMatch(int metaId, int variantType)
{
    if (metaId == int(variantType))
        return true;

    if (variantType == QVariant::Int && metaId == QMetaType::Short)
        return true;

    if (variantType == QVariant::UInt && (metaId == QMetaType::UShort ||
                                          metaId == QMetaType::UChar))
        return true;

    if (variantType == QVariant::List) {
        if (metaId == QDBusTypeHelper<bool>::listId() ||
            metaId == QDBusTypeHelper<short>::listId() ||
            metaId == QDBusTypeHelper<ushort>::listId() || 
            metaId == QDBusTypeHelper<int>::listId() ||
            metaId == QDBusTypeHelper<uint>::listId() ||
            metaId == QDBusTypeHelper<qlonglong>::listId() ||
            metaId == QDBusTypeHelper<qulonglong>::listId() ||
            metaId == QDBusTypeHelper<double>::listId())
            return true;
    }

    return false;               // no match
}

static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags,
                    const QDBusTypeList &types, QList<int>& metaTypes)
{
    // find the first slot
    const QMetaObject *super = mo;
    while (super != &QObject::staticMetaObject &&
           super != &QDBusAbstractAdaptor::staticMetaObject)
        super = super->superClass();

    int attributeMask = (flags & QDBusConnection::ExportAllSlots) ?
                        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 = qDBusNameToTypeId(mm.typeName());
        bool isAsync = qDBusCheckAsyncTag(mm.tag());

        // consistency check:
        if (isAsync && returnType != QMetaType::Void)
            continue;

        int inputCount = qDBusParametersForMethod(mm, metaTypes);
        if (inputCount == -1)
            continue;           // problem parsing

        metaTypes[0] = returnType;
        bool hasMessage = false;
        if (inputCount > 0 &&
            metaTypes.at(inputCount) == QDBusConnectionPrivate::messageMetaType) {
            // "no input parameters" is allowed as long as the message meta type is there
            hasMessage = true;
            --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 (hasMessage && (mm.attributes() & attributeMask) != attributeMask)
            continue;           // not exported

        // if we got here, this slot matched
        return idx;
    }

    // no slot matched
    return -1;
}

static CallDeliveryEvent* prepareReply(QObject *object, int idx, const QList<int> &metaTypes,
                                       const QDBusMessage &msg)
{
    Q_ASSERT(object);

    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 0;           // no match

    // we can deliver
    // prepare for the call
    CallDeliveryEvent *data = new CallDeliveryEvent;
    data->object = object;
    data->flags = 0;
    data->message = msg;
    data->metaTypes = metaTypes;
    data->slotIdx = idx;

    return data;
}

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)
    CallDeliveryEvent *call = prepareReply(hook.obj, hook.midx, hook.params, msg);
    if (call) {
        postCallDeliveryEvent(call);
        return true;
    }
    return false;
}

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.
    //
    // The D-Bus specification requires that all MethodCall messages be replied to, unless the
    // caller specifically waived this requirement. This means that we inspect if the user slot
    // generated a reply and, if it didn't, we will. Obviously, if the user slot doesn't take a
    // QDBusMessage parameter, it cannot generate a reply.
    //
    // When a return message is generated, the slot's return type, if any, will be placed
    // in the message's first position. If there are non-const reference parameters to the
    // slot, they must appear at the end and will be placed in the subsequent message
    // positions.

    if (!object)
        return false;

    QList<int> metaTypes;
    int idx;

    {
        const QMetaObject *mo = object->metaObject();
        QDBusTypeList typeList(msg.signature().toUtf8());
        QByteArray name = msg.name().toUtf8();

        // find a slot that matches according to the rules above
        idx = ::findSlot(mo, name, flags, typeList, metaTypes);
        if (idx == -1) {
            // try with no parameters, but with a QDBusMessage
            idx = ::findSlot(mo, name, flags, QDBusTypeList(), metaTypes);
            if (metaTypes.count() != 2 || metaTypes.at(1) != messageMetaType)
                return false;
        }
    }

    // found the slot to be called
    // prepare for the call:
    CallDeliveryEvent *call = new CallDeliveryEvent;

    // parameters:
    call->object = object;
    call->flags = flags;
    call->message = msg;

    // save our state:
    call->metaTypes = metaTypes;
    call->slotIdx = idx;

    postCallDeliveryEvent(call);

    // ready
    return true;
}

void QDBusConnectionPrivate::postCallDeliveryEvent(CallDeliveryEvent *data)
{
    Q_ASSERT(data);
    data->conn = this;    
#if USE_OUTSIDE_DISPATCH
    callDeliveryMutex.lock();
    callDeliveryState = data;
#else
    QCoreApplication::postEvent( this, data );
#endif
}

CallDeliveryEvent *QDBusConnectionPrivate::postedCallDeliveryEvent()
{
    CallDeliveryEvent *e = callDeliveryState;
    Q_ASSERT(e && e->conn == this);

    // release it:
    callDeliveryState = 0;
    callDeliveryMutex.unlock();

    return e;
}

void QDBusConnectionPrivate::deliverCall(const CallDeliveryEvent& data) const
{
    // resume state:
    const QList<int>& metaTypes = data.metaTypes;
    const QDBusMessage& msg = data.message;

    QVarLengthArray<void *, 10> params;
    params.reserve(metaTypes.count());

    QVariantList auxParameters;
    // let's create the parameter list

    // first one is the return type -- add it below
    params.append(0);

    // add the input parameters
    int i;
    for (i = 1; i <= msg.count(); ++i) {
        int id = metaTypes[i];
        if (id == QDBusConnectionPrivate::messageMetaType)
            break;

        if (id == int(msg.at(i - 1).userType()))
            // no conversion needed
            params.append(const_cast<void *>( msg.at(i - 1).constData() ));
        else {
            // convert to what the function expects
            auxParameters.append(QVariant());
            
            const QVariant &in = msg.at(i - 1);
            QVariant &out = auxParameters[auxParameters.count() - 1];

            bool error = false;
            if (id == QVariant::List) {
                int mid = in.userType();
                // the only conversion possible here is from a specialised QList<T> to QVariantList
                if (mid == QDBusTypeHelper<bool>::listId())
                    out = qVariantFromValue(QDBusTypeHelper<bool>::toVariantList(in));
                else if (mid == QDBusTypeHelper<short>::listId())
                    out = qVariantFromValue(QDBusTypeHelper<short>::toVariantList(in));
                else if (mid == QDBusTypeHelper<ushort>::listId())
                    out = qVariantFromValue(QDBusTypeHelper<ushort>::toVariantList(in));
                else if (mid == QDBusTypeHelper<int>::listId())
                    out = qVariantFromValue(QDBusTypeHelper<int>::toVariantList(in));
                else if (mid == QDBusTypeHelper<uint>::listId())
                    out = qVariantFromValue(QDBusTypeHelper<uint>::toVariantList(in));
                else if (mid == QDBusTypeHelper<qlonglong>::listId())
                    out = qVariantFromValue(QDBusTypeHelper<qlonglong>::toVariantList(in));
                else if (mid == QDBusTypeHelper<qulonglong>::listId())
                    out = qVariantFromValue(QDBusTypeHelper<qulonglong>::toVariantList(in));
                else if (mid == QDBusTypeHelper<double>::listId())
                    out = qVariantFromValue(QDBusTypeHelper<double>::toVariantList(in));
                else
                    error = true;
            } else if (in.type() == QVariant::UInt) {
                if (id == QMetaType::UChar) {
                    uchar uc = in.toUInt();
                    out = qVariantFromValue(uc);
                } else if (id == QMetaType::UShort) {
                    ushort us = in.toUInt();
                    out = qVariantFromValue(us);
                } else {
                    error = true;
                }
            } else if (in.type() == QVariant::Int) {
                if (id == QMetaType::Short) {
                    short s = in.toInt();
                    out = qVariantFromValue(s);
                } else {
                    error = true;
                }
            } else {
                error = true;
            }

            if (error)
                qFatal("Internal error: got invalid meta type %d when trying to convert to meta type %d",
                       in.userType(), id);

            params.append( const_cast<void *>(out.constData()) );
        }
    }

    bool takesMessage = false;
    if (metaTypes.count() > i && metaTypes[i] == 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);
        outputArgs.append( arg );
        params[0] = const_cast<void*>(outputArgs.at( outputArgs.count() - 1 ).constData());
    }
    for ( ; i < metaTypes.count(); ++i) {
        QVariant arg(metaTypes[i], null);
        outputArgs.append( arg );
        params.append( const_cast<void*>(outputArgs.at( outputArgs.count() - 1 ).constData()) );
    }

    // make call:
    bool fail;
    if (data.object.isNull())
        fail = true;
    else
        fail = data.object->qt_metacall(QMetaObject::InvokeMetaMethod,
                                        data.slotIdx, params.data()) >= 0;

    // do we create a reply? Only if the caller is waiting for a reply and one hasn't been sent
    // yet.
    if (!msg.noReply() && !msg.wasRepliedTo()) {
        if (!fail) {
            // normal reply
            QDBusMessage reply = QDBusMessage::methodReply(msg);
            reply += outputArgs;

            qDebug() << "Automatically sending reply:" << reply;
            send(reply);
        }
        else {
            // generate internal error
            QDBusMessage reply = QDBusMessage::error(msg, QDBusError(QDBusError::InternalError,
                    QLatin1String("Failed to deliver message")));
            qWarning("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), busService(0)
{
    extern bool qDBusInitThreads();
    static const int msgType = registerMessageMetaType();
    static const bool threads = qDBusInitThreads();
    static const bool metatypes = QDBusMetaTypeId::innerInitialize();

    Q_UNUSED(msgType);
    Q_UNUSED(threads);
    Q_UNUSED(metatypes);

    dbus_error_init(&error);

    rootNode.flags = 0;
}

QDBusConnectionPrivate::~QDBusConnectionPrivate()
{
    if (dbus_error_is_set(&error))
        dbus_error_free(&error);

    closeConnection();
    rootNode.clear();        // free resources
    qDeleteAll(cachedMetaObjects);
}

void QDBusConnectionPrivate::closeConnection()
{
    QWriteLocker locker(&lock);
    ConnectionMode oldMode = mode;
    mode = InvalidMode; // prevent reentrancy
    if (oldMode == ServerMode) {
        if (server) {
            dbus_server_disconnect(server);
            dbus_server_unref(server);
            server = 0;
        }
    } else if (oldMode == ClientMode) {
        if (connection) {
            dbus_connection_close(connection);
            // send the "close" message
            while (dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS)
                ;
            dbus_connection_unref(connection);
            connection = 0;
        }
    }
}

bool QDBusConnectionPrivate::handleError()
{
    lastError = QDBusError(&error);
    if (dbus_error_is_set(&error))
        dbus_error_free(&error);
    return lastError.isValid();
}

void QDBusConnectionPrivate::bindToApplication()
{
    // Yay, now that we have an application we are in business
    Q_ASSERT_X(QCoreApplication::instance(), "QDBusConnection",
               "qDBusBindToApplication called without an application");
    moveToThread(QCoreApplication::instance()->thread());
    
    // Re-add all watchers
    WatcherHash oldWatchers = watchers;
    watchers.clear();
    QHashIterator<int, QDBusConnectionPrivate::Watcher> it(oldWatchers);
    while (it.hasNext()) {
        it.next();
        if (!it.value().read && !it.value().write) {
            qDBusAddWatch(it.value().watch, this);
        } else {
            watchers.insertMulti(it.key(), it.value());
        }
    }

    // Re-add all timeouts
    while (!pendingTimeouts.isEmpty())
       qDBusAddTimeout(pendingTimeouts.takeFirst(), this);
}

void QDBusConnectionPrivate::timerEvent(QTimerEvent *e)
{
    DBusTimeout *timeout = timeouts.value(e->timerId(), 0);
    dbus_timeout_handle(timeout);
}

void QDBusConnectionPrivate::doDispatch()
{
    if (mode == ClientMode)
        while (dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS);
}

void QDBusConnectionPrivate::socketRead(int fd)
{
    QHashIterator<int, QDBusConnectionPrivate::Watcher> it(watchers);
    while (it.hasNext()) {
        it.next();
        if (it.key() == fd && it.value().read && it.value().read->isEnabled()) {
            if (!dbus_watch_handle(it.value().watch, DBUS_WATCH_READABLE))
                qDebug("OUT OF MEM");
        }
    }

    doDispatch();
}

void QDBusConnectionPrivate::socketWrite(int fd)
{
    QHashIterator<int, QDBusConnectionPrivate::Watcher> it(watchers);
    while (it.hasNext()) {
        it.next();
        if (it.key() == fd && it.value().write && it.value().write->isEnabled()) {
            if (!dbus_watch_handle(it.value().watch, DBUS_WATCH_WRITABLE))
                qDebug("OUT OF MEM");
        }
    }
}

void QDBusConnectionPrivate::objectDestroyed(QObject *obj)
{
    QWriteLocker locker(&lock);
    huntAndDestroy(obj, &rootNode);

    SignalHookHash::iterator sit = signalHooks.begin();
    while (sit != signalHooks.end()) {
        if (static_cast<QObject *>(sit.value().obj) == obj)
            sit = signalHooks.erase(sit);
        else
            ++sit;
    }

    obj->disconnect(this);
}

void QDBusConnectionPrivate::relaySignal(QObject *obj, const char *interface, const char *name,
                                         const QVariantList &args)
{
    QReadLocker locker(&lock);
    QDBusMessage message = QDBusMessage::signal(QLatin1String("/"), QLatin1String(interface),
                                                QLatin1String(name));
    message += args;
    DBusMessage *msg = message.toDBusMessage();
    if (!msg) {
        qWarning("Could not emit signal %s.%s", interface, name);
        return;
    }

    //qDebug() << "Emitting signal" << message;
    //qDebug() << "for paths:";
    dbus_message_set_no_reply(msg, true); // the reply would not be delivered to anything
    huntAndEmit(connection, msg, obj, &rootNode);
    dbus_message_unref(msg);
}

int QDBusConnectionPrivate::registerMessageMetaType()
{
    int tp = messageMetaType = qRegisterMetaType<QDBusMessage>("QDBusMessage");
    return tp;
}

int QDBusConnectionPrivate::findSlot(QObject* obj, const QByteArray &normalizedName,
                                     QList<int> &params)
{
    int midx = obj->metaObject()->indexOfMethod(normalizedName);
    if (midx == -1) {
        qWarning("No such slot '%s' while connecting D-Bus", normalizedName.constData());
        return -1;
    }

    int inputCount = qDBusParametersForMethod(obj->metaObject()->method(midx), params);
    if ( inputCount == -1 || inputCount + 1 != params.count() )
        return -1;              // failed to parse or invalid arguments or output arguments

    return midx;
}

bool QDBusConnectionPrivate::prepareHook(QDBusConnectionPrivate::SignalHook &hook, QString &key,
                                         const QString &service, const QString &path,
                                         const QString &interface, const QString &name,
                                         QObject *receiver, const char *signal, int minMIdx,
                                         bool buildSignature)
{
    QByteArray normalizedName = QMetaObject::normalizedSignature(signal + 1);
    hook.midx = findSlot(receiver, normalizedName, hook.params);
    if (hook.midx < minMIdx)
        return false;

    hook.sender = service;
    hook.path = path;
    hook.obj = receiver;

    // build the D-Bus signal name and signature
    QString mname = name;
    if (mname.isEmpty()) {
        normalizedName.truncate(normalizedName.indexOf('('));        
        mname = QString::fromUtf8(normalizedName);
    }
    key = mname;
    key.reserve(interface.length() + 1 + mname.length());
    key += ':';
    key += interface;

    if (buildSignature) {
        hook.signature.clear();
        for (int i = 1; i < hook.params.count(); ++i)
            if (hook.params.at(i) != messageMetaType)
                hook.signature += QLatin1String( QDBusType::dbusSignature( QVariant::Type(hook.params.at(i)) ) );
    }
    
    return true;                // connect to this signal
}

bool QDBusConnectionPrivate::activateInternalFilters(const ObjectTreeNode *node, const QDBusMessage &msg)
{
    // object may be null

    if (msg.interface().isEmpty() || msg.interface() == QLatin1String(DBUS_INTERFACE_INTROSPECTABLE)) {
        if (msg.method() == QLatin1String("Introspect") && msg.signature().isEmpty())
            qDBusIntrospectObject(node, msg);
        if (msg.interface() == QLatin1String(DBUS_INTERFACE_INTROSPECTABLE))
            return true;
    }

    if (node->obj && (msg.interface().isEmpty() ||
                      msg.interface() == QLatin1String(DBUS_INTERFACE_PROPERTIES))) {
        if (msg.method() == QLatin1String("Get") && msg.signature() == QLatin1String("ss"))
            qDBusPropertyGet(node, msg);
        else if (msg.method() == QLatin1String("Set") && msg.signature() == QLatin1String("ssv"))
            qDBusPropertySet(node, msg);

        if (msg.interface() == QLatin1String(DBUS_INTERFACE_PROPERTIES))
            return true;
    }

    return false;
}

bool QDBusConnectionPrivate::activateObject(const ObjectTreeNode *node, const QDBusMessage &msg)
{
    // This is called by QDBusConnectionPrivate::handleObjectCall to place a call to a slot
    // on the object.
    //
    // The call is routed through the adaptor sub-objects if we have any

    // object may be null

    QDBusAdaptorConnector *connector;
    if (node->flags & QDBusConnection::ExportAdaptors &&
        (connector = qDBusFindAdaptorConnector(node->obj))) {
        int newflags = node->flags | QDBusConnection::ExportAllSlots;

        if (msg.interface().isEmpty()) {
            // place the call in all interfaces
            // let the first one that handles it to work
            foreach (const QDBusAdaptorConnector::AdaptorData &entry, connector->adaptors)
                if (activateCall(entry.adaptor, newflags, msg))
                    return true;
        } else {
            // check if we have an interface matching the name that was asked:
            QDBusAdaptorConnector::AdaptorMap::ConstIterator it;
            it = qLowerBound(connector->adaptors.constBegin(), connector->adaptors.constEnd(),
                             msg.interface());
            if (it != connector->adaptors.end() && it->interface == msg.interface())
                if (activateCall(it->adaptor, newflags, msg))
                return true;
        }
    }

    // no adaptors matched
    // try our standard filters
    if (activateInternalFilters(node, msg))
        return true;

    // try the object itself:
    if (node->flags & QDBusConnection::ExportSlots && activateCall(node->obj, node->flags, msg))
        return true;
#if 0
    // nothing matched
    qDebug("Call failed: no match for %s%s%s at %s",
           qPrintable(msg.interface()), msg.interface().isEmpty() ? "" : ".",
           qPrintable(msg.name()),
           qPrintable(msg.path()));
#endif
    return false;
}

template<typename Func>
static bool applyForObject(QDBusConnectionPrivate::ObjectTreeNode *root, const QString &fullpath,
                           Func& functor)
{
    // walk the object tree
    QStringList path = fullpath.split(QLatin1Char('/'));
    if (path.last().isEmpty())
        path.removeLast();      // happens if path is "/"
    int i = 1;
    QDBusConnectionPrivate::ObjectTreeNode *node = root;

    // try our own tree first
    while (node && !(node->flags & QDBusConnection::ExportChildObjects) ) {
        if (i == path.count()) {
            // found our object
            functor(node);
            return true;
        }

        QVector<QDBusConnectionPrivate::ObjectTreeNode::Data>::ConstIterator it =
            qLowerBound(node->children.constBegin(), node->children.constEnd(), path.at(i));
        if (it != node->children.constEnd() && it->name == path.at(i))
            // match
            node = it->node;
        else
            node = 0;

        ++i;
    }

    // any object in the tree can tell us to switch to its own object tree:
    if (node && node->flags & QDBusConnection::ExportChildObjects) {
        QObject *obj = node->obj;

        while (obj) {
            if (i == path.count()) {
                // we're at the correct level
                QDBusConnectionPrivate::ObjectTreeNode fakenode(*node);
                fakenode.obj = obj;
                functor(&fakenode);
                return true;
            }

            const QObjectList children = obj->children();

            // find a child with the proper name
            QObject *next = 0;
            foreach (QObject *child, children)
                if (child->objectName() == path.at(i)) {
                    next = child;
                    break;
                }

            if (!next)
                break;

            ++i;
            obj = next;
        }
    }

    // object not found
    return false;
}

struct qdbus_activateObject
{
    QDBusConnectionPrivate *self;
    const QDBusMessage &msg;
    bool returnVal;
    inline qdbus_activateObject(QDBusConnectionPrivate *s, const QDBusMessage &m)
        : self(s), msg(m)
    { }

    inline void operator()(QDBusConnectionPrivate::ObjectTreeNode *node)
    { returnVal = self->activateObject(node, msg); }
};

bool QDBusConnectionPrivate::handleObjectCall(const QDBusMessage &msg)
{
    QReadLocker locker(&lock);

    qdbus_activateObject apply(this, msg);
    if (applyForObject(&rootNode, msg.path(), apply))
        return apply.returnVal;

    qDebug("Call failed: no object found at %s", qPrintable(msg.path()));
    return false;
}

bool QDBusConnectionPrivate::handleSignal(const QString &key, const QDBusMessage& msg)
{
    bool result = false;
    SignalHookHash::const_iterator it = signalHooks.find(key);
    //qDebug("looking for: %s", path.toLocal8Bit().constData());
    //qDebug() << signalHooks.keys();
    for ( ; it != signalHooks.constEnd() && it.key() == key; ++it) {
        const SignalHook &hook = it.value();
        if ( !hook.sender.isEmpty() && hook.sender != msg.sender() )
            continue;
        if ( !hook.path.isEmpty() && hook.path != msg.path() )
            continue;
        if ( !hook.signature.isEmpty() && hook.signature != msg.signature() )
            continue;
        if ( hook.signature.isEmpty() && !hook.signature.isNull() && !msg.signature().isEmpty())
            continue;

        // yes, |=
        result |= activateSignal(hook, msg);
    }
    return result;
}

bool QDBusConnectionPrivate::handleSignal(const QDBusMessage& msg)
{
    QString key = msg.member();
    key.reserve(key.length() + 1 + msg.interface().length());
    key += ':';
    key += msg.interface();

    QReadLocker locker(&lock);
    bool result = handleSignal(key, msg);    // one try

    key.truncate(msg.member().length() + 1); // keep the ':'
    result |= handleSignal(key, msg);        // second try
    return result;
}

static dbus_int32_t server_slot = -1;

void QDBusConnectionPrivate::setServer(DBusServer *s)
{
    if (!server) {
        handleError();
        return;
    }

    server = s;
    mode = ServerMode;

    dbus_server_allocate_data_slot(&server_slot);
    if (server_slot < 0)
        return;

    dbus_server_set_watch_functions(server, qDBusAddWatch, qDBusRemoveWatch,
                                    qDBusToggleWatch, this, 0); // ### check return type?
    dbus_server_set_timeout_functions(server, qDBusAddTimeout, qDBusRemoveTimeout,
                                      qDBusToggleTimeout, this, 0);
    dbus_server_set_new_connection_function(server, qDBusNewConnection, this, 0);

    dbus_server_set_data(server, server_slot, this, 0);
}

void QDBusConnectionPrivate::setConnection(DBusConnection *dbc)
{
    if (!dbc) {
        handleError();
        return;
    }

    connection = dbc;
    mode = ClientMode;

    dbus_connection_set_exit_on_disconnect(connection, false);
    dbus_connection_set_watch_functions(connection, qDBusAddWatch, qDBusRemoveWatch,
                                        qDBusToggleWatch, this, 0);
    dbus_connection_set_timeout_functions(connection, qDBusAddTimeout, qDBusRemoveTimeout,
                                          qDBusToggleTimeout, this, 0);
//    dbus_bus_add_match(connection, "type='signal',interface='com.trolltech.dbus.Signal'", &error);
//    dbus_bus_add_match(connection, "type='signal'", &error);

    dbus_bus_add_match(connection, "type='signal'", &error);
    if (handleError()) {
        closeConnection();
        return;
    }

    const char *service = dbus_bus_get_unique_name(connection);
    if (service) {
        QVarLengthArray<char, 56> filter;
        filter.append("destination='", 13);
        filter.append(service, qstrlen(service));
        filter.append("\'\0", 2);

        dbus_bus_add_match(connection, filter.constData(), &error);
        if (handleError()) {
            closeConnection();
            return;
        }
    } else {
        qWarning("QDBusConnectionPrivate::SetConnection: Unable to get base service");
    }

#if USE_OUTSIDE_DISPATCH
    dbus_connection_add_filter_outside(connection, qDBusSignalFilter, qDBusSignalFilterOutside, this, 0);
#else
    dbus_connection_add_filter(connection, qDBusSignalFilter, this, 0);
#endif

    //qDebug("base service: %s", service);

    // schedule a dispatch:
    QMetaObject::invokeMethod(this, "doDispatch", Qt::QueuedConnection);
}

extern "C"{
static void qDBusResultReceived(DBusPendingCall *pending, void *user_data)
{
    QDBusConnectionPrivate::messageResultReceived(pending, user_data);
}
}

void QDBusConnectionPrivate::messageResultReceived(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);

        // Deliver the return values of a remote function call.
        //
        // There is only one connection and it is specified by idx
        // The slot must have the same parameter types that the message does
        // The slot may have less parameters than the message
        // The slot may optionally have one final parameter that is QDBusMessage
        // The slot receives read-only copies of the message (i.e., pass by value or by const-ref)

        QDBusMessage msg = QDBusMessage::fromDBusMessage(reply, QDBusConnection(connection->name));
        qDebug() << "got message: " << msg;
        CallDeliveryEvent *e = prepareReply(call->receiver, call->methodIdx, call->metaTypes, msg);
        if (e)
            connection->postCallDeliveryEvent(e);
        else
            qDebug() << "Deliver failed!";
    }
    dbus_pending_call_unref(pending);
    delete call;
}

int QDBusConnectionPrivate::send(const QDBusMessage& message) const
{
    DBusMessage *msg = message.toDBusMessage();
    if (!msg)
        return 0;

    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);
    int serial = 0;
    if (isOk)
        serial = dbus_message_get_serial(msg);

    dbus_message_unref(msg);
    return serial;
}

QDBusMessage QDBusConnectionPrivate::sendWithReply(const QDBusMessage &message,
                                                   int mode)
{
    if (!QCoreApplication::instance() || mode == QDBusConnection::NoUseEventLoop) {
        DBusMessage *msg = message.toDBusMessage();
        if (!msg)
            return QDBusMessage();

        qDebug() << "sending message:" << message;
        DBusMessage *reply = dbus_connection_send_with_reply_and_block(connection, msg,
                                                                       -1, &error);
        handleError();
        dbus_message_unref(msg);

        if (lastError.isValid())
            return QDBusMessage::fromError(lastError);

        QDBusMessage amsg = QDBusMessage::fromDBusMessage(reply, QDBusConnection(name));
        qDebug() << "got message:" << amsg;

        if (dbus_connection_get_dispatch_status(connection) == DBUS_DISPATCH_DATA_REMAINS)
            QMetaObject::invokeMethod(this, "doDispatch", Qt::QueuedConnection);
        return amsg;
    } else {                    // use the event loop
        QDBusReplyWaiter waiter;
        if (sendWithReplyAsync(message, &waiter, SLOT(reply(const QDBusMessage&))) > 0) {
            // enter the event loop and wait for a reply
            waiter.exec(QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents);

            lastError = waiter.replyMsg; // set or clear error
            return waiter.replyMsg;
        }

        return QDBusMessage();
    }
}    

int QDBusConnectionPrivate::sendWithReplyAsync(const QDBusMessage &message, QObject *receiver,
                                               const char *method)
{
    if (!receiver || !method || !*method)
        // would not be able to deliver a reply
        return send(message);

    int slotIdx = -1;
    QList<int> metaTypes;
    QByteArray normalizedName = QMetaObject::normalizedSignature(method + 1);
    slotIdx = findSlot(receiver, normalizedName, metaTypes);
    if (slotIdx == -1)
        // would not be able to deliver a reply
        return send(message);

    DBusMessage *msg = message.toDBusMessage();
    if (!msg)
        return 0;

    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;
}

void QDBusConnectionPrivate::connectSignal(const QString &key, const SignalHook &hook)
{
    signalHooks.insertMulti(key, hook);
    connect(hook.obj, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*)));
}

void QDBusConnectionPrivate::registerObject(const ObjectTreeNode *node)
{
    connect(node->obj, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*)));

    if (node->flags & QDBusConnection::ExportAdaptors) {
        QDBusAdaptorConnector *connector = qDBusCreateAdaptorConnector(node->obj);

        // disconnect and reconnect to avoid duplicates
        connector->disconnect(SIGNAL(relaySignal(QObject*,const char*,const char*,QVariantList)),
                              this, SLOT(relaySignal(QObject*,const char*,const char*,QVariantList)));
        connect(connector, SIGNAL(relaySignal(QObject*,const char*,const char*,QVariantList)),
                SLOT(relaySignal(QObject*,const char*,const char*,QVariantList)));
    }
}

void QDBusConnectionPrivate::connectRelay(const QString &service, const QString &path,
                                          const QString &interface,
                                          QDBusAbstractInterface *receiver,
                                          const char *signal)
{
    // this function is called by QDBusAbstractInterface when one of its signals is connected
    // we set up a relay from D-Bus into it
    SignalHook hook;
    QString key;
    if (!prepareHook(hook, key, service, path, interface, QString(), receiver, signal,
                     QDBusAbstractInterface::staticMetaObject.methodCount(), true))
        return;                 // don't connect

    // add it to our list:
    QWriteLocker locker(&lock);
    SignalHookHash::ConstIterator it = signalHooks.find(key);
    SignalHookHash::ConstIterator end = signalHooks.end();
    for ( ; it != end && it.key() == key; ++it) {
        const SignalHook &entry = it.value();
        if (entry.sender == hook.sender &&
            entry.path == hook.path &&
            entry.signature == hook.signature &&
            entry.obj == hook.obj &&
            entry.midx == hook.midx)
            return;             // already there, no need to re-add
    }

    connectSignal(key, hook);
}

void QDBusConnectionPrivate::disconnectRelay(const QString &service, const QString &path,
                                             const QString &interface,
                                             QDBusAbstractInterface *receiver,
                                             const char *signal)
{
    // this function is called by QDBusAbstractInterface when one of its signals is disconnected
    // we remove relay from D-Bus into it
    SignalHook hook;
    QString key;
    if (!prepareHook(hook, key, service, path, interface, QString(), receiver, signal,
                     QDBusAbstractInterface::staticMetaObject.methodCount(), true))
        return;                 // don't connect

    // remove it from our list:
    QWriteLocker locker(&lock);
    SignalHookHash::Iterator it = signalHooks.find(key);
    SignalHookHash::Iterator end = signalHooks.end();
    for ( ; it != end && it.key() == key; ++it) {
        const SignalHook &entry = it.value();
        if (entry.sender == hook.sender &&
            entry.path == hook.path &&
            entry.signature == hook.signature &&
            entry.obj == hook.obj &&
            entry.midx == hook.midx) {
            // found it
            signalHooks.erase(it);
            return;
        }
    }

    qWarning("QDBusConnectionPrivate::disconnectRelay called for a signal that was not found");
}

QString QDBusConnectionPrivate::getNameOwner(const QString& name)
{
    if (QDBusUtil::isValidUniqueConnectionName(name))
        return name;
    if (!connection || !QDBusUtil::isValidBusName(name))
        return QString();

    QDBusMessage msg = QDBusMessage::methodCall(QLatin1String(DBUS_SERVICE_DBUS),
            QLatin1String(DBUS_PATH_DBUS), QLatin1String(DBUS_INTERFACE_DBUS),
            QLatin1String("GetNameOwner"));
    msg << name;
    QDBusMessage reply = sendWithReply(msg, QDBusConnection::NoUseEventLoop);
    if (!lastError.isValid() && reply.type() == QDBusMessage::ReplyMessage)
        return reply.first().toString();
    return QString();
}

QDBusInterfacePrivate *
QDBusConnectionPrivate::findInterface(const QString &service,
                                      const QString &path,
                                      const QString &interface)
{
    // check if it's there first -- FIXME: add binding mode
    QDBusMetaObject *mo = 0;
    QString owner = getNameOwner(service);
    if (connection && !owner.isEmpty() && QDBusUtil::isValidObjectPath(path) &&
        (interface.isEmpty() || QDBusUtil::isValidInterfaceName(interface)))
        // always call here with the unique connection name
        mo = findMetaObject(owner, path, interface);

    QDBusInterfacePrivate *p = new QDBusInterfacePrivate(QDBusConnection(name), this, owner, path, interface, mo);

    if (!mo) {
        // invalid object
        p->isValid = false;
        p->lastError = lastError;
        if (!lastError.isValid()) {
            // try to determine why we couldn't get the data
            if (!connection)
                p->lastError = QDBusError(QDBusError::Disconnected,
                                          QLatin1String("Not connected to D-Bus server"));
            else if (owner.isEmpty())
                p->lastError = QDBusError(QDBusError::ServiceUnknown,
                                          QString(QLatin1String("Service %1 is unknown")).arg(service));
            else if (!QDBusUtil::isValidObjectPath(path))
                p->lastError = QDBusError(QDBusError::InvalidArgs,
                                          QString(QLatin1String("Object path %1 is invalid")).arg(path));
            else if (!interface.isEmpty() && !QDBusUtil::isValidInterfaceName(interface))
                p->lastError = QDBusError(QDBusError::InvalidArgs,
                                          QString(QLatin1String("Interface %1 is invalid")).arg(interface));
            else
                p->lastError = QDBusError(QDBusError::Other, QLatin1String("Unknown error"));
        }
    }

    return p;
}

struct qdbus_Introspect
{
    QString xml;
    inline void operator()(QDBusConnectionPrivate::ObjectTreeNode *node)
    { xml = qDBusIntrospectObject(node); }
};

QDBusMetaObject *
QDBusConnectionPrivate::findMetaObject(const QString &service, const QString &path,
                                       const QString &interface)
{
    // service must be a unique connection name
    if (!interface.isEmpty()) {
        QReadLocker locker(&lock);
        QDBusMetaObject *mo = cachedMetaObjects.value(interface, 0);
        if (mo)
            return mo;
    }
    if (service == QString::fromUtf8(dbus_bus_get_unique_name(connection))) {
        // it's one of our own
        QWriteLocker locker(&lock);
        QDBusMetaObject *mo = 0;
        if (!interface.isEmpty())
            mo = cachedMetaObjects.value(interface, 0);
        if (mo)
            // maybe it got created when we switched from read to write lock
            return mo;

        qdbus_Introspect apply;
        if (!applyForObject(&rootNode, path, apply)) {
            lastError = QDBusError(QDBusError::InvalidArgs,
                                   QString(QLatin1String("No object at %1")).arg(path));
            return 0;           // no object at path
        }

        // release the lock and return
        return QDBusMetaObject::createMetaObject(interface, apply.xml, cachedMetaObjects, lastError);
    }

    // not local: introspect the target object:
    QDBusMessage msg = QDBusMessage::methodCall(service, path,
                                                QLatin1String(DBUS_INTERFACE_INTROSPECTABLE),
                                                QLatin1String("Introspect"));


    QDBusMessage reply = sendWithReply(msg, QDBusConnection::NoUseEventLoop);

    // it doesn't exist yet, we have to create it
    QWriteLocker locker(&lock);
    QDBusMetaObject *mo = 0;
    if (!interface.isEmpty())
        mo = cachedMetaObjects.value(interface, 0);
    if (mo)
        // maybe it got created when we switched from read to write lock
        return mo;

    QString xml;
    if (reply.type() == QDBusMessage::ReplyMessage)
        // fetch the XML description
        xml = reply.first().toString();
    else {
        lastError = reply;
        if (reply.type() != QDBusMessage::ErrorMessage || lastError != QDBusError::UnknownMethod)
            return 0;           // error
    }

    // release the lock and return
    return QDBusMetaObject::createMetaObject(interface, xml, cachedMetaObjects, lastError);
}

void QDBusReplyWaiter::reply(const QDBusMessage &msg)
{
    replyMsg = msg;
    QTimer::singleShot(0, this, SLOT(quit()));
}

#include "qdbusconnection_p.moc"

--- NEW FILE: qdbusinterface.cpp ---
/* -*- C++ -*-
 *
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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 <dbus/dbus.h>
#include <QtCore/qpointer.h>

#include "qdbusinterface_p.h"
#include "qdbusconnection_p.h"

/*!
    \class QDBusInterface
    \brief Proxy class for interfaces on remote objects.

    QDBusInterface is a generic accessor class that is used to place calls to remote objects,
    connect to signals exported by remote objects and get/set the value of remote properties. This
    class is useful for dynamic access to remote objects: that is, when you do not have a generated
    code that represents the remote interface.

    Calls are usually placed by using the call() function, which constructs the message, sends it
    over the bus, waits for the reply and decodes the reply. Signals are connected to by using the
    normal QObject::connect() function. Finally, properties are accessed using the
    QObject::property() and QObject::setProperty() functions. 
*/

QDBusInterface::QDBusInterface(QDBusInterfacePrivate *p)
    : QDBusAbstractInterface(p)
{
}

/*!
    Destroy the object interface and frees up any resource used.
*/
QDBusInterface::~QDBusInterface()
{
    // resources are freed in QDBusInterfacePrivate::~QDBusInterfacePrivate()
}

/*!
    \internal
    Overrides QObject::metaObject to return our own copy.
*/
const QMetaObject *QDBusInterface::metaObject() const
{
    return d_func()->isValid ? d_func()->metaObject : &QDBusAbstractInterface::staticMetaObject;
}

/*!
    \internal
    Override QObject::qt_metacast to catch the interface name too.
*/
void *QDBusInterface::qt_metacast(const char *_clname)
{
    if (!_clname) return 0;
    if (!strcmp(_clname, "QDBusInterface"))
        return static_cast<void*>(const_cast<QDBusInterface*>(this));
    if (d_func()->interface == _clname)
        return static_cast<void*>(const_cast<QDBusInterface*>(this));
    return QDBusAbstractInterface::qt_metacast(_clname);
}

/*!
    \internal
    Dispatch the call through the private.
*/
int QDBusInterface::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
    _id = QDBusAbstractInterface::qt_metacall(_c, _id, _a);
    if (_id < 0 || !d_func()->isValid)
        return _id;
    return d_func()->metacall(_c, _id, _a);
}

int QDBusInterfacePrivate::metacall(QMetaObject::Call c, int id, void **argv)
{
    Q_Q(QDBusInterface);
    
    if (c == QMetaObject::InvokeMetaMethod) {
        int offset = metaObject->methodOffset();
        QMetaMethod mm = metaObject->method(id + offset);
        
        if (mm.methodType() == QMetaMethod::Signal) {
            // signal relay from D-Bus world to Qt world
            QMetaObject::activate(q, metaObject, id, argv);
            
        } else if (mm.methodType() == QMetaMethod::Slot) {
            // method call relay from Qt world to D-Bus world
            // get D-Bus equivalent signature
            QString methodName = metaObject->dbusNameForMethod(id);
            const int *inputTypes = metaObject->inputTypesForMethod(id);
            const int *outputTypes = metaObject->outputTypesForMethod(id);

            int inputTypesCount = *inputTypes;
            int outputTypesCount = *outputTypes++;

            // we will assume that the input arguments were passed correctly
            QVariantList args;
            for (int i = 1; i <= inputTypesCount; ++i)
                args << QVariant(inputTypes[i], argv[i]);

            // make the call
            QPointer<QDBusInterface> qq = q;
            QDBusMessage reply = q->callWithArgs(methodName, args);
            args.clear();

            // access to "this" or to "q" below this point must check for "qq"
            // we may have been deleted!

            // check if we got the right number of parameters back:
            bool success = false;
            if (reply.count() == outputTypesCount) {
                // copy the values out
                for (int i = 0; i < outputTypesCount; ++i) {
                    // treat the return value specially, since it may be null:
                    if (i == 0 && argv[0] == 0)
                        continue;

                    // ensure that the types are correct:
                    const QVariant &item = reply.at(i);
                    if (outputTypes[i] != item.userType()) {
                        success = false;
                        break;
                    }

                    if (i == 0)
                        QDBusMetaObject::assign(argv[0], item);
                    else
                        QDBusMetaObject::assign(argv[inputTypesCount + i], item);
                }
            }

            // bail out, something weird happened
            if (!success && !qq.isNull()) {
                QString errmsg = QLatin1String("Invalid signature `%1' in return from call to %2.%3");
                lastError = QDBusError(QDBusError::InvalidSignature,
                                       errmsg.arg(reply.signature(), interface, methodName));
            }

            // done
            return -1;
        }
    } else if (c == QMetaObject::ReadProperty) {
        // Qt doesn't support non-readable properties
        // we have to re-check
        QMetaProperty mp = metaObject->property(id + metaObject->propertyOffset());
        if (!mp.isReadable())
            return -1;          // don't read

        QVariant value = property(mp);
        if (value.type() == QVariant::Invalid)
            // an error occurred -- property already set lastError
            return -1;
        else if (mp.type() == QVariant::LastType)
            // QVariant is special in this context
            *reinterpret_cast<QVariant *>(argv[0]) = value;
        else
            QDBusMetaObject::assign(argv[0], value);

        return -1; // handled
    } else if (c == QMetaObject::WriteProperty) {
        // QMetaProperty::write has already checked that we're writable
        // it has also checked that the type is right
        QVariant value(metaObject->propertyMetaType(id), argv[0]);
        QMetaProperty mp = metaObject->property(id + metaObject->propertyOffset());

        setProperty(mp, value);
        return -1;
    }
    return id;
}

QDBusInterfacePtr::QDBusInterfacePtr(QDBusConnection &conn, const QString &service, const QString &path,
                   const QString &interface)
    : d(conn.findInterface(service, path, interface))
{
}

QDBusInterfacePtr::QDBusInterfacePtr(const QString &service, const QString &path, const QString &interface)
    : d(QDBus::sessionBus().findInterface(service, path, interface))
{
}


--- NEW FILE: qdbusinterface.h ---
/* -*- C++ -*-
 *
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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 "qdbusabstractinterface.h"

class QDBusInterfacePrivate;
class QDBUS_EXPORT QDBusInterface: public QDBusAbstractInterface
{
    friend class QDBusConnection;
private:
    QDBusInterface(QDBusInterfacePrivate *p);
    
public:
    ~QDBusInterface();
    
    virtual const QMetaObject *metaObject() const;
    virtual void *qt_metacast(const char *);
    virtual int qt_metacall(QMetaObject::Call, int, void **);

private:
    Q_DECLARE_PRIVATE(QDBusInterface)
    Q_DISABLE_COPY(QDBusInterface)
};

struct QDBUS_EXPORT QDBusInterfacePtr
{
    QDBusInterfacePtr(QDBusInterface *iface) : d(iface) { }
    QDBusInterfacePtr(QDBusConnection &conn, const QString &service, const QString &path,
             const QString &interface = QString());
    QDBusInterfacePtr(const QString &service, const QString &path, const QString &interface = QString());
    ~QDBusInterfacePtr() { delete d; }

    QDBusInterface *interface() { return d; }
    QDBusInterface *operator->() { return d; }
private:
    QDBusInterface *const d;
    Q_DISABLE_COPY(QDBusInterfacePtr)
};

#endif

--- NEW FILE: qdbusinterface_p.h ---
/* 
 *
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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 "qdbusabstractinterface_p.h"
#include "qdbusmetaobject_p.h"
#include "qdbusinterface.h"

class QDBusInterfacePrivate: public QDBusAbstractInterfacePrivate
{
public:
    Q_DECLARE_PUBLIC(QDBusInterface)

    QDBusMetaObject *metaObject;

    inline QDBusInterfacePrivate(const QDBusConnection &con, QDBusConnectionPrivate *conp,
                                 const QString &serv, const QString &p, const QString &iface,
                                 QDBusMetaObject *mo = 0)
        : QDBusAbstractInterfacePrivate(con, conp, serv, p, iface), metaObject(mo)
    {
    }
    ~QDBusInterfacePrivate()
    {
        if (metaObject && !metaObject->cached)
            delete metaObject;
    }

    int metacall(QMetaObject::Call c, int id, void **argv);
};

#endif

--- NEW FILE: qdbusinternalfilters.cpp ---
/* -*- mode: C++ -*-
 *
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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 "qdbusconnection_p.h"

#include <dbus/dbus.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qmetaobject.h>
#include <QtCore/qstringlist.h>

#include "qdbusabstractadaptor.h"
#include "qdbusabstractadaptor_p.h"
#include "qdbusconnection.h"
#include "qdbusmessage.h"
#include "qdbustypehelper_p.h"
#include "qdbusutil.h"

// defined in qdbusxmlgenerator.cpp
extern QString qDBusGenerateMetaObjectXml(QString interface, const QMetaObject *mo,
                                          const QMetaObject *base, int flags);

static const char introspectableInterfaceXml[] =
    "  <interface name=\"org.freedesktop.DBus.Introspectable\">\n"
    "    <method name=\"Introspect\">\n"
    "      <arg name=\"xml_data\" type=\"s\" direction=\"out\"/>\n"
    "    </method>\n"
    "  </interface>\n";

static const char propertiesInterfaceXml[] =
    "  <interface name=\"org.freedesktop.DBus.Properties\">\n"
    "    <method name=\"Get\">\n"
    "      <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
    "      <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
    "      <arg name=\"value\" type=\"v\" direction=\"out\"/>\n"
    "    </method>\n"
    "    <method name=\"Set\">\n"
    "      <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
    "      <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
    "      <arg name=\"value\" type=\"v\" direction=\"in\"/>\n"
    "    </method>\n"
    "  </interface>\n";

static QString generateSubObjectXml(QObject *object)
{
    QString retval;
    foreach (QObject *child, object->children()) {
        QString name = child->objectName();
        if (!name.isEmpty())
            retval += QString(QLatin1String("  <node name=\"%1\"/>\n"))
                      .arg(name);
    }
    return retval;
}

QString qDBusIntrospectObject(const QDBusConnectionPrivate::ObjectTreeNode *node)
{
    // object may be null

    QString xml_data(QLatin1String(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE));
    xml_data += QLatin1String("<node>\n");

    if (node->obj) {
        if (node->flags & QDBusConnection::ExportContents) {
            const QMetaObject *mo = node->obj->metaObject();
            for ( ; mo != &QObject::staticMetaObject; mo = mo->superClass())
                xml_data += qDBusGenerateMetaObjectXml(QString(), mo, mo->superClass(),
                                                  node->flags);
        }

        // does this object have adaptors?
        QDBusAdaptorConnector *connector;
        if (node->flags & QDBusConnection::ExportAdaptors &&
            (connector = qDBusFindAdaptorConnector(node->obj))) {

            // trasverse every adaptor in this object
            QDBusAdaptorConnector::AdaptorMap::ConstIterator it = connector->adaptors.constBegin();
            QDBusAdaptorConnector::AdaptorMap::ConstIterator end = connector->adaptors.constEnd();
            for ( ; it != end; ++it) {
                // add the interface:
                QString ifaceXml = QDBusAbstractAdaptorPrivate::retrieveIntrospectionXml(it->adaptor);
                if (ifaceXml.isEmpty()) {
                    // add the interface's contents:
                    ifaceXml += qDBusGenerateMetaObjectXml(it->interface, it->metaObject,
                                                           &QDBusAbstractAdaptor::staticMetaObject,
                                                           QDBusConnection::ExportAllContents);

                    QDBusAbstractAdaptorPrivate::saveIntrospectionXml(it->adaptor, ifaceXml);
                }

                xml_data += ifaceXml;
            }
        }

        xml_data += QLatin1String( introspectableInterfaceXml );
        xml_data += QLatin1String( propertiesInterfaceXml );
    }

    if (node->flags & QDBusConnection::ExportChildObjects) {
        xml_data += generateSubObjectXml(node->obj);
    } else {
        // generate from the object tree
        foreach (const QDBusConnectionPrivate::ObjectTreeNode::Data &entry, node->children) {
            if (entry.node && (entry.node->obj || !entry.node->children.isEmpty()))
                xml_data += QString(QLatin1String("  <node name=\"%1\"/>\n"))
                            .arg(entry.name);
        }
    }

    xml_data += QLatin1String("</node>\n");
    return xml_data;
}

void qDBusIntrospectObject(const QDBusConnectionPrivate::ObjectTreeNode *node,
                           const QDBusMessage &msg)
{
    // now send it
    QDBusMessage reply = QDBusMessage::methodReply(msg);
    reply << qDBusIntrospectObject(node);
    msg.connection().send(reply);
}

// implement the D-Bus interface org.freedesktop.DBus.Properties

static void sendPropertyError(const QDBusMessage &msg, const QString &interface_name)
{
    QDBusMessage error = QDBusMessage::error(msg, QLatin1String(DBUS_ERROR_INVALID_ARGS),
                                   QString::fromLatin1("Interface %1 was not found in object %2")
                                   .arg(interface_name)
                                   .arg(msg.path()));
    msg.connection().send(error);
}

void qDBusPropertyGet(const QDBusConnectionPrivate::ObjectTreeNode *node, const QDBusMessage &msg)
{
    Q_ASSERT(msg.count() == 2);
    QString interface_name = msg.at(0).toString();
    QByteArray property_name = msg.at(1).toString().toUtf8();

    QDBusAdaptorConnector *connector;
    QVariant value;
    if (node->flags & QDBusConnection::ExportAdaptors &&
        (connector = qDBusFindAdaptorConnector(node->obj))) {

        // find the class that implements interface_name
        QDBusAdaptorConnector::AdaptorMap::ConstIterator it;
        it = qLowerBound(connector->adaptors.constBegin(), connector->adaptors.constEnd(),
                         interface_name);
        if (it != connector->adaptors.end() && it->interface == interface_name)
            value = it->adaptor->property(property_name);
    }

    if (!value.isValid() && node->flags & QDBusConnection::ExportProperties) {
        // try the object itself
        int pidx = node->obj->metaObject()->indexOfProperty(property_name);
        if (pidx != -1) {
            QMetaProperty mp = node->obj->metaObject()->property(pidx);
            if (mp.isScriptable() || (node->flags & QDBusConnection::ExportAllProperties) ==
                QDBusConnection::ExportAllProperties)
                value = mp.read(node->obj);
        }
    }

    if (!value.isValid()) {
        // the property was not found
        sendPropertyError(msg, interface_name);
        return;
    }

    QDBusMessage reply = QDBusMessage::methodReply(msg);
    reply.setSignature(QLatin1String("v"));
    reply << value;
    msg.connection().send(reply);
}

void qDBusPropertySet(const QDBusConnectionPrivate::ObjectTreeNode *node, const QDBusMessage &msg)
{
    Q_ASSERT(msg.count() == 3);
    QString interface_name = msg.at(0).toString();
    QByteArray property_name = msg.at(1).toString().toUtf8();
    QVariant value = QDBusTypeHelper<QVariant>::fromVariant(msg.at(2));

    QDBusAdaptorConnector *connector;
    if (node->flags & QDBusConnection::ExportAdaptors &&
        (connector = qDBusFindAdaptorConnector(node->obj))) {

        // find the class that implements interface_name
        QDBusAdaptorConnector::AdaptorMap::ConstIterator it;
        it = qLowerBound(connector->adaptors.constBegin(), connector->adaptors.constEnd(),
                         interface_name);
        if (it != connector->adaptors.end() && it->interface == interface_name)
            if (it->adaptor->setProperty(property_name, value)) {
                msg.connection().send(QDBusMessage::methodReply(msg));
                return;
            }
    }

    if (node->flags & QDBusConnection::ExportProperties) {
        // try the object itself
        int pidx = node->obj->metaObject()->indexOfProperty(property_name);
        if (pidx != -1) {
            QMetaProperty mp = node->obj->metaObject()->property(pidx);
            if (mp.isScriptable() || (node->flags & QDBusConnection::ExportAllProperties) ==
                QDBusConnection::ExportAllProperties) {

                if (mp.write(node->obj, value)) {
                    msg.connection().send(QDBusMessage::methodReply(msg));
                    return;
                }
            }
        }
    }

    // the property was not found or not written to
    sendPropertyError(msg, interface_name);
}

--- NEW FILE: qdbusintrospection.cpp ---
/* -*- C++ -*-
 *
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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_p.h"
#include "qdbusxmlparser_p.h"

/*!
    \class QDBusIntrospection
    \brief Information about introspected objects and interfaces on D-Bus.
    \internal

    This class provides structures and methods for parsing the XML introspection data for D-Bus.
    Normally, you don't have to use the methods provided here: QDBusInterface and QDBusObject will
    do that for you.

    But they may prove useful if the XML data was obtained through other means (like parsing a file).
*/

/*!
    \class QDBusIntrospection::Argument
    \brief One argument to a D-Bus method or signal.

    This struct represents one argument passed to a method or received from a method or signal in
    D-Bus. The struct does not contain information on the direction (input or output).
*/

/*!
    \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.
*/

/*!
    \fn QDBusIntrospection::Argument::operator==(const Argument &other) const
    Compares this object against \a other and return true if they are the same.
*/

/*!
    \class QDBusIntrospection::Method
    \brief Information about one method.

    This struct represents one method discovered through introspection. A method is composed of
    its \a name, its input arguments, its output arguments, and, optionally, annotations. There are no
    "in-out" arguments.
*/

/*!
    \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.
*/

/*!
    \fn QDBusIntrospection::Method::operator==(const Method &other) const
    Compares this object against \a other and return true if they are the same.
*/

/*!
    \class QDBusIntrospection::Signal
    \brief Information about one signal.

    This struct represents one signal discovered through introspection. A signal is composed of
    its \a name, its output arguments, and, optionally, annotations.
*/

/*!
    \var QDBusIntrospection::Signal::name
    The signal's name.
*/

/*!
    \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.
*/

/*!
    \fn QDBusIntrospection::Signal::operator==(const Signal& other) const
    Compares this object against \a other and return true if they are the same.
*/

/*!
    \class QDBusIntrospection::Property
    \brief Information about one property.

    This struct represents one property discovered through introspection. A property is composed of
    its \a name, its \a type, its \a access rights, and, optionally, annotations.
*/

/*!
    \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:
    \value Read
    \value Write
    \value 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.
*/

/*!
    \fn QDBusIntrospection::Property::operator==(const Property &other) const
    Compares this object against \a other and return true if they are the same.
*/

/*!
    \class QDBusIntrospection::Interface
    \brief Information about one interface on the bus.

    Each interface on D-Bus has an unique \a name, identifying where that interface was defined.
    Interfaces may have annotations, methods, signals and properties, but none are mandatory.
*/

/*!
    \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.
*/

/*!
    \fn QDBusIntrospection::Interface::operator==(const Interface &other) const
    Compares this object against \a other and return true if they are the same.

    Note that two interfaces are considered to be the same if they have the same name. The internal
    structures in the objects are not compared.
*/

/*!
    \class QDBusIntrospection::Object
    \brief Information about one object on the bus.

    An object on the D-Bus bus is represented by its service and path on the service but, unlike
    interfaces, objects are mutable. That is, their contents can change with time. Therefore,
    while the (service, path) pair uniquely identifies an object, the information contained in
    this struct may no longer represent the object.

    An object can contain interfaces and child (sub) objects.
*/

/*!
    \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 \l
    {QDBusIntrospection::Object::path}{path}.
*/

/*!
    \class QDBusIntrospection::ObjectTree
    \brief Complete information about one object node and its descendency.
    
    This struct contains the same data as QDBusIntrospection::Object, plus the actual data for the
    interfaces and child (sub) objects that was available in the XML document.
*/

/*!
    \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 (given by \a xml) containing one interface.

    The first element tag in this XML data must be either \<node\> or \<interface\>. If it is
    \<node\>, then the \<interface\> tag must be a child tag of the \<node\> one.

    If there are multiple interfaces in this XML data, it is undefined which one will be
    returned.
*/
QDBusIntrospection::Interface
QDBusIntrospection::parseInterface(const QString &xml)
{
    // be lazy
    Interfaces ifs = parseInterfaces(xml);
    if (ifs.isEmpty())
        return Interface();

    // return the first in map order (probably alphabetical order)
    return *ifs.constBegin().value();
}

/*!
    Parses the XML document fragment (given by \a xml) containing several interfaces.

    If the first element tag in this document fragment is \<node\>, the interfaces parsed will
    be those found as child elements of the \<node\> tag.
*/
QDBusIntrospection::Interfaces
QDBusIntrospection::parseInterfaces(const QString &xml)
{
    QString null;
    QDBusXmlParser parser(null, null, xml);
    return parser.interfaces();
}

/*!
    Parses the XML document fragment (given by \a xml) containing one object, found at the service
    \a service and path \a path.

    The first element tag in this document must be \<node\>. If that tag does not contain
    a name attribute, the \a path argument will be used to determine the path of this
    object node.

    This function does not parse the interfaces contained in the node, nor sub-object's contents.
    It will only list their names. If you need to know their contents, use parseObjectTree.
*/
QDBusIntrospection::Object
QDBusIntrospection::parseObject(const QString &xml, const QString &service, const QString &path)
{
    QDBusXmlParser parser(service, path, xml);
    QSharedDataPointer<QDBusIntrospection::Object> retval = parser.object();
    if (!retval)
        return QDBusIntrospection::Object();
    return *retval;
}

/*!
    Parses the XML document fragment (given by \a xml) containing one object node and returns all
    the information about the interfaces and sub-objects, found at the service \a service and path
    \a path.

    The Objects map returned will contain the absolute path names in the key.
*/
QDBusIntrospection::ObjectTree
QDBusIntrospection::parseObjectTree(const QString &xml, const QString &service, const QString &path)
{
    QDBusXmlParser parser(service, path, xml);
    QSharedDataPointer<QDBusIntrospection::ObjectTree> retval = parser.objectTree();
    if (!retval)
        return QDBusIntrospection::ObjectTree();
    return *retval;
}

--- NEW FILE: qdbusintrospection_p.h ---
/* -*- C++ -*-
 *
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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 "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
    {
        QString type;
        QString name;

        inline bool operator==(const Argument& other) const
        { return name == other.name && type == other.type; }
    };
    
    struct Method
    {
        QString name;
        Arguments inputArgs;
        Arguments outputArgs;
        Annotations annotations;

        inline bool operator==(const Method& other) const
        { return name == other.name && annotations == other.annotations &&
                inputArgs == other.inputArgs && outputArgs == other.outputArgs; }
    };

    struct Signal
    {
        QString name;
        Arguments outputArgs;
        Annotations annotations;

        inline bool operator==(const Signal& other) const
        { return name == other.name && annotations == other.annotations &&
                outputArgs == other.outputArgs; }
    };

    struct Property
    {
        enum Access { Read, Write, ReadWrite };
        QString name;
        QString type;
        Access access;
        Annotations annotations;

        inline bool operator==(const Property& other) const
        { return access == other.access && name == other.name &&
                annotations == other.annotations && type == other.type; }
    };

    struct Interface: public QSharedData
    {
        QString name;
        QString introspection;

        Annotations annotations;
        Methods methods;
        Signals signals_;
        Properties properties;

        inline bool operator==(const Interface &other) const
        { return !name.isEmpty() && name == other.name; }
    };

    struct Object: public QSharedData
    {
        QString service;
        QString path;
        QString introspection;

        QStringList interfaces;
        QStringList childObjects;
    };

    struct ObjectTree: public Object
    {
        Interfaces interfaceData;
        Objects childObjectData;
    };

public:
    static Interface parseInterface(const QString &xml);
    static Interfaces parseInterfaces(const QString &xml);
    static Object parseObject(const QString &xml, const QString &service = QString(),
                              const QString &path = QString());
    static ObjectTree parseObjectTree(const QString &xml,
                                      const QString &service,
                                      const QString &path);

private:
    QDBusIntrospection();
};

#endif

--- NEW FILE: qdbusmacros.h ---
/* qdbusmessage.h QDBusMessage object
 *
 * Copyright (C) 2005 Harald Fernengel <harry at kdevelop.org>
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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.
 *
 */

/*!
    \file qdbusmacros.h
*/

#ifndef QDBUSMACROS_H
#define QDBUSMACROS_H

#include <QtCore/qglobal.h>
#include <QtCore/qmetatype.h>
#include <QtCore/qvariant.h>

#ifdef QT_NO_MEMBER_TEMPLATES
# error Sorry, you need a compiler with support for template member functions to compile QtDBus.
#endif

#if defined(QDBUS_MAKEDLL)
# define QDBUS_EXPORT Q_DECL_EXPORT
#else
# define QDBUS_EXPORT Q_DECL_IMPORT
#endif

#ifndef Q_MOC_RUN
# define Q_ASYNC
#endif

#ifdef Q_CC_MSVC
#include <QtCore/qlist.h>
#include <QtCore/qset.h>
#include <QtCore/qhash.h>
#include <QtCore/qvector.h>
class QDBusType;
inline uint qHash(const QVariant&)  { Q_ASSERT(0); return 0; }
inline uint qHash(const QDBusType&) { Q_ASSERT(0); return 0; }
#endif

#endif

--- NEW FILE: qdbusmarshall.cpp ---
/* qdbusmarshall.cpp
 *
 * Copyright (C) 2005 Harald Fernengel <harry at kdevelop.org>
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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 "qdbusmarshall_p.h"
#include "qdbustype_p.h"
#include "qdbustypehelper_p.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>

static QVariant qFetchParameter(DBusMessageIter *it);

template <typename T>
inline T qIterGet(DBusMessageIter *it)
{
    T t;
    dbus_message_iter_get_basic(it, &t);
    return t;
}

template<>
inline QVariant qIterGet(DBusMessageIter *it)
{
    DBusMessageIter sub;
    dbus_message_iter_recurse(it, &sub);
    return QDBusTypeHelper<QVariant>::toVariant(qFetchParameter(&sub));
}    

template <typename DBusType, typename QtType>
inline QVariant qFetchList(DBusMessageIter *arrayIt)
{
    QList<QtType> list;

    DBusMessageIter it;
    dbus_message_iter_recurse(arrayIt, &it);
    if (dbus_message_iter_get_array_len(&it) == 0)
        return QDBusTypeHelper<QList<QtType> >::toVariant(list);

    do {
        list.append( static_cast<QtType>( qIterGet<DBusType>(&it) ) );
    } while (dbus_message_iter_next(&it));

    return QDBusTypeHelper<QList<QtType> >::toVariant(list);
}

static QStringList qFetchStringList(DBusMessageIter *arrayIt)
{
    QStringList list;

    DBusMessageIter it;
    dbus_message_iter_recurse(arrayIt, &it);
    if (dbus_message_iter_get_array_len(&it) == 0)
        return list;

    do {
        list.append(QString::fromUtf8(qIterGet<char *>(&it)));
    } while (dbus_message_iter_next(&it));

    return list;
}

static QVariant qFetchParameter(DBusMessageIter *it)
{
    switch (dbus_message_iter_get_arg_type(it)) {
    case DBUS_TYPE_BYTE:
        return qVariantFromValue(qIterGet<unsigned char>(it));
    case DBUS_TYPE_INT16:
	return qVariantFromValue(qIterGet<dbus_int16_t>(it));
    case DBUS_TYPE_UINT16:
	return qVariantFromValue(qIterGet<dbus_uint16_t>(it));
    case DBUS_TYPE_INT32:
        return qIterGet<dbus_int32_t>(it);
    case DBUS_TYPE_UINT32:
        return qIterGet<dbus_uint32_t>(it);
    case DBUS_TYPE_DOUBLE:
        return qIterGet<double>(it);
    case DBUS_TYPE_BOOLEAN:
        return bool(qIterGet<dbus_bool_t>(it));
    case DBUS_TYPE_INT64:
        return static_cast<qlonglong>(qIterGet<dbus_int64_t>(it));
    case DBUS_TYPE_UINT64:
        return static_cast<qulonglong>(qIterGet<dbus_uint64_t>(it));
    case DBUS_TYPE_STRING:
    case DBUS_TYPE_OBJECT_PATH:
    case DBUS_TYPE_SIGNATURE:
        return QString::fromUtf8(qIterGet<char *>(it));
    case DBUS_TYPE_VARIANT:
        return qIterGet<QVariant>(it);
    case DBUS_TYPE_ARRAY: {
        int arrayType = dbus_message_iter_get_element_type(it);
        switch (arrayType)
        {
        case DBUS_TYPE_BYTE: {
            // QByteArray
            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);
        }
        case DBUS_TYPE_INT16:
            return qFetchList<dbus_int16_t, short>(it);
        case DBUS_TYPE_UINT16:
            return qFetchList<dbus_uint16_t, ushort>(it);
        case DBUS_TYPE_INT32:
            return qFetchList<dbus_int32_t, int>(it);
        case DBUS_TYPE_UINT32:
            return qFetchList<dbus_uint32_t, uint>(it);
        case DBUS_TYPE_BOOLEAN:
            return qFetchList<dbus_bool_t, bool>(it);
        case DBUS_TYPE_DOUBLE:
            return qFetchList<double, double>(it);
        case DBUS_TYPE_INT64:
            return qFetchList<dbus_int64_t, qlonglong>(it);
        case DBUS_TYPE_UINT64:
            return qFetchList<dbus_uint64_t, qulonglong>(it);
        case DBUS_TYPE_STRING:
        case DBUS_TYPE_OBJECT_PATH:
        case DBUS_TYPE_SIGNATURE:
            return qFetchStringList(it);
        case DBUS_TYPE_VARIANT:
            return qFetchList<QVariant, QVariant>(it);
        case 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_get_array_len(&sub) == 0)
                // empty map
                return map;
            
            do {
                DBusMessageIter itemIter;
                dbus_message_iter_recurse(&sub, &itemIter);
                Q_ASSERT(dbus_message_iter_has_next(&itemIter));
                QString key = qFetchParameter(&itemIter).toString();
                dbus_message_iter_next(&itemIter);
                map.insertMulti(key, qFetchParameter(&itemIter));
            } while (dbus_message_iter_next(&sub));
            return map;
        }
        }
    }
    // fall through
    // common handling for structs and lists of lists (for now)
    case DBUS_TYPE_STRUCT: {
        QList<QVariant> list;
        DBusMessageIter sub;
        dbus_message_iter_recurse(it, &sub);
        if (dbus_message_iter_get_array_len(&sub) == 0)
            return list;
        do {
            list.append(qFetchParameter(&sub));
        } while (dbus_message_iter_next(&sub));
        return list;
    }

    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();
        break;
    }
}

void QDBusMarshall::messageToList(QList<QVariant> &list, DBusMessage *message)
{
    Q_ASSERT(message);

    DBusMessageIter it;
    if (!dbus_message_iter_init(message, &it))
        return;

    do {
        list.append(qFetchParameter(&it));
    } while (dbus_message_iter_next(&it));
}

// 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)
{
    if (!type.isValid()) {
        // guess it from the variant
        type = QDBusType::guessFromVariant(var);
        return true;
    }

    int id = var.userType(); 
    
    if (type.dbusType() == DBUS_TYPE_VARIANT) {
        // this is a non symmetrical operation:
        // nest a QVariant if we want variant and it isn't so
        if (id != QDBusTypeHelper<QVariant>::id()) {
            QVariant tmp = var;
            var = QDBusTypeHelper<QVariant>::toVariant(tmp);
        }
        return true;
    }

    switch (id) {
    case QVariant::Bool:
    case QMetaType::Short: 
    case QMetaType::UShort:
    case QMetaType::UChar:
    case QVariant::Int:
    case QVariant::UInt:
    case QVariant::LongLong:
    case QVariant::ULongLong:
    case QVariant::Double:
    case QVariant::String:
        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
        void *null = 0;
        var = QVariant(type.qvariantType(), null);
        break;
    }

    default:
        if (id == QDBusTypeHelper<QVariant>::id()) {
            // if we got here, it means the match above for DBUS_TYPE_VARIANT didn't work
            qWarning("Invalid conversion from nested variant to '%s'",
                     type.dbusSignature().constData());
            return false;
        } else if (type.dbusType() == DBUS_TYPE_ARRAY) {
            int subType = type.arrayElement().dbusType();
            if ((id == QDBusTypeHelper<bool>::listId() && subType == DBUS_TYPE_BOOLEAN) ||
                (id == QDBusTypeHelper<short>::listId() && subType == DBUS_TYPE_INT16) ||
                (id == QDBusTypeHelper<ushort>::listId() && subType == DBUS_TYPE_UINT16) ||
                (id == QDBusTypeHelper<int>::listId() && subType == DBUS_TYPE_INT32) ||
                (id == QDBusTypeHelper<uint>::listId() && subType == DBUS_TYPE_UINT32) ||
                (id == QDBusTypeHelper<qlonglong>::listId() && subType == DBUS_TYPE_INT64) ||
                (id == QDBusTypeHelper<qulonglong>::listId() && subType == DBUS_TYPE_UINT64) ||
                (id == QDBusTypeHelper<double>::listId() && subType == DBUS_TYPE_DOUBLE))
                return true;
        }

        qWarning("Invalid conversion from %s to '%s'", var.typeName(),
                 type.dbusSignature().constData());
        return false;
    }

    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 &typelist);

template<typename T>
static void qIterAppend(DBusMessageIter *it, const QDBusType &type, T arg)
{
    dbus_message_iter_append_basic(it, type.dbusType(), &arg);
}

template<typename DBusType, typename QtType>
static void qAppendListToMessage(DBusMessageIter *it, const QDBusType &subType,
                                 const QVariant &var)
{
    QList<QtType> list = QDBusTypeHelper<QList<QtType> >::fromVariant(var);
    foreach (const QtType &item, list)
        qIterAppend(it, subType, static_cast<DBusType>(item));
}

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();
        foreach (QString str, list)
            qIterAppend(&sub, subType, str.toUtf8().constData());
        break;
    }

    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: {
        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);
            
            // 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);
        }
        break;
    }

    case QVariant::List: {
        const QVariantList list = var.toList();
        foreach (QVariant v, list)
            qVariantToIteratorInternal(&sub, v, subType);
        break;        
    }

    default: {
        int id = var.userType();
        if (id == QDBusTypeHelper<bool>::listId())
            qAppendListToMessage<dbus_bool_t,bool>(&sub, subType, var);
        else if (id == QDBusTypeHelper<short>::listId())
            qAppendListToMessage<dbus_int16_t,short>(&sub, subType, var);
        else if (id == QDBusTypeHelper<ushort>::listId())
            qAppendListToMessage<dbus_uint16_t,ushort>(&sub, subType, var);
        else if (id == QDBusTypeHelper<int>::listId())
            qAppendListToMessage<dbus_int32_t,int>(&sub, subType, var);
        else if (id == QDBusTypeHelper<uint>::listId())
            qAppendListToMessage<dbus_uint32_t,uint>(&sub, subType, var);
        else if (id == QDBusTypeHelper<qlonglong>::listId())
            qAppendListToMessage<dbus_int64_t,qlonglong>(&sub, subType, var);
        else if (id == QDBusTypeHelper<qulonglong>::listId())
            qAppendListToMessage<dbus_uint64_t,qulonglong>(&sub, subType, var);
        else if (id == QDBusTypeHelper<double>::listId())
            qAppendListToMessage<double,double>(&sub, subType, var);
#if 0   // never reached, since QVariant::List mached
        else if (id == QDBusTypeHelper<QVariant>::listId())
            qAppendListToMessage<QVariant,QVariant>(&sub, subType, var);
#endif
        else
            qFatal("qAppendArrayToMessage got unknown type!");
        break;
    }
    }
    
    dbus_message_iter_close_container(it, &sub);
}

static void qAppendStructToMessage(DBusMessageIter *it, const QDBusTypeList &typeList,
                                   const QVariantList &list)
{
    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)
{
    Q_UNUSED(type);             // type is 'v'

    QVariant arg = var;
    if (var.userType() == QDBusTypeHelper<QVariant>::id())
        arg = QDBusTypeHelper<QVariant>::fromVariant(var); // extract the inner variant
    
    QDBusType t = QDBusType::guessFromVariant(arg);
    
    // now add this variant
    DBusMessageIter sub;
    dbus_message_iter_open_container(it, DBUS_TYPE_VARIANT, t.dbusSignature(), &sub);
    qVariantToIteratorInternal(&sub, arg, 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, QDBusTypeHelper<uchar>::fromVariant(var) );
        break;
    case DBUS_TYPE_BOOLEAN:
        qIterAppend( it, type, static_cast<dbus_bool_t>(var.toBool()) );
        break;
    case DBUS_TYPE_INT16:
        qIterAppend( it, type, QDBusTypeHelper<short>::fromVariant(var) );
        break;
    case DBUS_TYPE_UINT16:
        qIterAppend( it, type, QDBusTypeHelper<ushort>::fromVariant(var) );
        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), QDBusType());
}

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);

    if (signature.isEmpty())
        qListToIterator(&it, list);
    else
        qListToIterator(&it, list, QDBusTypeList(signature.toUtf8()));
}

--- NEW FILE: qdbusmarshall_p.h ---
/* qdbusmarshall_p.h QDBusMarshall object
 *
 * Copyright (C) 2005 Harald Fernengel <harry at kdevelop.org>
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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 QDBUSMARSHALLPRIVATE_H
#define QDBUSMARSHALLPRIVATE_H

struct DBusMessage;

template <typename T> class QList;
class QVariant;
class QString;

/*!
    \internal
*/
class QDBusMarshall
{
public:
    static void listToMessage(const QList<QVariant> &list, DBusMessage *message,
                              const QString& signature);
    static void messageToList(QList<QVariant> &list, DBusMessage *message);
};

#endif

--- NEW FILE: qdbusmessage.cpp ---
/* qdbusmessage.cpp
 *
 * Copyright (C) 2005 Harald Fernengel <harry at kdevelop.org>
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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 <qdebug.h>
#include <qstringlist.h>

#include <dbus/dbus.h>

#include "qdbuserror.h"
#include "qdbusmarshall_p.h"
#include "qdbusmessage_p.h"
#include "qdbustypehelper_p.h"

QDBusMessagePrivate::QDBusMessagePrivate()
    : connection(QString()), msg(0), reply(0), type(DBUS_MESSAGE_TYPE_INVALID),
      timeout(-1), ref(1), repliedTo(false)
{
}

QDBusMessagePrivate::~QDBusMessagePrivate()
{
    if (msg)
        dbus_message_unref(msg);
    if (reply)
        dbus_message_unref(reply);
}

///////////////
/*!
    \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)
    \list
      \o Method calls
      \o Method return values
      \o Signal emissions
      \o Error codes
    \endlist

    Objects of this type are created with the four static functions signal, methodCall,
    methodReply and error.
*/

/*!
    \enum QDBusMessage::MessageType
    The possible message types:

    \value MethodCallMessage    a message representing an outgoing or incoming method call
    \value SignalMessage        a message representing an outgoing or incoming signal emission
    \value ReplyMessage         a message representing the return values of a method call
    \value ErrorMessage         a message representing an error condition in response to a method call
    \value InvalidMessage       an invalid message: this is never set on messages received from D-Bus
*/

/*!
    Constructs a new DBus message representing a signal emission. A DBus signal is emitted
    from one application and is received by all applications that are listening for that signal
    from that interface.

    The signal will be constructed to represent a signal coming from the path \a path, interface \a
    interface and signal name \a name.

    The QDBusMessage object that is returned can be sent with QDBusConnection::send().
*/
QDBusMessage QDBusMessage::signal(const QString &path, const QString &interface,
                                  const QString &name)
{
    QDBusMessage message;
    message.d_ptr->type = DBUS_MESSAGE_TYPE_SIGNAL;
    message.d_ptr->path = path;
    message.d_ptr->interface = interface;
    message.d_ptr->name = name;

    return message;
}

/*!
    Constructs a new DBus message representing a method call. A method call always informs
    its destination address (\a service, \a path, \a interface and \a method).

    The DBus bus allows calling a method on a given remote object without specifying the
    destination interface, if the method name is unique. However, if two interfaces on the
    remote object export the same method name, the result is undefined (one of the two may be
    called or an error may be returned).

    When using DBus in a peer-to-peer context (i.e., not on a bus), the \a service parameter is
    optional.

    The QDBusObject and QDBusInterface classes provide a simpler abstraction to synchronous
    method calling.

    This function returns a QDBusMessage object that can be sent with QDBusConnection::send(),
    QDBusConnection::sendWithReply(), or QDBusConnection::sendWithReplyAsync().
*/
QDBusMessage QDBusMessage::methodCall(const QString &service, const QString &path,
                                      const QString &interface, const QString &method)
{
    QDBusMessage message;
    message.d_ptr->type = DBUS_MESSAGE_TYPE_METHOD_CALL;
    message.d_ptr->service = service;
    message.d_ptr->path = path;
    message.d_ptr->interface = interface;
    message.d_ptr->name = method;

    return message;
}

/*!
    Constructs a new DBus message representing the return values from a called method. The \a other
    variable represents the method call that the reply will be for.
    
    This function returns a QDBusMessage object that can be sent with QDBusConnection::send().
*/
QDBusMessage QDBusMessage::methodReply(const QDBusMessage &other)
{
    Q_ASSERT(other.d_ptr->msg);

    QDBusMessage message;
    message.d_ptr->connection = other.d_ptr->connection;
    message.d_ptr->type = DBUS_MESSAGE_TYPE_METHOD_RETURN;
    message.d_ptr->reply = dbus_message_ref(other.d_ptr->msg);
    other.d_ptr->repliedTo = true;

    return message;
}

/*!
    Constructs a DBus message representing an error condition described by the \a name
    parameter. The \a msg parameter is optional and may contain a human-readable description of the
    error. The \a other variable represents the method call that this error relates to.

    This function returns 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_ptr->msg);

    QDBusMessage message;
    message.d_ptr->connection = other.d_ptr->connection;
    message.d_ptr->type = DBUS_MESSAGE_TYPE_ERROR;
    message.d_ptr->name = name;
    message.d_ptr->message = msg;
    message.d_ptr->reply = dbus_message_ref(other.d_ptr->msg);
    other.d_ptr->repliedTo = true;

    return message;
}

/*!
    \overload
    Constructs a DBus message representing an error, where \a other is the method call that
    generated this error and \a error is the error code.
*/
QDBusMessage QDBusMessage::error(const QDBusMessage &other, const QDBusError &error)
{
    Q_ASSERT(other.d_ptr->msg);

    QDBusMessage message;
    message.d_ptr->connection = other.d_ptr->connection;
    message.d_ptr->type = DBUS_MESSAGE_TYPE_ERROR;
    message.d_ptr->name = error.name();
    message.d_ptr->message = error.message();
    message.d_ptr->reply = dbus_message_ref(other.d_ptr->msg);
    other.d_ptr->repliedTo = true;

    return message;
}

/*!
    Constructs an empty, invalid QDBusMessage object.

    \sa methodCall(), methodReply(), signal(), error()
*/
QDBusMessage::QDBusMessage()
{
    d_ptr = new QDBusMessagePrivate;
}

/*!
    Constructs a copy of the object given by \a other.
*/
QDBusMessage::QDBusMessage(const QDBusMessage &other)
    : QList<QVariant>(other)
{
    d_ptr = other.d_ptr;
    d_ptr->ref.ref();
}

/*!
    Disposes of the object and frees any resources that were being held.
*/
QDBusMessage::~QDBusMessage()
{
    if (!d_ptr->ref.deref())
        delete d_ptr;
}

/*!
    Copies the contents of the object given by \a other.
*/
QDBusMessage &QDBusMessage::operator=(const QDBusMessage &other)
{
    QList<QVariant>::operator=(other);
    qAtomicAssign(d_ptr, other.d_ptr);
    return *this;
}

static inline const char *data(const QByteArray &arr)
{
    return arr.isEmpty() ? 0 : arr.constData();
}

/*!
    \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_ptr->type) {
    case DBUS_MESSAGE_TYPE_METHOD_CALL:
        msg = dbus_message_new_method_call(data(d_ptr->service.toUtf8()), data(d_ptr->path.toUtf8()),
                                           data(d_ptr->interface.toUtf8()), data(d_ptr->name.toUtf8()));
        break;
    case DBUS_MESSAGE_TYPE_SIGNAL:
        msg = dbus_message_new_signal(data(d_ptr->path.toUtf8()), data(d_ptr->interface.toUtf8()),
                                      data(d_ptr->name.toUtf8()));
        break;
    case DBUS_MESSAGE_TYPE_METHOD_RETURN:
        msg = dbus_message_new_method_return(d_ptr->reply);
        break;
    case DBUS_MESSAGE_TYPE_ERROR:
        msg = dbus_message_new_error(d_ptr->reply, data(d_ptr->name.toUtf8()), data(d_ptr->message.toUtf8()));
        break;
    }
    if (!msg)
        return 0;

    QDBusMarshall::listToMessage(*this, msg, d_ptr->signature);
    return msg;
}

/*!
    \internal
    Constructs a QDBusMessage by parsing the given DBusMessage object.
*/
QDBusMessage QDBusMessage::fromDBusMessage(DBusMessage *dmsg, const QDBusConnection &connection)
{
    QDBusMessage message;
    if (!dmsg)
        return message;

    message.d_ptr->connection = connection;
    message.d_ptr->type = dbus_message_get_type(dmsg);
    message.d_ptr->path = QString::fromUtf8(dbus_message_get_path(dmsg));
    message.d_ptr->interface = QString::fromUtf8(dbus_message_get_interface(dmsg));
    message.d_ptr->name = message.d_ptr->type == DBUS_MESSAGE_TYPE_ERROR ?
                      QString::fromUtf8(dbus_message_get_error_name(dmsg)) :
                      QString::fromUtf8(dbus_message_get_member(dmsg));
    message.d_ptr->service = QString::fromUtf8(dbus_message_get_sender(dmsg));
    message.d_ptr->signature = QString::fromUtf8(dbus_message_get_signature(dmsg));
    message.d_ptr->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_ptr->type = DBUS_MESSAGE_TYPE_ERROR;
    message.d_ptr->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_ptr->path;
}

/*!
    Returns the interface of the method being called (in the case of a method call) or of
    the signal being received from.
*/
QString QDBusMessage::interface() const
{
    return d_ptr->interface;
}

/*!
    Returns the name of the signal that was emitted or the name of the error that was
    received.
    \sa member()
*/
QString QDBusMessage::name() const
{
    return d_ptr->name;
}

/*!
    \fn QDBusMessage::member() const
    Returns the name of the method being called.
*/

/*!
    \fn QDBusMessage::method() const
    \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_ptr->service;
}

/*!
    \fn QDBusMessage::sender() const
    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_ptr->timeout;
}

/*!
    Sets the timeout for this message to be processed, given by \a ms, in milliseconds.
*/
void QDBusMessage::setTimeout(int ms)
{
    qAtomicDetach(d_ptr);
    d_ptr->timeout = ms;
}

/*!
    Returns the flag that indicates if this message should see a reply or not. This is only
    meaningful for MethodCall messages: any other kind of message cannot have replies and this
    function will always return false for them.
*/
bool QDBusMessage::noReply() const
{
    if (!d_ptr->msg)
        return false;
    return dbus_message_get_no_reply(d_ptr->msg);
}

/*!
    Returns the unique serial number assigned to this message
    or 0 if the message was not sent yet.
 */
int QDBusMessage::serialNumber() const
{
    if (!d_ptr->msg)
        return 0;
    return dbus_message_get_serial(d_ptr->msg);
}

/*!
    Returns the unique serial number assigned to the message
    that triggered this reply message.

    If this message is not a reply to another message, 0
    is returned.

 */
int QDBusMessage::replySerialNumber() const
{
    if (!d_ptr->msg)
        return 0;
    return dbus_message_get_reply_serial(d_ptr->msg);
}

/*!
    Returns true if this is a MethodCall message and a reply for it has been generated using
    QDBusMessage::methodReply or QDBusMessage::error.
*/
bool QDBusMessage::wasRepliedTo() const
{
    return d_ptr->repliedTo;
}

/*!
    Returns the signature of the signal that was received or for the output arguments
    of a method call.
*/
QString QDBusMessage::signature() const
{
    return d_ptr->signature;
}

/*!
    Sets the signature for the output arguments of this method call to be the value of \a
    signature. This function has no meaning in other types of messages or when dealing with received
    method calls.

    A message's signature indicate the type of the parameters to
    be marshalled over the bus. If there are more arguments than entries 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).

*/
void QDBusMessage::setSignature(const QString &signature)
{
    qAtomicDetach(d_ptr);
    d_ptr->signature = signature;
}

/*!
    Returns the connection this message was received on or an unconnected QDBusConnection object if
    this isn't a message that has been received.
*/
QDBusConnection QDBusMessage::connection() const
{
    return d_ptr->connection;
}

/*!
    Returns the message type.
*/
QDBusMessage::MessageType QDBusMessage::type() const
{
    switch (d_ptr->type) {
    case DBUS_MESSAGE_TYPE_METHOD_CALL:
        return MethodCallMessage;
    case DBUS_MESSAGE_TYPE_METHOD_RETURN:
        return ReplyMessage;
    case DBUS_MESSAGE_TYPE_ERROR:
        return ErrorMessage;
    case DBUS_MESSAGE_TYPE_SIGNAL:
        return SignalMessage;
    default:
        return InvalidMessage;
    }
}

// Document QDBusReply here
/*!
    \class QDBusReply
    \brief The reply for a method call to a remote object.

    A QDBusReply object is a subset of the QDBusMessage object that represents a method call's
    reply. It contains only the first output argument or the error code and is used by
    QDBusInterface-derived classes to allow returning the error code as the function's return
    argument.

    It can be used in the following manner:
    \code
        QDBusReply<QString> reply = interface->call("RemoteMethod");
        if (reply.isSuccess())
            // use the returned value
            useValue(reply.value());
        else
            // call failed. Show an error condition.
            showError(reply.error());
    \endcode

    If the remote method call cannot fail, you can skip the error checking:
    \code
        QString reply = interface->call("RemoteMethod");
    \endcode

    However, if it does fail under those conditions, the value returned by QDBusReply::value() is
    undefined. It may be undistinguishable from a valid return value.

    QDBusReply objects are used for remote calls that have no output arguments or return values
    (i.e., they have a "void" return type). In this case, you can only test if the reply succeeded
    or not, by calling isError() and isSuccess(), and inspecting the error condition by calling
    error(). You cannot call value().

    \sa QDBusMessage, QDBusInterface
*/

/*!
    \fn QDBusReply::QDBusReply(const QDBusMessage &reply)
    Automatically construct a QDBusReply object from the reply message \a reply, extracting the
    first return value from it if it is a success reply.
*/

/*!
    \fn QDBusReply::QDBusReply(const QDBusError &error)
    Constructs an error reply from the D-Bus error code given by \a error.
*/

/*!
    \fn QDBusReply::isError() const
    Returns true if this reply is an error reply. You can extract the error contents using the
    error() function.
*/

/*!
    \fn QDBusReply::isSuccess() const
    Returns true if this reply is a normal error reply (not an error). You can extract the returned
    value with value()
*/

/*!
    \fn QDBusReply::error()
    Returns the error code that was returned from the remote function call. If the remote call did
    not return an error (i.e., if it succeeded), then the QDBusError object that is returned will
    not be a valid error code (QDBusError::isValid() will return false).
*/

/*!
    \fn QDBusReply::value()
    Returns the remote function's calls return value. If the remote call returned with an error,
    the return value of this function is undefined and may be undistinguishable from a valid return
    value.

    This function is not available if the remote call returns \c void.
*/

/*!
    \fn QDBusReply::operator Type()
    Returns the same as value().
    
    This function is not available if the remote call returns \c void.
*/

/*!
    \internal
    \fn QDBusReply::fromVariant(const QDBusReply<QVariant> &variantReply)
    Converts the QDBusReply<QVariant> object to this type by converting the variant contained in
    \a variantReply to the template's type and copying the error condition.

    If the QVariant in variantReply is not convertible to this type, it will assume an undefined
    value.
*/

#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";
    }
}

static void debugVariantList(QDebug dbg, const QVariantList &list);
static void debugVariantMap(QDebug dbg, const QVariantMap &map);

static void debugVariant(QDebug dbg, const QVariant &v)
{
    dbg.nospace() << v.typeName() << "(";
    switch (v.userType())
    {
    case QVariant::Bool:
        dbg.nospace() << v.toBool();
        break;
    case QMetaType::UChar:
        dbg.nospace() << qvariant_cast<uchar>(v);
        break;
    case QMetaType::Short:
        dbg.nospace() << qvariant_cast<short>(v);
        break;
    case QMetaType::UShort:
        dbg.nospace() << qvariant_cast<ushort>(v);
        break;
    case QVariant::Int:
        dbg.nospace() << v.toInt();
        break;
    case QVariant::UInt:
        dbg.nospace() << v.toUInt();
        break;
    case QVariant::LongLong:
        dbg.nospace() << v.toLongLong();
        break;
    case QVariant::ULongLong:
        dbg.nospace() << v.toULongLong();
        break;
    case QVariant::Double:
        dbg.nospace() << v.toDouble();
        break;
    case QVariant::String:
        dbg.nospace() << v.toString();
        break;
    case QVariant::ByteArray:
        dbg.nospace() << v.toByteArray();
        break;
    case QVariant::StringList:
        dbg.nospace() << v.toStringList();
        break;
    case QVariant::List:
        debugVariantList(dbg, v.toList());
        break;
    case QVariant::Map:
        debugVariantMap(dbg, v.toMap());
        break;

    default: {
        int id = v.userType();
        if (id == QDBusTypeHelper<QVariant>::id())
            debugVariant(dbg, QDBusTypeHelper<QVariant>::fromVariant(v));
        else if (id == QDBusTypeHelper<bool>::listId())
            dbg.nospace() << QDBusTypeHelper<QList<bool> >::fromVariant(v);
        else if (id == QDBusTypeHelper<short>::listId())
            dbg.nospace() << QDBusTypeHelper<QList<short> >::fromVariant(v);
        else if (id == QDBusTypeHelper<ushort>::listId())
            dbg.nospace() << QDBusTypeHelper<QList<ushort> >::fromVariant(v);
        else if (id == QDBusTypeHelper<int>::listId())
            dbg.nospace() << QDBusTypeHelper<QList<int> >::fromVariant(v);
        else if (id == QDBusTypeHelper<uint>::listId())
            dbg.nospace() << QDBusTypeHelper<QList<uint> >::fromVariant(v);
        else if (id == QDBusTypeHelper<qlonglong>::listId())
            dbg.nospace() << QDBusTypeHelper<QList<qlonglong> >::fromVariant(v);
        else if (id == QDBusTypeHelper<qulonglong>::listId())
            dbg.nospace() << QDBusTypeHelper<QList<qulonglong> >::fromVariant(v);
        else if (id == QDBusTypeHelper<double>::listId())
            dbg.nospace() << QDBusTypeHelper<QList<double> >::fromVariant(v);
        else
            dbg.nospace() << "unknown";
    }
    }
    dbg.nospace() << ")";
}    

static void debugVariantList(QDebug dbg, const QVariantList &list)
{
    bool first = true;
    QVariantList::ConstIterator it = list.constBegin();
    QVariantList::ConstIterator end = list.constEnd();
    for ( ; it != end; ++it) {
        if (!first)
            dbg.nospace() << ", ";
        debugVariant(dbg, *it);
        first = false;
    }
}

static void debugVariantMap(QDebug dbg, const QVariantMap &map)
{
    QVariantMap::ConstIterator it = map.constBegin();
    QVariantMap::ConstIterator end = map.constEnd();
    for ( ; it != end; ++it) {
        dbg << "(" << it.key() << ", ";
        debugVariant(dbg, it.value());
        dbg << ") ";
    }
}        

QDebug operator<<(QDebug dbg, const QDBusMessage &msg)
{
    dbg.nospace() << "QDBusMessage(type=" << msg.type()
                  << ", service=" << msg.service()
                  << ", path=" << msg.path()
                  << ", interface=" << msg.interface()
                  << ", name=" << msg.name()
                  << ", signature=" << msg.signature()
                  << ", contents=(";
    debugVariantList(dbg, msg);
    dbg.nospace() << " ) )";
    return dbg.space();
}
#endif


--- NEW FILE: qdbusmessage.h ---
/* qdbusmessage.h QDBusMessage object
 *
 * Copyright (C) 2005 Harald Fernengel <harry at kdevelop.org>
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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 QDBUSMESSAGE_H
#define QDBUSMESSAGE_H

#include "qdbusmacros.h"
#include "qdbuserror.h"
#include <QtCore/qlist.h>
#include <QtCore/qvariant.h>

#include <limits.h>

class QDBusMessagePrivate;
class QDBusConnection;
class QDBusConnectionPrivate;
struct DBusMessage;

class QDBUS_EXPORT QDBusMessage: public QList<QVariant>
{
    //friend class QDBusConnection;
    friend class QDBusConnectionPrivate;
public:
    enum { DefaultTimeout = -1, NoTimeout = INT_MAX};
    enum MessageType { InvalidMessage, MethodCallMessage, ReplyMessage,
                       ErrorMessage, SignalMessage };

    QDBusMessage();
    QDBusMessage(const QDBusMessage &other);
    ~QDBusMessage();

    QDBusMessage &operator=(const QDBusMessage &other);

    static QDBusMessage signal(const QString &path, const QString &interface,
                               const QString &name);
    static QDBusMessage methodCall(const QString &destination, const QString &path,
                                   const QString &interface, const QString &method);
    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;
    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);

    bool noReply() const;

    QString signature() const;
    void setSignature(const QString &signature);

    QDBusConnection connection() const;

    int serialNumber() const;
    int replySerialNumber() const;
    bool wasRepliedTo() const;

private:
    DBusMessage *toDBusMessage() const;
    static QDBusMessage fromDBusMessage(DBusMessage *dmsg, const QDBusConnection &connection);
    static QDBusMessage fromError(const QDBusError& error);
    QDBusMessagePrivate *d_ptr;
};

#ifndef QT_NO_DEBUG
QDebug operator<<(QDebug, const QDBusMessage &);
#endif

#endif


--- NEW FILE: qdbusmessage_p.h ---
/* qdbusmessage.h QDBusMessage private object
 *
 * Copyright (C) 2005 Harald Fernengel <harry at kdevelop.org>
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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 QDBUSMESSAGE_P_H
#define QDBUSMESSAGE_P_H

#include <qatomic.h>
#include <qstring.h>
#include "qdbusconnection.h"
struct DBusMessage;

class QDBusMessagePrivate
{
public:
    QDBusMessagePrivate();
    ~QDBusMessagePrivate();

    QString service, path, interface, name, message, signature;
    QDBusConnection connection;
    DBusMessage *msg;
    DBusMessage *reply;
    int type;
    int timeout;
    QAtomic ref;

    mutable bool repliedTo : 1;
};

#endif

--- NEW FILE: qdbusmetaobject.cpp ---
/* -*- C++ -*-
 *
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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 "qdbusmetaobject_p.h"

#include <QtCore/qbytearray.h>
#include <QtCore/qhash.h>
#include <QtCore/qstring.h>
#include <QtCore/qvarlengtharray.h>

#include "qdbusutil.h"
#include "qdbuserror.h"
#include "qdbusintrospection_p.h"
#include "qdbusabstractinterface_p.h"

class QDBusMetaObjectGenerator
{
public:
    QDBusMetaObjectGenerator(const QString &interface,
                             const QDBusIntrospection::Interface *parsedData);
    void write(QDBusMetaObject *obj);
    void writeWithoutXml(QDBusMetaObject *obj);

private:
    struct Method {
        QByteArray parameters;
        QByteArray typeName;
        QByteArray tag;
        QByteArray inputSignature;
        QByteArray outputSignature;
        QVarLengthArray<int, 4> inputTypes;
        QVarLengthArray<int, 4> outputTypes;
        int flags;
    };
    
    struct Property {
        QByteArray typeName;
        QByteArray signature;
        int type;
        int flags;
    };

    enum PropertyFlags  {
        Invalid = 0x00000000,
        Readable = 0x00000001,
        Writable = 0x00000002,
        Resettable = 0x00000004,
        EnumOrFlag = 0x00000008,
        StdCppSet = 0x00000100,
  //    Override = 0x00000200,
        Designable = 0x00001000,
        ResolveDesignable = 0x00002000,
        Scriptable = 0x00004000,
        ResolveScriptable = 0x00008000,
        Stored = 0x00010000,
        ResolveStored = 0x00020000,
        Editable = 0x00040000,
        ResolveEditable = 0x00080000,
        User = 0x00100000,
        ResolveUser = 0x00200000
    };

    enum MethodFlags  {
        AccessPrivate = 0x00,
        AccessProtected = 0x01,
        AccessPublic = 0x02,
        AccessMask = 0x03, //mask

        MethodMethod = 0x00,
        MethodSignal = 0x04,
        MethodSlot = 0x08,
        MethodTypeMask = 0x0c,

        MethodCompatibility = 0x10,
        MethodCloned = 0x20,
        MethodScriptable = 0x40
    };

    QMap<QByteArray, Method> methods;
    QMap<QByteArray, Property> properties;
    
    const QDBusIntrospection::Interface *data;
    QString interface;

    void parseMethods();
    void parseSignals();
    void parseProperties();
};

static const int intsPerProperty = 2;
static const int intsPerMethod = 4;

// ### from kernel/qmetaobject.cpp (Qt 4.1.2):
struct QDBusMetaObjectPrivate
{
    int revision;
    int className;
    int classInfoCount, classInfoData;
    int methodCount, methodData;
    int propertyCount, propertyData;
    int enumeratorCount, enumeratorData;
    
    // this is specific for QDBusMetaObject:
    int propertyDBusData;
    int methodDBusData;
};

QDBusMetaObjectGenerator::QDBusMetaObjectGenerator(const QString &interfaceName,
                                                   const QDBusIntrospection::Interface *parsedData)
    : data(parsedData), interface(interfaceName)
{
    if (data) {
        parseProperties();
        parseSignals();             // call parseSignals first so that slots override signals
        parseMethods();
    }
}

void QDBusMetaObjectGenerator::parseMethods()
{
    foreach (const QDBusIntrospection::Method &m, data->methods) {
        Method mm;

        QByteArray prototype = m.name.toLatin1();
        prototype += '(';

        bool ok = true;

        // build the input argument list
        foreach (const QDBusIntrospection::Argument &arg, m.inputArgs) {
            int typeId = QDBusUtil::signatureToType(arg.type);
            if (typeId == QVariant::Invalid) {
                ok = false;
                break;
            }

            mm.inputSignature += arg.type;
            mm.inputTypes.append(typeId);

            mm.parameters.append(arg.name.toLatin1());
            mm.parameters.append(',');
            
            prototype.append( QVariant::typeToName( QVariant::Type(typeId) ) );
            prototype.append(',');
        }
        if (!ok) continue;

        // build the output argument list:
        for (int i = 0; i < m.outputArgs.count(); ++i) {
            const QDBusIntrospection::Argument &arg = m.outputArgs.at(i);

            int typeId = QDBusUtil::signatureToType(arg.type);
            if (typeId == QVariant::Invalid) {
                ok = false;
                break;
            }

            mm.outputSignature += arg.type;
            mm.outputTypes.append(typeId);

            if (i == 0) {
                // return value
                mm.typeName = QVariant::typeToName( QVariant::Type(typeId) );
            } else {
                // non-const ref parameter
                mm.parameters.append(arg.name.toLatin1());
                mm.parameters.append(',');

                prototype.append( QVariant::typeToName( QVariant::Type(typeId) ) );
                prototype.append("&,");
            }
        }
        if (!ok) continue;

        // convert the last commas:
        if (!mm.parameters.isEmpty()) {
            mm.parameters.truncate(mm.parameters.length() - 1);
            prototype[prototype.length() - 1] = ')';
        } else {
            prototype.append(')');
        }

        // check the async tag
        if (m.annotations.value(QLatin1String(ANNOTATION_NO_WAIT)) == QLatin1String("true"))
            mm.tag = "Q_ASYNC";

        // meta method flags
        mm.flags = AccessPublic | MethodSlot | MethodScriptable;

        // add
        methods.insert(QMetaObject::normalizedSignature(prototype), mm);
    }
}

void QDBusMetaObjectGenerator::parseSignals()
{
    foreach (const QDBusIntrospection::Signal &s, data->signals_) {
        Method mm;

        QByteArray prototype = s.name.toLatin1();
        prototype += '(';

        bool ok = true;

        // build the output argument list
        foreach (const QDBusIntrospection::Argument &arg, s.outputArgs) {
            int typeId = QDBusUtil::signatureToType(arg.type);
            if (typeId == QVariant::Invalid) {
                ok = false;
                break;
            }

            mm.inputSignature += arg.type;
            mm.inputTypes.append(typeId);

            mm.parameters.append(arg.name.toLatin1());
            mm.parameters.append(',');
            
            prototype.append( QVariant::typeToName( QVariant::Type(typeId) ) );
            prototype.append(',');
        }
        if (!ok) continue;

        // convert the last commas:
        if (!mm.parameters.isEmpty()) {
            mm.parameters.truncate(mm.parameters.length() - 1);
            prototype[prototype.length() - 1] = ')';
        } else {
            prototype.append(')');
        }

        // meta method flags
        mm.flags = AccessProtected | MethodSignal | MethodScriptable;

        // add
        methods.insert(QMetaObject::normalizedSignature(prototype), mm);
    }
}

void QDBusMetaObjectGenerator::parseProperties()
{
    foreach (const QDBusIntrospection::Property &p, data->properties) {
        Property mp;
        mp.type = QDBusUtil::signatureToType(p.type);
        if (mp.type == QVariant::Invalid)
            continue;
        
        QByteArray name = p.name.toLatin1();
        mp.signature = p.type.toLatin1();
        mp.typeName = QVariant::typeToName( QVariant::Type(mp.type) );

        // build the flags:
        mp.flags = StdCppSet | Scriptable | Stored;
        if (p.access != QDBusIntrospection::Property::Write)
            mp.flags |= Readable;
        if (p.access != QDBusIntrospection::Property::Read)
            mp.flags |= Writable;

        if (mp.typeName == "QVariant")
            mp.flags |= 0xff << 24;
        else if (mp.type < 0xff)
            // encode the type in the flags
            mp.flags |= mp.type << 24;

        // add the property:
        properties.insert(name, mp);
    }
}

void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj)
{
    // this code here is mostly copied from qaxbase.cpp
    // with a few modifications to make it cleaner
    
    QString className = interface;
    className.replace(QLatin1Char('.'), QLatin1String("::"));
    if (className.isEmpty())
        className = QLatin1String("QDBusInterface");

    QVarLengthArray<int> data;
    data.resize(sizeof(QDBusMetaObjectPrivate) / sizeof(int));

    QDBusMetaObjectPrivate *header = reinterpret_cast<QDBusMetaObjectPrivate *>(data.data());
    header->revision = 1;
    header->className = 0;
    header->classInfoCount = 0;
    header->classInfoData = 0;
    header->methodCount = methods.count();
    header->methodData = data.size();
    header->propertyCount = properties.count();
    header->propertyData = header->methodData + header->methodCount * 5;
    header->enumeratorCount = 0;
    header->enumeratorData = 0;
    header->propertyDBusData = header->propertyData + header->propertyCount * 3;
    header->methodDBusData = header->propertyDBusData + header->propertyCount * intsPerProperty;

    int data_size = data.size() +
                    (header->methodCount * (5+intsPerMethod)) +
                    (header->propertyCount * (3+intsPerProperty));
    foreach (const Method &mm, methods)
        data_size += 2 + mm.inputTypes.count() + mm.outputTypes.count();
    data.resize(data_size + 1);

    char null('\0');
    QByteArray stringdata = className.toLatin1();
    stringdata += null;
    stringdata.reserve(8192);

    int offset = header->methodData;
    int signatureOffset = header->methodDBusData;
    int typeidOffset = header->methodDBusData + header->methodCount * intsPerMethod;
    data[typeidOffset++] = 0;                           // eod

    // add each method:
    for (QMap<QByteArray, Method>::ConstIterator it = methods.constBegin();
         it != methods.constEnd(); ++it) {
        // form "prototype\0parameters\0typeName\0tag\0inputSignature\0outputSignature"
        const Method &mm = it.value();

        data[offset++] = stringdata.length();
        stringdata += it.key();                 // prototype
        stringdata += null;
        data[offset++] = stringdata.length();
        stringdata += mm.parameters;
        stringdata += null;
        data[offset++] = stringdata.length();
        stringdata += mm.typeName;
        stringdata += null;
        data[offset++] = stringdata.length();
        stringdata += mm.tag;
        stringdata += null;
        data[offset++] = mm.flags;

        data[signatureOffset++] = stringdata.length();
        stringdata += mm.inputSignature;
        stringdata += null;
        data[signatureOffset++] = stringdata.length();
        stringdata += mm.outputSignature;
        stringdata += null;

        data[signatureOffset++] = typeidOffset;
        data[typeidOffset++] = mm.inputTypes.count();
        memcpy(data.data() + typeidOffset, mm.inputTypes.data(), mm.inputTypes.count() * sizeof(int));
        typeidOffset += mm.inputTypes.count();

        data[signatureOffset++] = typeidOffset;
        data[typeidOffset++] = mm.outputTypes.count();
        memcpy(data.data() + typeidOffset, mm.outputTypes.data(), mm.outputTypes.count() * sizeof(int));
        typeidOffset += mm.outputTypes.count();
    }

    Q_ASSERT(offset == header->propertyData);
    Q_ASSERT(signatureOffset == header->methodDBusData + header->methodCount * intsPerMethod);
    Q_ASSERT(typeidOffset == data.size());

    // add each property
    signatureOffset = header->propertyDBusData;
    for (QMap<QByteArray, Property>::ConstIterator it = properties.constBegin();
         it != properties.constEnd(); ++it) {
        const Property &mp = it.value();

        // form is "name\0typeName\0signature\0"
        data[offset++] = stringdata.length();
        stringdata += it.key();                 // name
        stringdata += null;
        data[offset++] = stringdata.length();
        stringdata += mp.typeName;
        stringdata += null;
        data[offset++] = mp.flags;

        data[signatureOffset++] = stringdata.length();
        stringdata += mp.signature;
        stringdata += null;
        data[signatureOffset++] = mp.type;
    }

    Q_ASSERT(offset == header->propertyDBusData);
    Q_ASSERT(signatureOffset == header->methodDBusData);

    char *string_data = new char[stringdata.length()];
    memcpy(string_data, stringdata, stringdata.length());

    uint *uint_data = new uint[data.size()];
    memcpy(uint_data, data.data(), data.size() * sizeof(int));

    // put the metaobject together
    obj->d.data = uint_data;
    obj->d.extradata = 0;
    obj->d.stringdata = string_data;
    obj->d.superdata = &QDBusAbstractInterface::staticMetaObject;
}

#if 0
void QDBusMetaObjectGenerator::writeWithoutXml(const QString &interface)
{
    // no XML definition
    QString tmp(interface);
    tmp.replace(QLatin1Char('.'), QLatin1String("::"));
    QByteArray name(tmp.toLatin1());

    QDBusMetaObjectPrivate *header = new QDBusMetaObjectPrivate;
    memset(header, 0, sizeof *header);
    header->revision = 1;
    // leave the rest with 0

    char *stringdata = new char[name.length() + 1];
    stringdata[name.length()] = '\0';
    
    d.data = reinterpret_cast<uint*>(header);
    d.extradata = 0;
    d.stringdata = stringdata;
    d.superdata = &QDBusAbstractInterface::staticMetaObject;
    cached = false;
}
#endif

/////////
// class QDBusMetaObject

QDBusMetaObject *QDBusMetaObject::createMetaObject(const QString &interface, const QString &xml,
                                                   QHash<QString, QDBusMetaObject *> &cache,
                                                   QDBusError &error)
{
    error = QDBusError();
    QDBusIntrospection::Interfaces parsed = QDBusIntrospection::parseInterfaces(xml);

    QDBusMetaObject *we = 0;
    QDBusIntrospection::Interfaces::ConstIterator it = parsed.constBegin();
    QDBusIntrospection::Interfaces::ConstIterator end = parsed.constEnd();
    for ( ; it != end; ++it) {
        // check if it's in the cache
        QDBusMetaObject *obj = cache.value(it.key(), 0);
        if (!obj) {
            // not in cache; create
            obj = new QDBusMetaObject;
            QDBusMetaObjectGenerator generator(it.key(), it.value().constData());
            generator.write(obj);

            if ( (obj->cached = !it.key().startsWith( QLatin1String("local.") )) )
                // cache it
                cache.insert(it.key(), obj);

        }

        if (it.key() == interface)
            // it's us
            we = obj;
    }

    if (we)
        return we;
    // still nothing?
    
    if (parsed.isEmpty()) {
        // object didn't return introspection
        we = new QDBusMetaObject;
        QDBusMetaObjectGenerator generator(interface, 0);
        generator.write(we);
        we->cached = false;
        return we;
    } else if (interface.isEmpty()) {
        // merge all interfaces
        it = parsed.constBegin();
        QDBusIntrospection::Interface merged = *it.value().constData();
 
        for (++it; it != end; ++it) {
            merged.annotations.unite(it.value()->annotations);
            merged.methods.unite(it.value()->methods);
            merged.signals_.unite(it.value()->signals_);
            merged.properties.unite(it.value()->properties);
        }

        merged.name = QLatin1String("local.Merged");
        merged.introspection.clear();

        we = new QDBusMetaObject;
        QDBusMetaObjectGenerator generator(merged.name, &merged);
        generator.write(we);
        we->cached = false;
        return we;
    }

    // mark as an error
    error = QDBusError(QDBusError::UnknownInterface,
                       QString( QLatin1String("Interface '%1' was not found") )
                       .arg(interface));
    return 0;
}

QDBusMetaObject::QDBusMetaObject()
{
}

inline const QDBusMetaObjectPrivate *priv(const uint* data)
{
    return reinterpret_cast<const QDBusMetaObjectPrivate *>(data);
}

const char *QDBusMetaObject::dbusNameForMethod(int id) const
{
    //id -= methodOffset();
    if (id >= 0 && id < priv(d.data)->methodCount) {
        int handle = priv(d.data)->methodDBusData + id*intsPerMethod;
        return d.stringdata + d.data[handle];
    }
    return 0;
}

const char *QDBusMetaObject::inputSignatureForMethod(int id) const
{
    //id -= methodOffset();
    if (id >= 0 && id < priv(d.data)->methodCount) {
        int handle = priv(d.data)->methodDBusData + id*intsPerMethod;
        return d.stringdata + d.data[handle + 1];
    }
    return 0;
}

const char *QDBusMetaObject::outputSignatureForMethod(int id) const
{
    //id -= methodOffset();
    if (id >= 0 && id < priv(d.data)->methodCount) {
        int handle = priv(d.data)->methodDBusData + id*intsPerMethod;
        return d.stringdata + d.data[handle + 2];
    }
    return 0;
}

const int *QDBusMetaObject::inputTypesForMethod(int id) const
{
    //id -= methodOffset();
    if (id >= 0 && id < priv(d.data)->methodCount) {
        int handle = priv(d.data)->methodDBusData + id*intsPerMethod;
        return reinterpret_cast<const int*>(d.data + d.data[handle + 3]);
    }
    return 0;
}

const int *QDBusMetaObject::outputTypesForMethod(int id) const
{
    //id -= methodOffset();
    if (id >= 0 && id < priv(d.data)->methodCount) {
        int handle = priv(d.data)->methodDBusData + id*intsPerMethod;
        return reinterpret_cast<const int*>(d.data + d.data[handle + 4]);
    }
    return 0;
}

int QDBusMetaObject::propertyMetaType(int id) const
{
    //id -= propertyOffset();
    if (id >= 0 && id < priv(d.data)->propertyCount) {
        int handle = priv(d.data)->propertyDBusData + id*intsPerProperty;
        return d.data[handle + 1];
    }
    return 0;
}

template<typename T>
static inline void assign_helper(void *ptr, const QVariant &value)
{
    *reinterpret_cast<T *>(ptr) = qvariant_cast<T>(value);
}

void QDBusMetaObject::assign(void *ptr, const QVariant &value)
{
    switch (value.userType())
    {
    case QVariant::Bool:
        assign_helper<bool>(ptr, value);
        return;

    case QMetaType::UChar:
        assign_helper<uchar>(ptr, value);
        return;

    case QMetaType::Short:
        assign_helper<short>(ptr, value);
        return;

    case QMetaType::UShort:
        assign_helper<ushort>(ptr, value);
        return;

    case QVariant::Int:
        assign_helper<int>(ptr, value);
        return;

    case QVariant::UInt:
        assign_helper<uint>(ptr, value);
        return;

    case QVariant::LongLong:
        assign_helper<qlonglong>(ptr, value);
        return;

    case QVariant::ULongLong:
        assign_helper<qulonglong>(ptr, value);
        return;

    case QVariant::Double:
        assign_helper<double>(ptr, value);
        return;

    case QVariant::String:
        assign_helper<QString>(ptr, value);
        return;

    case QVariant::ByteArray:
        assign_helper<QByteArray>(ptr, value);
        return;

    case QVariant::List:
        assign_helper<QVariantList>(ptr, value);
        return;

    case QVariant::Map:
        assign_helper<QVariantMap>(ptr, value);
        return;

    default:
        ;    
    }
}

bool QDBusMetaTypeId::initialized = false;
int QDBusMetaTypeId::variant = 0;
int QDBusMetaTypeId::boollist = 0;
int QDBusMetaTypeId::shortlist = 0;
int QDBusMetaTypeId::ushortlist = 0;
int QDBusMetaTypeId::intlist = 0;
int QDBusMetaTypeId::uintlist = 0;
int QDBusMetaTypeId::longlonglist = 0;
int QDBusMetaTypeId::ulonglonglist = 0;
int QDBusMetaTypeId::doublelist = 0;

bool QDBusMetaTypeId::innerInitialize()
{
    variant = qRegisterMetaType<QVariant>("QVariant");
    boollist = qRegisterMetaType<QList<bool> >("QList<bool>");
    shortlist = qRegisterMetaType<QList<short> >("QList<short>");
    ushortlist = qRegisterMetaType<QList<ushort> >("QList<ushort>");
    intlist = qRegisterMetaType<QList<int> >("QList<int>");
    uintlist = qRegisterMetaType<QList<uint> >("QList<uint>");
    longlonglist = qRegisterMetaType<QList<qlonglong> >("QList<qlonglong>");
    ulonglonglist = qRegisterMetaType<QList<qulonglong> >("QList<qulonglong>");
    doublelist = qRegisterMetaType<QList<double> >("QList<double>");
    initialized = true;
    return true;
}

int qDBusMetaTypeId(QVariant *)
{ QDBusMetaTypeId::initialize(); return QDBusMetaTypeId::variant; }
int qDBusMetaTypeId(QList<bool> *)
{ QDBusMetaTypeId::initialize(); return QDBusMetaTypeId::boollist; }
int qDBusMetaTypeId(QList<short> *)
{ QDBusMetaTypeId::initialize(); return QDBusMetaTypeId::shortlist; }
int qDBusMetaTypeId(QList<ushort> *)
{ QDBusMetaTypeId::initialize(); return QDBusMetaTypeId::ushortlist; }
int qDBusMetaTypeId(QList<int> *)
{ QDBusMetaTypeId::initialize(); return QDBusMetaTypeId::intlist; }
int qDBusMetaTypeId(QList<uint> *)
{ QDBusMetaTypeId::initialize(); return QDBusMetaTypeId::uintlist; }
int qDBusMetaTypeId(QList<qlonglong> *)
{ QDBusMetaTypeId::initialize(); return QDBusMetaTypeId::longlonglist; }
int qDBusMetaTypeId(QList<qulonglong> *)
{ QDBusMetaTypeId::initialize(); return QDBusMetaTypeId::ulonglonglist; }
int qDBusMetaTypeId(QList<double> *)
{ QDBusMetaTypeId::initialize(); return QDBusMetaTypeId::doublelist; }

--- NEW FILE: qdbusmetaobject_p.h ---
/* 
 *
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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 QDBUSMETAOBJECTPRIVATE_H
#define QDBUSMETAOBJECTPRIVATE_H

#include <QtCore/qmetaobject.h>
#include "qdbusmacros.h"

class QDBusError;

struct QDBusMetaObjectPrivate;
struct QDBUS_EXPORT QDBusMetaObject: public QMetaObject
{
    bool cached;

    static QDBusMetaObject *createMetaObject(const QString &interface, const QString &xml,
                                             QHash<QString, QDBusMetaObject *> &map,
                                             QDBusError &error);
    ~QDBusMetaObject()
    {
        delete [] d.stringdata;
        delete [] d.data;
    }

    // methods (slots & signals):
    const char *dbusNameForMethod(int id) const;
    const char *inputSignatureForMethod(int id) const;
    const char *outputSignatureForMethod(int id) const;
    const int *inputTypesForMethod(int id) const;
    const int *outputTypesForMethod(int id) const;

    // properties:
    int propertyMetaType(int id) const;

    // helper function:
    static void assign(void *, const QVariant &value);

private:
    QDBusMetaObject();
};

struct QDBusMetaTypeId
{
    static bool innerInitialize();
    static bool initialized;
    static inline void initialize()
    {
        if (initialized) return;
        innerInitialize();
    }
    
    static int variant;
    static int boollist;
    static int shortlist;
    static int ushortlist;
    static int intlist;
    static int uintlist;
    static int longlonglist;
    static int ulonglonglist;
    static int doublelist;
};

#endif

--- NEW FILE: qdbusmisc.cpp ---
/* qdbusmisc.cpp Miscellaneous routines that didn't fit anywhere else
 *
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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 <string.h>

#include <QtCore/qvariant.h>
#include <QtCore/qmetaobject.h>

#include "qdbusconnection_p.h"
#include "qdbustypehelper_p.h"

bool qDBusCheckAsyncTag(const char *tag)
{
    if (!tag || !*tag)
        return false;

    const char *p = strstr(tag, "async");
    if (p != NULL &&
        (p == tag || *(p-1) == ' ') &&
        (p[5] == '\0' || p[5] == ' '))
        return true;

    p = strstr(tag, "Q_ASYNC");
    if (p != NULL &&
        (p == tag || *(p-1) == ' ') &&
        (p[7] == '\0' || p[7] == ' '))
        return true;

    return false;
}

int qDBusNameToTypeId(const char *name)
{
    int id = static_cast<int>( QVariant::nameToType(name) );
    if (id == QVariant::UserType)
        id = QMetaType::type(name);

    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 == QDBusConnectionPrivate::registerMessageMetaType() ||
            id == QDBusTypeHelper<QVariant>::id() ||
            id == QDBusTypeHelper<bool>::listId() ||
            id == QDBusTypeHelper<short>::listId() ||
            id == QDBusTypeHelper<ushort>::listId() ||
            id == QDBusTypeHelper<int>::listId() ||
            id == QDBusTypeHelper<qlonglong>::listId() ||
            id == QDBusTypeHelper<qulonglong>::listId() ||
            id == QDBusTypeHelper<double>::listId())
            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
int qDBusParametersForMethod(const QMetaMethod &mm, QList<int>& metaTypes)
{
    QList<QByteArray> parameterTypes = mm.parameterTypes();
    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'", mm.signature());
            // pointer?
            return -1;
        }

        if (type.endsWith('&')) {
            type.truncate(type.length() - 1);
            int id = qDBusNameToTypeId(type);
            if (id == 0) {
                //qWarning("Could not parse the method '%s'", mm.signature());
                // invalid type in method parameter list
                return -1;
            }

            metaTypes.append( id );
            seenMessage = true; // it cannot appear anymore anyways
            continue;
        }

        if (seenMessage) {      // && !type.endsWith('&')
            //qWarning("Could not parse the method '%s'", mm.signature());
            // non-output parameters after message or after output params
            return -1;          // not allowed
        }

        int id = qDBusNameToTypeId(type);
        if (id == 0) {
            //qWarning("Could not parse the method '%s'", mm.signature());
            // invalid type in method parameter list
            return -1;
        }
        metaTypes.append(id);
        ++inputCount;

        if (id == QDBusConnectionPrivate::registerMessageMetaType())
            seenMessage = true;
    }

    return inputCount;
}

--- NEW FILE: qdbusreply.h ---
/* qdbusreply.h QDBusReply object - a reply from D-Bus
 *
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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 QDBUSREPLY_H
#define QDBUSREPLY_H

#include <QtCore/qglobal.h>
#include <QtCore/qvariant.h>

#include "qdbusmacros.h"
#include "qdbusmessage.h"
#include "qdbuserror.h"

#include "qdbustypehelper_p.h"

template<typename T>
class QDBusReply
{
    typedef T Type;
public:
    inline QDBusReply(const QDBusMessage &reply)
        : m_data(Type())
    {
        *this = reply;
    }
    inline QDBusReply& operator=(const QDBusMessage& reply)
    {
        m_error = reply;
        if (isSuccess())
            m_data = QDBusTypeHelper<Type>::fromVariant(reply.at(0));
        else
            m_data = Type();
        return *this;
    }

    inline QDBusReply(const QDBusError &error = QDBusError())
        : m_error(error), m_data(Type())
    {
    }
    inline QDBusReply& operator=(const QDBusError& error)
    {
        m_error = error;
        m_data = Type();
        return *this;
    }

    inline QDBusReply& operator=(const QDBusReply& other)
    {
        m_error = other.m_error;
        m_data = other.m_data;
        return *this;
    }

    inline bool isError() const { return m_error.isValid(); }
    inline bool isSuccess() const { return !m_error.isValid(); }

    inline const QDBusError& error() { return m_error; }

    inline Type value()
    {
        return m_data;
    }

    inline operator Type ()
    {
        return m_data;
    }

    static QDBusReply<T> fromVariant(const QDBusReply<QVariant> &variantReply)
    {
        QDBusReply<T> retval;
        retval.m_error = variantReply.m_error;
        if (retval.isSuccess()) {
            retval.m_data = qvariant_cast<Type>(variantReply.m_data);
            if (!qVariantCanConvert<Type>(variantReply.m_data))
                retval.m_error = QDBusError(QDBusError::InvalidSignature,
                                            QLatin1String("Unexpected reply signature"));
        }
        return retval;
    }

private:
    QDBusError m_error;
    Type m_data;
};

# ifndef Q_QDOC
// specialize for void:
template<>
class QDBUS_EXPORT QDBusReply<void>
{
public:
    inline QDBusReply(const QDBusMessage &reply)
        : m_error(reply)
    {
    }
    inline QDBusReply(const QDBusError &error)
        : m_error(error)
    {
    }    

    inline bool isError() const { return m_error.isValid(); }
    inline bool isSuccess() const { return !m_error.isValid(); }

    inline const QDBusError& error() { return m_error; }

private:
    QDBusError m_error;
};
# endif

#endif

--- NEW FILE: qdbusserver.cpp ---
/* qdbusserver.cpp
 *
 * Copyright (C) 2005 Harald Fernengel <harry at kdevelop.org>
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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 "qdbusserver.h"
#include "qdbusconnection_p.h"

QDBusServer::QDBusServer(const QString &addr, QObject *parent)
    : QObject(parent)
{
    d = new QDBusConnectionPrivate(this);

    if (addr.isEmpty())
        return;

    d->setServer(dbus_server_listen(addr.toUtf8().constData(), &d->error));
}

bool QDBusServer::isConnected() const
{
    return d->server && dbus_server_get_is_connected(d->server);
}

QDBusError QDBusServer::lastError() const
{
    return d->lastError;
}

QString QDBusServer::address() const
{
    QString addr;
    if (d->server) {
        char *c = dbus_server_get_address(d->server);
        addr = QString::fromUtf8(c);
        dbus_free(c);
    }

    return addr;
}

#include "qdbusserver.moc"

--- NEW FILE: qdbusserver.h ---
/* qdbusserver.h QDBusServer object
 *
 * Copyright (C) 2005 Harald Fernengel <harry at 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 QDBUSSERVER_H
#define QDBUSSERVER_H

#include "qdbusmacros.h"
#include <QtCore/qobject.h>
#include <QtCore/qstring.h>

class QDBusConnectionPrivate;
class QDBusError;

class QDBUS_EXPORT QDBusServer: public QObject
{
    Q_OBJECT
public:
    QDBusServer(const QString &address, QObject *parent = 0);

    bool isConnected() const;
    QDBusError lastError() const;
    QString address() const;

private:
    Q_DISABLE_COPY(QDBusServer)
    QDBusConnectionPrivate *d;
};

#endif

--- NEW FILE: qdbusthread.cpp ---
/* qdbusintegrator.cpp QDBusConnection private implementation
 *
 * Copyright (C) 2005 Harald Fernengel <harry at kdevelop.org>
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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;
}

        

--- NEW FILE: qdbustype.cpp ---
/* -*- C++ -*-
 *
 * Copyright (C) 2005 Thiago Macieira <thiago at kde.org>
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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_p.h"
#include "qdbustypehelper_p.h"
#include <dbus/dbus.h>

#include <QtCore/qstringlist.h>

class QDBusTypePrivate: public QSharedData
{
public:
    int code;
    mutable int qvariantType;
    mutable QByteArray signature;
    QDBusTypeList subTypes;

    inline QDBusTypePrivate()
        : code(0), qvariantType(QVariant::Invalid)
    { }
};

/*!
    \class QDBusType
    \brief Represents one single D-Bus type.
    \internal

    D-Bus provides a set of primitive types that map to normal, C++ types and to QString, as well as
    the possibility to extend the set with the so-called "container" types. The available types are
    as follows:

    - Primitive (or basic): integers of 16, 32 and 64 bits, both signed and unsigned; byte (8 bits);
      double-precision floating point and Unicode strings
    - Arrays: a homogeneous, ordered list of zero or more entries
    - Maps: an unordered list of (key, value) pairs, where key must be a primitive type and value
      can be any D-Bus type
    - Structs: an ordered list of a fixed number of entries of any type
    - Variants: a "wildcard" container that can assume the value of any other type, including
      structs and arrays

    Any type can be placed inside an array (including other arrays), but only entries of the same
    type can be placed inside the same array. The analogous type for D-Bus arrays are the Qt
    #QList template classes.

    Structs have a fixed number of entries and each entry has a fixed type. They are analogous to C
    and C++ structs (hence the name).

    Maps or dictionaries are analogous to the Qt #QMap template class, with the additional
    restriction that the key type must be a primitive one. D-Bus implements maps by using arrays of
    a special type (a "dictionary entry"), so inspecting a QDBusType of a Map will reveal that it is
    an array (see isArray()).

    Variants contain exactly one entry, but the type can vary freely. It is analogous to the Qt
    class #QVariant, but the QtDBus implementation uses #QDBusVariant to represent D-Bus Variants.
*/

/*!
    Constructs an empty (invalid) type.
*/
QDBusType::QDBusType()
    : d(0)
{
}

/*!
    Constructs the type based on the D-Bus type given by \a type.
*/
QDBusType::QDBusType(int type)
{
    char c[2] = { type, 0 };
    *this = QDBusType(c);
}

/*!
    Constructs the type based on the QVariant type given by \a type.

    \sa QVariant::Type
*/
QDBusType::QDBusType(QVariant::Type type)
{
    const char *sig = dbusSignature(type);

    // it never returns NULL
    // but it may return an empty string:
    if (sig[0] == '\0')
        return;

    if (qstrlen(sig) > 2) {
        *this = QDBusType(sig);
    } else {
        d = new QDBusTypePrivate;
        d->qvariantType = type;
        d->code = sig[0];
        if (sig[1] == '\0')
            // single-letter type
            return;
        else {
            // 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;
        }
    }
}

/*!
    Parses the D-Bus signature given by \a signature and constructs the type it represents.
*/
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;
}

/*!
    \overload
    Parses the D-Bus signature given by \a str and constructs the type it represents.
*/
QDBusType::QDBusType(const QString& str)
{
    *this = QDBusType( str.toUtf8().constData() );
}

/*!
    \overload 
    Parses the D-Bus signature given by \a str and constructs the type it represents.
*/
QDBusType::QDBusType(const QByteArray& str)
{
    *this = QDBusType( str.constData() );
}

/*!
    \internal
    Creates a QDBusType object based on the current element pointed to by \a iter.
*/
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 object \a other.
*/
QDBusType::QDBusType(const QDBusType& other)
    : d(other.d)
{
}

/*!
    Release the resources associated with this type.
*/
QDBusType::~QDBusType()
{
}

/*!
    Copies the type from the object given by \a other.
*/
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.
*/
int QDBusType::qvariantType() const
{
    if (d && d->qvariantType != QVariant::Invalid)
        return d->qvariantType;

    if (!d)
        return QVariant::Invalid;

    return d->qvariantType = qvariantType(dbusSignature().constData());
}

/*!
    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.
*/
bool QDBusType::isBasic() const
{
    return d && dbus_type_is_basic(d->code);
}

/*!
    Returns true if this type is a 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 this type is the same one as \a other.
*/
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;
}

/*!
    \fn QDBusType::operator!=(const QDBusType &other) const
    Returns true if the this type and the one given by \a other are different.
*/

/*!
    Converts the DBus type code \a type to QVariant::Type.
*/
int QDBusType::qvariantType(int type)
{
    char c[2] = { type, 0 };
    return qvariantType(c);
}

/*!
    Converts the DBus type signature \a signature to QVariant::Type.
*/
int 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_BYTE:
        return QMetaType::UChar;

    case DBUS_TYPE_INT16:
        return QMetaType::Short;

    case DBUS_TYPE_UINT16:
        return QMetaType::UShort;
        
    case DBUS_TYPE_INT32:
        return QVariant::Int;
        
    case DBUS_TYPE_UINT32:
        return QVariant::UInt;

    case DBUS_TYPE_INT64:
        return QVariant::LongLong;

    case DBUS_TYPE_UINT64:
        return QVariant::ULongLong;

    case DBUS_TYPE_DOUBLE:
        return QVariant::Double;

    case DBUS_TYPE_STRING:
    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 QDBusTypeHelper<QVariant>::id();

    case DBUS_TYPE_ARRAY:       // special case
        switch (signature[1]) {
        case DBUS_TYPE_BOOLEAN:
            return QDBusTypeHelper<bool>::listId();

        case DBUS_TYPE_BYTE:
            return QVariant::ByteArray;

        case DBUS_TYPE_INT16:
            return QDBusTypeHelper<short>::listId();

        case DBUS_TYPE_UINT16:
            return QDBusTypeHelper<ushort>::listId();

        case DBUS_TYPE_INT32:
            return QDBusTypeHelper<int>::listId();

        case DBUS_TYPE_UINT32:
            return QDBusTypeHelper<uint>::listId();

        case DBUS_TYPE_INT64:
            return QDBusTypeHelper<qlonglong>::listId();

        case DBUS_TYPE_UINT64:
            return QDBusTypeHelper<qulonglong>::listId();

        case DBUS_TYPE_DOUBLE:
            return QDBusTypeHelper<double>::listId();

        case DBUS_TYPE_STRING:
        case DBUS_TYPE_OBJECT_PATH:
        case DBUS_TYPE_SIGNATURE:
            return QVariant::StringList;

        case DBUS_TYPE_VARIANT:
            return QVariant::List;

        case DBUS_DICT_ENTRY_BEGIN_CHAR:
            return QVariant::Map;

        default:
            return QVariant::List;
        }
    default:
        return QVariant::Invalid;

    }
}

/*!
    Converts the QVariant::Type \a t to a DBus type code.
*/
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:
        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:
        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_INVALID; // invalid

    default:
        break;                  // avoid compiler warnings
    }

    if (int(t) == QDBusTypeHelper<QVariant>::id())
        return DBUS_TYPE_VARIANT;

    return DBUS_TYPE_INVALID;
}

/*!
    Converts the QVariant::Type \a t to a DBus type signature.
*/
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:
        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:
        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) == QDBusTypeHelper<QVariant>::id())
            return DBUS_TYPE_VARIANT_AS_STRING;
        if (int(t) == QDBusTypeHelper<bool>::listId())
            return DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BOOLEAN_AS_STRING;
        if (int(t) == QDBusTypeHelper<short>::listId())
            return DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_INT16_AS_STRING;
        if (int(t) == QDBusTypeHelper<ushort>::listId())
            return DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_UINT16_AS_STRING;
        if (int(t) == QDBusTypeHelper<int>::listId())
            return DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_INT32_AS_STRING;
        if (int(t) == QDBusTypeHelper<uint>::listId())
            return DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_UINT32_AS_STRING;
        if (int(t) == QDBusTypeHelper<qlonglong>::listId())
            return DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_INT64_AS_STRING;
        if (int(t) == QDBusTypeHelper<qulonglong>::listId())
            return DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_UINT64_AS_STRING;
        if (int(t) == QDBusTypeHelper<double>::listId())
            return DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_DOUBLE_AS_STRING;

        return DBUS_TYPE_INVALID_AS_STRING;
    }
}

/*!
    \enum QDBusType::VariantListMode
    Defines how the guessFromVariant() function will behave when the QVariant is of type
    QVariant::List.
*/

/*!
    Guesses the DBus type from the given \a variant.
*/
QDBusType QDBusType::guessFromVariant(const QVariant& variant, VariantListMode mode)
{
    if (variant.type() == QVariant::List) {
        // investigate deeper
        QDBusType t;
        t.d = new QDBusTypePrivate;
        const QVariantList list = variant.toList();

        t.d->code = DBUS_TYPE_ARRAY;
        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;
            }
        } else {
            // an array of "something"
            t.d->subTypes << QDBusType('v');
            return t;
        }
            
        // treat it as a struct
        t.d->code = DBUS_TYPE_STRUCT;
        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( QVariant::Type( variant.userType() ) );
}

/*!
   \class QDBusTypeList
   \brief A list of DBus types.
   \internal

   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: copies the type list from \a other.
*/

/*!
   \fn QDBusTypeList::QDBusTypeList(const QList<QDBusType>& other)

   Copy constructor: copies the type list from \a other.
*/

/*!
   Constructs a type list by parsing the given \a signature.
*/
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));
}

/*!
    \internal
    Constructs a type list by parsing the elements on this iterator 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;
}

--- NEW FILE: qdbustype_p.h ---
/* -*- C++ -*-
 *
 * Copyright (C) 2005 Thiago Macieira <thiago at kde.org>
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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:
    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);

    int 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;
    inline bool operator!=(const QDBusType &other) const
    { return !(*this == other); }

    static int qvariantType(int type);
    static int 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;

    QByteArray dbusSignature() const;
};

#endif // QDBUSTYPE_H

--- NEW FILE: qdbustypehelper_p.h ---
/* qdbuslisthelper_p.h Helper class to convert to and from QVariantList
 *
 * Copyright (C) 2005 Harald Fernengel <harry at kdevelop.org>
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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 QDBUSTYPEHELPERPRIVATE_H
#define QDBUSTYPEHELPERPRIVATE_H

#include <QtCore/qlist.h>
#include <QtCore/qvariant.h>
#include <QtCore/qmetatype.h>

// we're going to support all D-Bus primitive types here:
// uchar -- not needed: QByteArray
// bool
// short
// ushort
// int
// uint
// qlonglong
// qulonglong
// double
// QString -- not needed: QStringList
// QList -- not possible: will use QVariant
// QVariant
// QDBusStruct -- not yet existant
// QMap -- not possible: will use QVariant

inline QDBUS_EXPORT int qDBusMetaTypeId(bool *) { return QVariant::Bool; }
inline QDBUS_EXPORT int qDBusMetaTypeId(uchar *) { return QMetaType::UChar; }
inline QDBUS_EXPORT int qDBusMetaTypeId(short *) { return QMetaType::Short; }
inline QDBUS_EXPORT int qDBusMetaTypeId(ushort *) { return QMetaType::UShort; }
inline QDBUS_EXPORT int qDBusMetaTypeId(int *) { return QVariant::Int; }
inline QDBUS_EXPORT int qDBusMetaTypeId(uint *) { return QVariant::UInt; }
inline QDBUS_EXPORT int qDBusMetaTypeId(qlonglong *) { return QVariant::LongLong; }
inline QDBUS_EXPORT int qDBusMetaTypeId(qulonglong *) { return QVariant::ULongLong; }
inline QDBUS_EXPORT int qDBusMetaTypeId(double *) { return QVariant::Double; }
inline QDBUS_EXPORT int qDBusMetaTypeId(QString *) { return QVariant::String; }
QDBUS_EXPORT int qDBusMetaTypeId(QVariant *);
QDBUS_EXPORT int qDBusMetaTypeId(QList<bool> *);
inline QDBUS_EXPORT int qDBusMetaTypeId(QByteArray *) { return QVariant::ByteArray; }
QDBUS_EXPORT int qDBusMetaTypeId(QList<short> *);
QDBUS_EXPORT int qDBusMetaTypeId(QList<ushort> *);
QDBUS_EXPORT int qDBusMetaTypeId(QList<int> *);
QDBUS_EXPORT int qDBusMetaTypeId(QList<uint> *);
QDBUS_EXPORT int qDBusMetaTypeId(QList<qlonglong> *);
QDBUS_EXPORT int qDBusMetaTypeId(QList<qulonglong> *);
QDBUS_EXPORT int qDBusMetaTypeId(QList<double> *);
inline QDBUS_EXPORT int qDBusMetaTypeId(QStringList *) { return QVariant::StringList; }
inline QDBUS_EXPORT int qDBusMetaTypeId(QVariantList *) { return QVariant::List; }
inline QDBUS_EXPORT int qDBusMetaTypeId(QVariantMap *) { return QVariant::Map; }

// implement the copy mechanism
template<class T>
struct QDBusTypeHelper
{
    typedef T Type;
    typedef QList<T> List;

    static inline int id()
    {
        Type* t = 0;
        return qDBusMetaTypeId(t);
    }

    static inline int listId()
    {
        List *l = 0;
        return qDBusMetaTypeId(l);
    }
    
    static inline QVariant toVariant(const Type &t)
    {
        return QVariant(id(), &t);
    }

    static bool canSpecialConvert(const QVariant &);
    static Type specialConvert(const QVariant &);

    static inline Type fromVariant(const QVariant &v)
    {
        if (canSpecialConvert(v))
            return specialConvert(v);

        QVariant copy(v);
        if (copy.convert( QVariant::Type(id()) ))
            return *reinterpret_cast<const Type *>(copy.constData());
        return Type();
    }

    static inline QVariantList toVariantList(const List list)
    {
        QVariantList tmp;
        Q_FOREACH (const Type &t, list)
            tmp.append(toVariant(t));
        return tmp;
    }

    static inline QVariantList toVariantList(const QVariant &v)
    {
        return toVariantList(QDBusTypeHelper<List>::fromVariant(v));
    }

    static inline List fromVariantList(const QVariantList list)
    {
        List tmp;
        Q_FOREACH (const QVariant &v, list)
            tmp.append(fromVariant(v));
        return tmp;
    }
};

template<>
struct QDBusTypeHelper<QVariant>
{
    static inline int id()
    {
        QVariant *t = 0;
        return qDBusMetaTypeId(t);
    }

    static inline int listId()
    {
        return QVariant::List;
    }

    static inline QVariant toVariant(const QVariant &t)
    {
        return QVariant(id(), &t);
    }

    static inline QVariant fromVariant(const QVariant &v)
    {
        if (v.userType() == id())
            return *reinterpret_cast<const QVariant *>(v.constData());
        return v;
    }

    static inline QVariantList toVariantList(const QVariantList &list)
    {
        return list;
    }

    static inline QVariantList fromVariantList(const QVariantList &list)
    {
        return list;
    }
};

#if !defined(QT_NO_CAST_FROM_ASCII) && !defined(QT_NO_CAST_TO_ASCII)
template<>
struct QDBusTypeHelper<char *>
{
    static inline int id()
    { return QVariant::String; }

    static inline QVariant toVariant(const char *t)
    { return QVariant(t); }

    static inline QByteArray fromVariant(const QVariant &v)
    { return v.toString().toAscii(); }
};

template<>
struct QDBusTypeHelper<const char *>
{
    static inline int id()
    { return QVariant::String; }

    static inline QVariant toVariant(const char *t)
    { return QVariant(t); }

    static inline QByteArray fromVariant(const QVariant &v)
    { return v.toString().toAscii(); }
};
#endif

// support three exceptions: uchar, short and ushort
// we have to do this as long as QVariant can't convert to/from the integer metatypes
template<> inline bool QDBusTypeHelper<short>::canSpecialConvert(const QVariant &v)
{ return v.userType() < int(QVariant::UserType); }
template<> inline short QDBusTypeHelper<short>::specialConvert(const QVariant &v)
{ return v.toInt(); }

template<> inline bool QDBusTypeHelper<ushort>::canSpecialConvert(const QVariant &v)
{ return v.userType() < int(QVariant::UserType); }
template<> inline ushort QDBusTypeHelper<ushort>::specialConvert(const QVariant &v)
{ return v.toUInt(); }

template<> inline bool QDBusTypeHelper<uchar>::canSpecialConvert(const QVariant &v)
{ return v.userType() < int(QVariant::UserType); }
template<> inline uchar QDBusTypeHelper<uchar>::specialConvert(const QVariant &v)
{ return v.toUInt(); }

template<typename T> inline bool QDBusTypeHelper<T>::canSpecialConvert(const QVariant &)
{ return false; }
template<typename T> inline T QDBusTypeHelper<T>::specialConvert(const QVariant &)
{ return T(); }

#endif

--- NEW FILE: qdbusutil.cpp ---
/* -*- C++ -*-
 *
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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>

#include "qdbustype_p.h"

/*!
    \namespace QDBusUtil
    The QDBusUtil namespace contains a few functions that are of general use when dealing with D-Bus
    strings.
*/
namespace QDBusUtil
{
    /*!
        \fn QDBusUtil::isValidInterfaceName(const QString &ifaceName)
        Returns true if this is \a ifaceName is a valid interface name.

        Valid interface names must:
        \list
          \o not be empty
          \o not exceed 255 characters in length
          \o be composed of dot-separated string components that contain only ASCII letters, digits
             and the underscore ("_") character
          \o contain at least two such components
        \endlist
    */
    bool isValidInterfaceName(const QString& ifaceName)
    {
        if (ifaceName.isEmpty() || ifaceName.length() > DBUS_MAXIMUM_NAME_LENGTH)
            return false;

        QStringList parts = ifaceName.split(QLatin1Char('.'));
        if (parts.count() < 2)
            return false;           // at least two parts

        foreach (QString part, parts)
            if (!isValidMemberName(part))
                return false;

        return true;
    }

    /*!
        \fn QDBusUtil::isValidUniqueConnectionName(const QString &connName)
        Returns true if \a connName is a valid unique connection name.

        Unique connection names start with a colon (":") and are followed by a list of dot-separated
        components composed of ASCII letters, digits, the hypen or the underscore ("_") character.
    */
    bool isValidUniqueConnectionName(const QString &connName)
    {
        if (connName.isEmpty() || connName.length() > DBUS_MAXIMUM_NAME_LENGTH ||
            !connName.startsWith(QLatin1Char(':')))
            return false;

        QStringList parts = connName.mid(1).split(QLatin1Char('.'));
        if (parts.count() < 1)
            return false;

        QRegExp regex(QLatin1String("[a-zA-Z0-9_-]+"));
        foreach (QString part, parts)
            if (!regex.exactMatch(part))
                return false;

        return true;
    }

    /*!
        \fn QDBusUtil::isValidBusName(const QString &busName)
        Returns true if \a busName is a valid bus name.

        A valid bus name is either a valid unique connection name or follows the rules:
        \list
          \o is not empty
          \o does not exceed 255 characters in length
          \o be composed of dot-separated string components that contain only ASCII letters, digits,
             hyphens or underscores ("_"), but don't start with a digit
          \o contains at least two such elements
        \endlist

        \sa isValidUniqueConnectionName()
    */
    bool isValidBusName(const QString &busName)
    {
        if (busName.isEmpty() || busName.length() > DBUS_MAXIMUM_NAME_LENGTH)
            return false;

        if (busName.startsWith(QLatin1Char(':')))
            return isValidUniqueConnectionName(busName);

        QStringList parts = busName.split(QLatin1Char('.'));
        if (parts.count() < 1)
            return false;

        QRegExp regex(QLatin1String("[a-zA-Z_-][a-zA-Z0-9_-]*"));
        foreach (QString part, parts)
            if (!regex.exactMatch(part))
                return false;

        return true;
    }

    /*!
        \fn QDBusUtil::isValidMemberName(const QString &memberName)
        Returns true if \a memberName is a valid member name. A valid member name does not exceed
        255 characters in length, is not empty, is composed only of ASCII letters, digits and
        underscores, but does not start with a digit.
    */
    bool isValidMemberName(const QString &memberName)
    {
        if (memberName.isEmpty() || memberName.length() > DBUS_MAXIMUM_NAME_LENGTH)
            return false;

        QRegExp regex(QLatin1String("[a-zA-Z_][a-zA-Z0-9_]+"));
        return regex.exactMatch(memberName);
    }

    /*!
        \fn QDBusUtil::isValidErrorName(const QString &errorName)
        Returns true if \a errorName is a valid error name. Valid error names are valid interface
        names and vice-versa, so this function is actually an alias for isValidInterfaceName.
    */
    bool isValidErrorName(const QString &errorName)
    {
        return isValidInterfaceName(errorName);
    }

    /*!
        \fn QDBusUtil::isValidObjectPath(const QString &path)
        Returns true if \a path is valid object path.

        Valid object paths follow the rules:
        \list
          \o start with the slash character ("/")
          \o do not end in a slash, unless the path is just the initial slash
          \o do not contain any two slashes in sequence
          \o contain slash-separated parts, each of which is composed of ASCII letters, digits and
             underscores ("_")
        \endlist
    */
    bool isValidObjectPath(const QString &path)
    {
        if (path == QLatin1String("/"))
            return true;

        if (!path.startsWith(QLatin1Char('/')) || path.indexOf(QLatin1String("//")) != -1 ||
            path.endsWith(QLatin1Char('/')))
            return false;

        QStringList parts = path.split(QLatin1Char('/'));
        Q_ASSERT(parts.count() >= 1);
        parts.removeFirst();    // it starts with /, so we get an empty first part

        QRegExp regex(QLatin1String("[a-zA-Z0-9_]+"));
        foreach (QString part, parts)
            if (!regex.exactMatch(part))
                return false;

        return true;
    }

    /*!
        \fn QDBusUtil::isValidSignature(const QString &signature)
        Returns true if \a signature is a valid D-Bus type signature for one or more types.
        This function returns true if it can all of \a signature into valid, individual types and no
        characters remain in \a signature.

        \sa isValidSingleSignature()
    */
    bool isValidSignature(const QString &signature)
    {
        return dbus_signature_validate(signature.toUtf8(), 0);
    }

    /*!
        \fn QDBusUtil::isValidSingleSignature(const QString &signature)
        Returns true if \a signature is a valid D-Bus type signature for exactly one full type. This
        function tries to convert the type signature into a D-Bus type and, if it succeeds and no
        characters remain in the signature, it returns true.
    */
    bool isValidSingleSignature(const QString &signature)
    {
        return dbus_signature_validate_single(signature.toUtf8(), 0);
    }

    /*!
        \fn QDBusUtil::signatureToType(const QString &signature)
        Returns the Qt meta type id for the given D-Bus signature for exactly one full type, given
        by \a signature.

        \sa isValidSingleSignature(), typeToSignature(), QVariant::type(), QVariant::userType()
    */
    QVariant::Type signatureToType(const QString &signature)
    {
        return QVariant::Type( QDBusType::qvariantType(signature.toLatin1().constData()) );
    }

    /*!
        \fn QDBusUtil::typeToSignature(QVariant::Type type)
        Returns the D-Bus signature equivalent to the supplied meta type id \a type.

        \sa isValidSingleSignature(), signatureToType(), QVariant::type(), QVariant::userType()
    */
    const char *typeToSignature(QVariant::Type type)
    {
        return QDBusType::dbusSignature( type );
    }

} // namespace QDBusUtil

--- NEW FILE: qdbusutil.h ---
/* -*- C++ -*-
 *
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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 <QtCore/qvariant.h>

#include "qdbusmacros.h"

namespace QDBusUtil
{
    QDBUS_EXPORT bool isValidInterfaceName(const QString &ifaceName);

    QDBUS_EXPORT bool isValidUniqueConnectionName(const QString &busName);

    QDBUS_EXPORT bool isValidBusName(const QString &busName);

    QDBUS_EXPORT bool isValidMemberName(const QString &memberName);

    QDBUS_EXPORT bool isValidErrorName(const QString &errorName);

    QDBUS_EXPORT bool isValidObjectPath(const QString &path);

    QDBUS_EXPORT bool isValidSignature(const QString &signature);

    QDBUS_EXPORT bool isValidSingleSignature(const QString &signature);

    QDBUS_EXPORT QVariant::Type signatureToType(const QString &signature);

    QDBUS_EXPORT const char *typeToSignature(QVariant::Type type);
}

#endif

--- NEW FILE: qdbusxmlgenerator.cpp ---
/* -*- mode: C++ -*-
 *
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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/qcoreapplication.h>
#include <QtCore/qmetaobject.h>
#include <QtCore/qstringlist.h>

#include "qdbusinterface_p.h"   // for ANNOTATION_NO_WAIT
#include "qdbusabstractadaptor_p.h" // for QCLASSINFO_DBUS_*
#include "qdbusconnection_p.h"  // for the flags
#include "qdbusutil.h"

extern QDBUS_EXPORT QString qDBusGenerateMetaObjectXml(QString interface, const QMetaObject *mo,
                                                       const QMetaObject *base, int flags);

// implement the D-Bus org.freedesktop.DBus.Introspectable interface
// we do that by analysing the metaObject of all the adaptor interfaces

static QString generateInterfaceXml(const QMetaObject *mo, int flags, int methodOffset, int propOffset)
{
    QString retval;

    // start with properties:
    if (flags & QDBusConnection::ExportProperties) {
        for (int i = propOffset; i < mo->propertyCount(); ++i) {
            static const char *accessvalues[] = {0, "read", "write", "readwrite"};

            QMetaProperty mp = mo->property(i);

            if (!mp.isScriptable() && (flags & QDBusConnection::ExportAllProperties) !=
                QDBusConnection::ExportAllProperties)
                continue;

            int access = 0;
            if (mp.isReadable())
                access |= 1;
            if (mp.isWritable())
                access |= 2;

            int typeId = qDBusNameToTypeId(mp.typeName());
            if (!typeId)
                continue;

            retval += QString(QLatin1String("    <property name=\"%1\" type=\"%2\" access=\"%3\" />\n"))
                      .arg(mp.name())
                      .arg(QLatin1String( QDBusUtil::typeToSignature( QVariant::Type(typeId) )))
                      .arg(QLatin1String( accessvalues[access] ));
        }
    }

    // now add methods:
    for (int i = methodOffset; i < mo->methodCount(); ++i) {
        QMetaMethod mm = mo->method(i);
        QByteArray signature = mm.signature();
        int paren = signature.indexOf('(');

        bool isSignal;
        if (mm.methodType() == QMetaMethod::Signal)
            // adding a signal
            isSignal = true;
        else if (mm.methodType() == QMetaMethod::Slot && mm.access() == QMetaMethod::Public)
            isSignal = false;
        else
            continue;           // neither signal nor public slot

        if ((isSignal && !(flags & QDBusConnection::ExportSignals)) ||
            (!isSignal && !(flags & QDBusConnection::ExportSlots)))
            continue;

        QString xml = QString(QLatin1String("    <%1 name=\"%2\">\n"))
                      .arg(isSignal ? QLatin1String("signal") : QLatin1String("method"))
                      .arg(QLatin1String(signature.left(paren)));

        // check the return type first
        int typeId = qDBusNameToTypeId(mm.typeName());
        if (typeId)
            xml += QString(QLatin1String("      <arg type=\"%1\" direction=\"out\"/>\n"))
                   .arg(QLatin1String(QDBusUtil::typeToSignature( QVariant::Type(typeId) )));
        else if (*mm.typeName())
            continue;           // wasn't a valid type

        QList<QByteArray> names = mm.parameterNames();
        QList<int> types;
        int inputCount = qDBusParametersForMethod(mm, types);
        if (inputCount == -1)
            continue;           // invalid form
        if (isSignal && inputCount + 1 != types.count())
            continue;           // signal with output arguments?
        if (isSignal && types.at(inputCount) == QDBusConnectionPrivate::messageMetaType)
            continue;           // signal with QDBusMessage argument?

        int j;
        bool isScriptable = mm.attributes() & QMetaMethod::Scriptable;
        for (j = 1; j < types.count(); ++j) {
            // input parameter for a slot or output for a signal
            if (types.at(j) == QDBusConnectionPrivate::messageMetaType) {
                isScriptable = true;
                continue;
            }

            QString name;
            if (!names.at(j - 1).isEmpty())
                name = QString(QLatin1String("name=\"%1\" ")).arg(QLatin1String(names.at(j - 1)));

            bool isOutput = isSignal || j > inputCount;

            xml += QString(QLatin1String("      <arg %1type=\"%2\" direction=\"%3\"/>\n"))
                   .arg(name)
                   .arg(QLatin1String(QDBusUtil::typeToSignature( QVariant::Type(types.at(j)) )))
                   .arg(isOutput ? QLatin1String("out") : QLatin1String("in"));
        }

        if (!isScriptable) {
            // check if this was added by other means
            if (isSignal && (flags & QDBusConnection::ExportAllSignals) != QDBusConnection::ExportAllSignals)
                continue;
            if (!isSignal && (flags & QDBusConnection::ExportAllSlots) != QDBusConnection::ExportAllSlots)
                continue;
        }

        if (qDBusCheckAsyncTag(mm.tag()))
            // add the no-reply annotation
            xml += QLatin1String("      <annotation name=\"" ANNOTATION_NO_WAIT "\""
                                 " value=\"true\"/>\n");

        retval += xml;
        retval += QString(QLatin1String("    </%1>\n"))
                  .arg(isSignal ? QLatin1String("signal") : QLatin1String("method"));
    }

    return retval;
}

QString qDBusGenerateMetaObjectXml(QString interface, const QMetaObject *mo, const QMetaObject *base,
                                   int flags)
{
    if (interface.isEmpty()) {
        // generate the interface name from the meta object
        int idx = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTERFACE);
        if (idx >= mo->classInfoOffset()) {
            interface = QLatin1String(mo->classInfo(idx).value());
        } else {
            interface = QLatin1String(mo->className());
            interface.replace(QLatin1String("::"), QLatin1String("."));

            if (interface.startsWith( QLatin1String("QDBus") )) {
                interface.prepend( QLatin1String("com.trolltech.QtDBus.") );
            } else if (interface.startsWith( QLatin1Char('Q') )) {
                // assume it's Qt
                interface.prepend( QLatin1String("com.trolltech.Qt.") );
            } else if (!QCoreApplication::instance() ||
                       QCoreApplication::instance()->applicationName().isEmpty()) {
                interface.prepend( QLatin1String("local.") );
            } else {
                interface.prepend(QLatin1Char('.')).prepend( QCoreApplication::instance()->applicationName() );
                QStringList domainName = QCoreApplication::instance()->organizationDomain().split(QLatin1Char('.'));
                foreach (const QString &part, domainName)
                    interface.prepend(QLatin1Char('.')).prepend(part);
            }
        }
    }

    QString xml;
    int idx = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTROSPECTION);
    if (idx >= mo->classInfoOffset())
        return QString::fromUtf8(mo->classInfo(idx).value());
    else
        xml = generateInterfaceXml(mo, flags, base->methodCount(), base->propertyCount());

    if (xml.isEmpty())
        return QString();       // don't add an empty interface
    return QString(QLatin1String("  <interface name=\"%1\">\n%2  </interface>\n"))
        .arg(interface, xml);
}

--- NEW FILE: qdbusxmlparser.cpp ---
/* -*- C++ -*-
 *
 * Copyright (C) 2005 Thiago Macieira <thiago at kde.org>
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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 "qdbusutil.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(QLatin1String("annotation"));
    for (int i = 0; i < list.count(); ++i)
    {
        QDomElement ann = list.item(i).toElement();
        if (ann.isNull())
            continue;

        QString name = ann.attribute(QLatin1String("name")),
               value = ann.attribute(QLatin1String("value"));

        if (name.isEmpty())
            continue;

        retval.insert(name, value);
    }

    return retval;
}

static QDBusIntrospection::Arguments
parseArgs(const QDomElement& elem, const QLatin1String& direction, bool acceptEmpty = false)
{
    QDBusIntrospection::Arguments retval;
    QDomNodeList list = elem.elementsByTagName(QLatin1String("arg"));
    for (int i = 0; i < list.count(); ++i)
    {
        QDomElement arg = list.item(i).toElement();
        if (arg.isNull())
            continue;

        if ((acceptEmpty && !arg.hasAttribute(QLatin1String("direction"))) ||
            arg.attribute(QLatin1String("direction")) == direction) {

            QDBusIntrospection::Argument argData;
            if (arg.hasAttribute(QLatin1String("name")))
                argData.name = arg.attribute(QLatin1String("name")); // can be empty
            argData.type = arg.attribute(QLatin1String("type"));
            if (!QDBusUtil::isValidSingleSignature(argData.type))
                continue;

            retval << argData;
        }
    }
    return retval;
}

QDBusXmlParser::QDBusXmlParser(const QString& service, const QString& path,
                               const QString& xmlData)
    : m_service(service), m_path(path)
{
    QDomDocument doc;
    doc.setContent(xmlData);
    m_node = doc.firstChildElement(QLatin1String("node"));
}

QDBusXmlParser::QDBusXmlParser(const QString& service, const QString& path,
                               const QDomElement& node)
    : m_service(service), m_path(path), m_node(node)
{
}

QDBusIntrospection::Interfaces
QDBusXmlParser::interfaces() const
{
    QDBusIntrospection::Interfaces retval;

    if (m_node.isNull())
        return retval;

    QDomNodeList interfaces = m_node.elementsByTagName(QLatin1String("interface"));
    for (int i = 0; i < interfaces.count(); ++i)
    {
        QDomElement iface = interfaces.item(i).toElement();
        QString ifaceName = iface.attribute(QLatin1String("name"));
        if (iface.isNull() || ifaceName.isEmpty())
            continue;           // for whatever reason

        QDBusIntrospection::Interface *ifaceData = new QDBusIntrospection::Interface;
        ifaceData->name = ifaceName;
        {
            // save the data
            QTextStream ts(&ifaceData->introspection);
            iface.save(ts,2);
        }

        // parse annotations
        ifaceData->annotations = parseAnnotations(iface);

        // parse methods
        QDomNodeList list = iface.elementsByTagName(QLatin1String("method"));
        for (int j = 0; j < list.count(); ++j)
        {
            QDomElement method = list.item(j).toElement();
            QString methodName = method.attribute(QLatin1String("name"));
            if (method.isNull() || 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(QLatin1String("signal"));
        for (int j = 0; j < list.count(); ++j)
        {
            QDomElement signal = list.item(j).toElement();
            QString signalName = signal.attribute(QLatin1String("name"));
            if (signal.isNull() || 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(QLatin1String("property"));
        for (int j = 0; j < list.count(); ++j)
        {
            QDomElement property = list.item(j).toElement();
            QString propertyName = property.attribute(QLatin1String("name"));
            if (property.isNull() || propertyName.isEmpty())
                continue;

            QDBusIntrospection::Property propertyData;

            // parse data
            propertyData.name = propertyName;
            propertyData.type = property.attribute(QLatin1String("type"));
            propertyData.annotations = parseAnnotations(property);

            if (!QDBusUtil::isValidSingleSignature(propertyData.type))
                // cannot be!
                continue;

            QString access = property.attribute(QLatin1String("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
{
    if (m_node.isNull())
        return QSharedDataPointer<QDBusIntrospection::Object>();

    QDBusIntrospection::Object* objData;
    objData = new QDBusIntrospection::Object;
    objData->service = m_service;
    objData->path = m_path;

    // check if we have anything to process
    if (objData->introspection.isNull() && !m_node.firstChild().isNull()) {
        // yes, introspect this object
        QTextStream ts(&objData->introspection);
        m_node.save(ts,2);

        QDomNodeList objects = m_node.elementsByTagName(QLatin1String("node"));
        for (int i = 0; i < objects.count(); ++i) {
            QDomElement obj = objects.item(i).toElement();
            QString objName = obj.attribute(QLatin1String("name"));
            if (obj.isNull() || objName.isEmpty())
                continue;           // for whatever reason

            objData->childObjects.append(objName);
        }

        QDomNodeList interfaces = m_node.elementsByTagName(QLatin1String("interface"));
        for (int i = 0; i < interfaces.count(); ++i) {
            QDomElement iface = interfaces.item(i).toElement();
            QString ifaceName = iface.attribute(QLatin1String("name"));
            if (iface.isNull() || ifaceName.isEmpty())
                continue;

            objData->interfaces.append(ifaceName);
        }
    } else {
        objData->introspection = QLatin1String("<node/>\n");
    }

    QSharedDataPointer<QDBusIntrospection::Object> retval;
    retval = objData;
    return retval;
}

QSharedDataPointer<QDBusIntrospection::ObjectTree>
QDBusXmlParser::objectTree() const
{
    QSharedDataPointer<QDBusIntrospection::ObjectTree> retval;

    if (m_node.isNull())
        return retval;

    retval = new QDBusIntrospection::ObjectTree;

    retval->service = m_service;
    retval->path = m_path;

    QTextStream ts(&retval->introspection);
    m_node.save(ts,2);

    // interfaces are easy:
    retval->interfaceData = interfaces();
    retval->interfaces = retval->interfaceData.keys();

    // sub-objects are slightly more difficult:
    QDomNodeList objects = m_node.elementsByTagName(QLatin1String("node"));
    for (int i = 0; i < objects.count(); ++i) {
        QDomElement obj = objects.item(i).toElement();
        QString objName = obj.attribute(QLatin1String("name"));
        if (obj.isNull() || objName.isEmpty())
            continue;           // for whatever reason

        // check if we have anything to process
        if (!obj.firstChild().isNull()) {
            // yes, introspect this object
            QString xml;
            QTextStream ts(&xml);
            obj.save(ts,0);

            // parse it
            QString objAbsName = m_path;
            if (!objAbsName.endsWith(QLatin1Char('/')))
                objAbsName.append(QLatin1Char('/'));
            objAbsName += objName;

            QDBusXmlParser parser(m_service, objAbsName, obj);
            retval->childObjectData.insert(objName, parser.objectTree());
        }

        retval->childObjects << objName;
    }

    return QSharedDataPointer<QDBusIntrospection::ObjectTree>( retval );
}


--- NEW FILE: qdbusxmlparser_p.h ---
/* -*- C++ -*-
 *
 * Copyright (C) 2005 Thiago Macieira <thiago at kde.org>
 * Copyright (C) 2006 Trolltech AS. All rights reserved.
 *    Author: Thiago Macieira <thiago.macieira at 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_p.h"

/*!
    \internal
*/
class QDBusXmlParser
{
    QString m_service;
    QString m_path;
    QDomElement m_node;

public:
    QDBusXmlParser(const QString& service, const QString& path,
                   const QString& xmlData);
    QDBusXmlParser(const QString& service, const QString& path,
                   const QDomElement& node);

    QDBusIntrospection::Interfaces interfaces() const;
    QSharedDataPointer<QDBusIntrospection::Object> object() const;
    QSharedDataPointer<QDBusIntrospection::ObjectTree> objectTree() const;
};

#endif



More information about the dbus-commit mailing list