src/dbus/qdbusconnection.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/dbus/qdbusconnection.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,1052 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDBus module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qdebug.h>
+#include <qcoreapplication.h>
+#include <qstringlist.h>
+
+#include "qdbusconnection.h"
+#include "qdbusconnectioninterface.h"
+#include "qdbuserror.h"
+#include "qdbusmessage.h"
+#include "qdbusmessage_p.h"
+#include "qdbusconnection_p.h"
+#include "qdbusinterface_p.h"
+#include "qdbusutil_p.h"
+
+#include "qdbusthreaddebug_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QDBusConnectionManager
+{
+public:
+    QDBusConnectionManager() {}
+    ~QDBusConnectionManager();
+
+    QDBusConnectionPrivate *connection(const QString &name) const;
+    void removeConnection(const QString &name);
+    void setConnection(const QString &name, QDBusConnectionPrivate *c);
+
+    QDBusConnectionPrivate *sender() const;
+    void setSender(const QDBusConnectionPrivate *s);
+
+    mutable QMutex mutex;
+private:
+    QHash<QString, QDBusConnectionPrivate *> connectionHash;
+
+    mutable QMutex senderMutex;
+    QString senderName; // internal; will probably change
+};
+
+Q_GLOBAL_STATIC(QDBusConnectionManager, _q_manager)
+
+QDBusConnectionPrivate *QDBusConnectionManager::sender() const
+{
+    QMutexLocker locker(&senderMutex);
+    return connection(senderName);
+}
+
+void QDBusConnectionManager::setSender(const QDBusConnectionPrivate *s)
+{
+    QMutexLocker locker(&senderMutex);
+    senderName = (s ? s->name : QString());
+}
+
+QDBusConnectionPrivate *QDBusConnectionManager::connection(const QString &name) const
+{
+    return connectionHash.value(name, 0);
+}
+
+void QDBusConnectionManager::removeConnection(const QString &name)
+{
+    QDBusConnectionPrivate *d = 0;
+    d = connectionHash.take(name);
+    if (d && !d->ref.deref())
+        d->deleteYourself();
+
+    // Static objects may be keeping the connection open.
+    // However, it is harmless to have outstanding references to a connection that is
+    // closing as long as those references will be soon dropped without being used.
+
+    // ### Output a warning if connections are being used after they have been removed.
+}
+
+QDBusConnectionManager::~QDBusConnectionManager()
+{
+    for (QHash<QString, QDBusConnectionPrivate *>::const_iterator it = connectionHash.constBegin();
+         it != connectionHash.constEnd(); ++it) {
+        QDBusConnectionPrivate *d = it.value();
+        if (!d->ref.deref())
+            d->deleteYourself();
+        else
+            d->closeConnection();
+    }
+    connectionHash.clear();
+}
+
+QDBUS_EXPORT void qDBusBindToApplication();
+void qDBusBindToApplication()
+{
+}
+
+void QDBusConnectionManager::setConnection(const QString &name, QDBusConnectionPrivate *c)
+{
+    connectionHash[name] = c;
+    c->name = name;
+}
+
+/*!
+    \fn QDBusConnection &QDBusConnection::sessionBus()
+    \relates QDBusConnection
+
+    Returns a QDBusConnection object opened with the session bus. The object reference returned
+    by this function is valid until the QCoreApplication's destructor is run, when the
+    connection will be closed and the object, deleted.
+*/
+/*!
+    \fn QDBusConnection &QDBusConnection::systemBus()
+    \relates QDBusConnection
+
+    Returns a QDBusConnection object opened with the system bus. The object reference returned
+    by this function is valid until the QCoreApplication's destructor is run, when the
+    connection will be closed and the object, deleted.
+*/
+
+/*!
+    \class QDBusConnection
+    \inmodule QtDBus
+    \since 4.2
+
+    \brief The QDBusConnection class represents a connection to the D-Bus bus daemon.
+
+    This class is the initial point in a D-Bus session. Using it, you
+    can get access to remote objects, interfaces; connect remote
+    signals to your object's slots; register objects, etc.
+
+    D-Bus connections are created using the connectToBus() function,
+    which opens a connection to the server daemon and does the initial
+    handshaking, associating that connection with a name. Further
+    attempts to connect using the same name will return the same
+    connection.
+
+    The connection is then torn down using the disconnectFromBus()
+    function.
+
+    As a convenience for the two most common connection types, the
+    sessionBus() and systemBus() functions return open connections to
+    the session server daemon and the system server daemon,
+    respectively. Those connections are opened when first used and are
+    closed when the QCoreApplication destructor is run.
+
+    D-Bus also supports peer-to-peer connections, without the need for
+    a bus server daemon. Using this facility, two applications can
+    talk to each other and exchange messages. This can be achieved by
+    passing an address to connectToBus() function, which was opened by
+    another D-Bus application using QDBusServer.
+*/
+
+/*!
+    \enum QDBusConnection::BusType
+    Specifies the type of the bus connection. The valid bus types are:
+
+    \value SessionBus           the session bus, associated with the running desktop session
+    \value SystemBus            the system bus, used to communicate with system-wide processes
+    \value ActivationBus        the activation bus, the "alias" for the bus that started the
+                                service
+
+    On the Session Bus, one can find other applications by the same user that are sharing the same
+    desktop session (hence the name). On the System Bus, however, processes shared for the whole
+    system are usually found.
+*/
+
+/*!
+    \enum QDBusConnection::RegisterOption
+    Specifies the options for registering objects with the connection. The possible values are:
+
+    \value ExportAdaptors                       export the contents of adaptors found in this object
+
+    \value ExportScriptableSlots                export this object's scriptable slots
+    \value ExportScriptableSignals              export this object's scriptable signals
+    \value ExportScriptableProperties           export this object's scriptable properties
+    \value ExportScriptableContents             shorthand form for ExportScriptableSlots |
+                                                ExportScriptableSignals |
+                                                ExportScriptableProperties
+
+    \value ExportNonScriptableSlots             export this object's non-scriptable slots
+    \value ExportNonScriptableSignals           export this object's non-scriptable signals
+    \value ExportNonScriptableProperties        export this object's non-scriptable properties
+    \value ExportNonScriptableContents          shorthand form for ExportNonScriptableSlots |
+                                                ExportNonScriptableSignals |
+                                                ExportNonScriptableProperties
+
+    \value ExportAllSlots                       export all of this object's slots
+    \value ExportAllSignals                     export all of this object's signals
+    \value ExportAllProperties                  export all of this object's properties
+    \value ExportAllContents                    export all of this object's contents
+
+    \value ExportChildObjects                   export this object's child objects
+
+    \sa registerObject(), QDBusAbstractAdaptor, {usingadaptors.html}{Using adaptors}
+*/
+
+/*!
+    \enum QDBusConnection::UnregisterMode
+    The mode for unregistering an object path:
+
+    \value UnregisterNode       unregister this node only: do not unregister child objects
+    \value UnregisterTree       unregister this node and all its sub-tree
+
+    Note, however, if this object was registered with the ExportChildObjects option, UnregisterNode
+    will unregister the child objects too.
+*/
+
+/*!
+    Creates a QDBusConnection object attached to the connection with name \a name.
+
+    This does not open the connection. You have to call connectToBus() to open it.
+*/
+QDBusConnection::QDBusConnection(const QString &name)
+{
+    if (name.isEmpty()) {
+        d = 0;
+    } else {
+        QMutexLocker locker(&_q_manager()->mutex);
+        d = _q_manager()->connection(name);
+        if (d)
+            d->ref.ref();
+    }
+}
+
+/*!
+    Creates a copy of the \a other connection.
+*/
+QDBusConnection::QDBusConnection(const QDBusConnection &other)
+{
+    d = other.d;
+    if (d)
+        d->ref.ref();
+}
+
+/*!
+  \internal
+   Creates a connection object with the given \a dd as private object.
+*/
+QDBusConnection::QDBusConnection(QDBusConnectionPrivate *dd)
+{
+    d = dd;
+    if (d)
+        d->ref.ref();
+}
+
+/*!
+    Disposes of this object. This does not close the connection: you
+    have to call disconnectFromBus() to do that.
+*/
+QDBusConnection::~QDBusConnection()
+{
+    if (d && !d->ref.deref())
+        d->deleteYourself();
+}
+
+/*!
+    Creates a copy of the connection \a other in this object. Note
+    that the connection this object referenced before the copy, is not
+    spontaneously disconnected.
+
+    \sa disconnectFromBus()
+*/
+QDBusConnection &QDBusConnection::operator=(const QDBusConnection &other)
+{
+    if (other.d)
+        other.d->ref.ref();
+    if (d && !d->ref.deref())
+        d->deleteYourself();
+    d = other.d;
+    return *this;
+}
+
+/*!
+    Opens a connection of type \a type to one of the known busses and
+    associate with it the connection name \a name. Returns a
+    QDBusConnection object associated with that connection.
+*/
+QDBusConnection QDBusConnection::connectToBus(BusType type, const QString &name)
+{
+//    Q_ASSERT_X(QCoreApplication::instance(), "QDBusConnection::addConnection",
+//               "Cannot create connection without a Q[Core]Application instance");
+    if (!qdbus_loadLibDBus()) {
+        QDBusConnectionPrivate *d = 0;
+        return QDBusConnection(d);
+    }
+
+    QMutexLocker locker(&_q_manager()->mutex);
+
+    QDBusConnectionPrivate *d = _q_manager()->connection(name);
+    if (d || name.isEmpty())
+        return QDBusConnection(d);
+
+    d = new QDBusConnectionPrivate;
+    DBusConnection *c = 0;
+    QDBusErrorInternal error;
+    switch (type) {
+        case SystemBus:
+            c = q_dbus_bus_get_private(DBUS_BUS_SYSTEM, error);
+            break;
+        case SessionBus:
+            c = q_dbus_bus_get_private(DBUS_BUS_SESSION, error);
+            break;
+        case ActivationBus:
+            c = q_dbus_bus_get_private(DBUS_BUS_STARTER, error);
+            break;
+    }
+    d->setConnection(c, error); //setConnection does the error handling for us
+
+    _q_manager()->setConnection(name, d);
+
+    QDBusConnection retval(d);
+
+    // create the bus service
+    // will lock in QDBusConnectionPrivate::connectRelay()
+    d->setBusService(retval);
+
+    return retval;
+}
+
+/*!
+    Opens a peer-to-peer connection on address \a address and associate with it the
+    connection name \a name. Returns a QDBusConnection object associated with that connection.
+*/
+QDBusConnection QDBusConnection::connectToBus(const QString &address,
+                                              const QString &name)
+{
+//    Q_ASSERT_X(QCoreApplication::instance(), "QDBusConnection::addConnection",
+//               "Cannot create connection without a Q[Core]Application instance");
+    if (!qdbus_loadLibDBus()){
+        QDBusConnectionPrivate *d = 0;
+        return QDBusConnection(d);
+    }
+
+    QMutexLocker locker(&_q_manager()->mutex);
+
+    QDBusConnectionPrivate *d = _q_manager()->connection(name);
+    if (d || name.isEmpty())
+        return QDBusConnection(d);
+
+    d = new QDBusConnectionPrivate;
+    // setConnection does the error handling for us
+    QDBusErrorInternal error;
+    DBusConnection *c = q_dbus_connection_open_private(address.toUtf8().constData(), error);
+    if (c) {
+        if (!q_dbus_bus_register(c, error)) {
+            q_dbus_connection_unref(c);
+            c = 0;
+        }
+    }
+    d->setConnection(c, error);
+    _q_manager()->setConnection(name, d);
+
+    QDBusConnection retval(d);
+
+    // create the bus service
+    // will lock in QDBusConnectionPrivate::connectRelay()
+    d->setBusService(retval);
+
+    return retval;
+}
+
+/*!
+    Closes the connection of name \a name.
+
+    Note that if there are still QDBusConnection objects associated
+    with the same connection, the connection will not be closed until
+    all references are dropped. However, no further references can be
+    created using the QDBusConnection constructor.
+*/
+void QDBusConnection::disconnectFromBus(const QString &name)
+{
+    if (_q_manager()) {
+        QMutexLocker locker(&_q_manager()->mutex);
+        _q_manager()->removeConnection(name);
+    }
+}
+
+/*!
+    Sends the \a message over this connection, without waiting for a
+    reply. This is suitable for errors, signals, and return values as
+    well as calls whose return values are not necessary.
+
+    Returns true if the message was queued successfully, false otherwise.
+*/
+bool QDBusConnection::send(const QDBusMessage &message) const
+{
+    if (!d || !d->connection) {
+        QDBusError err = QDBusError(QDBusError::Disconnected,
+                                    QLatin1String("Not connected to D-BUS server"));
+        if (d)
+            d->lastError = err;
+        return false;
+    }
+    return d->send(message) != 0;
+}
+
+/*!
+    Sends the \a message over this connection and returns immediately.
+    When the reply is received, the method \a returnMethod is called in
+    the \a receiver object. If an error occurs, the method \a errorMethod
+    will be called instead.
+
+    If no reply is received within \a timeout milliseconds, an automatic
+    error will be delivered indicating the expiration of the call.
+    The default \a timeout is -1, which will be replaced with an
+    implementation-defined value that is suitable for inter-process
+    communications (generally, 25 seconds).
+
+    This function is suitable for method calls only. It is guaranteed
+    that the slot will be called exactly once with the reply, as long
+    as the parameter types match and no error occurs.
+
+    Returns true if the message was sent, or false if the message could
+    not be sent.
+*/
+bool QDBusConnection::callWithCallback(const QDBusMessage &message, QObject *receiver,
+                                       const char *returnMethod, const char *errorMethod,
+                                       int timeout) const
+{
+    if (!d || !d->connection) {
+        QDBusError err = QDBusError(QDBusError::Disconnected,
+                                    QLatin1String("Not connected to D-BUS server"));
+        if (d)
+            d->lastError = err;
+        return false;
+    }
+    return d->sendWithReplyAsync(message, receiver, returnMethod, errorMethod, timeout) != 0;
+}
+
+/*!
+    \overload
+    \deprecated
+    Sends the \a message over this connection and returns immediately.
+    When the reply is received, the method \a returnMethod is called in
+    the \a receiver object.
+
+    This function is suitable for method calls only. It is guaranteed
+    that the slot will be called exactly once with the reply, as long
+    as the parameter types match and no error occurs.
+
+    This function is dangerous because it cannot report errors, including
+    the expiration of the timeout.
+
+    Returns true if the message was sent, or false if the message could
+    not be sent.
+*/
+bool QDBusConnection::callWithCallback(const QDBusMessage &message, QObject *receiver,
+                                       const char *returnMethod, int timeout) const
+{
+    return callWithCallback(message, receiver, returnMethod, 0, timeout);
+}
+
+/*!
+    Sends the \a message over this connection and blocks, waiting for
+    a reply, for at most \a timeout milliseconds. This function is
+    suitable for method calls only. It returns the reply message as
+    its return value, which will be either of type
+    QDBusMessage::ReplyMessage or QDBusMessage::ErrorMessage.
+
+    See the QDBusInterface::call() function for a more friendly way
+    of placing calls.
+
+    \warning If \a mode is QDBus::BlockWithGui, this function will
+             reenter the Qt event loop in order to wait for the
+             reply. During the wait, it may deliver signals and other
+             method calls to your application. Therefore, it must be
+             prepared to handle a reentrancy whenever a call is
+             placed with call().
+*/
+QDBusMessage QDBusConnection::call(const QDBusMessage &message, QDBus::CallMode mode, int timeout) const
+{
+    if (!d || !d->connection) {
+        QDBusError err = QDBusError(QDBusError::Disconnected,
+                                    QLatin1String("Not connected to D-Bus server"));
+        if (d)
+            d->lastError = err;
+
+        return QDBusMessage::createError(err);
+    }
+
+    if (mode != QDBus::NoBlock)
+        return d->sendWithReply(message, mode, timeout);
+
+    d->send(message);
+    QDBusMessage retval;
+    retval << QVariant(); // add one argument (to avoid .at(0) problems)
+    return retval;
+}
+
+/*!
+    \since 4.5
+    Sends the \a message over this connection and returns
+    immediately. This function is suitable for method calls only. It
+    returns an object of type QDBusPendingCall which can be used to
+    track the status of the reply. The \a timeout parameter is used to
+    determine when an auto-generated error reply may be emitted and is
+    also the upper limit for waiting in QDBusPendingCall::waitForFinished().
+
+    See the QDBusInterface::asyncCall() function for a more friendly way
+    of placing calls.
+*/
+QDBusPendingCall QDBusConnection::asyncCall(const QDBusMessage &message, int timeout) const
+{
+    if (!d || !d->connection) {
+        return QDBusPendingCall(0); // null pointer -> disconnected
+    }
+
+    QDBusPendingCallPrivate *priv = d->sendWithReplyAsync(message, timeout);
+    return QDBusPendingCall(priv);
+}
+
+/*!
+    Connects the signal specified by the \a service, \a path, \a interface and \a name parameters to
+    the slot \a slot in object \a receiver. The arguments \a service and \a path can be empty,
+    denoting a connection to any signal of the (\a interface, \a name) pair, from any remote
+    application.
+
+    Returns true if the connection was successful.
+
+    \warning The signal will only be delivered to the slot if the parameters match. This verification
+             can be done only when the signal is received, not at connection time.
+*/
+bool QDBusConnection::connect(const QString &service, const QString &path, const QString& interface,
+                              const QString &name, QObject *receiver, const char *slot)
+{
+    return connect(service, path, interface, name, QString(), receiver, slot);
+}
+
+/*!
+    Disconnects the signal specified by the \a service, \a path, \a interface and \a name parameters from
+    the slot \a slot in object \a receiver. The arguments \a service and \a path can be empty,
+    denoting a disconnection from all signals of the (\a interface, \a name) pair, from all remote
+    applications.
+
+    Returns true if the disconnection was successful.
+*/
+bool QDBusConnection::disconnect(const QString &service, const QString &path, const QString &interface,
+                                 const QString &name, QObject *receiver, const char *slot)
+{
+    return disconnect(service, path, interface, name, QString(), receiver, slot);
+}
+
+/*!
+    \overload
+
+    Connects the signal to the slot \a slot in object \a
+    receiver. Unlike the other connect() overload, this function
+    allows one to specify the parameter signature to be connected
+    using the \a signature variable. The function will then verify
+    that this signature can be delivered to the slot specified by \a
+    slot and return false otherwise.
+
+    \note This function verifies that the signal signature matches the
+          slot's parameters, but it does not verify that the actual
+          signal exists with the given signature in the remote
+          service.
+*/
+bool QDBusConnection::connect(const QString &service, const QString &path, const QString& interface,
+                              const QString &name, const QString &signature,
+                              QObject *receiver, const char *slot)
+{
+    if (!receiver || !slot || !d || !d->connection)
+        return false;
+    if (!interface.isEmpty() && !QDBusUtil::isValidInterfaceName(interface))
+        return false;
+    if (interface.isEmpty() && name.isEmpty())
+        return false;
+
+    // check the slot
+    QDBusConnectionPrivate::SignalHook hook;
+    QString key;
+    QString name2 = name;
+    if (name2.isNull())
+        name2.detach();
+
+    QString owner = d->getNameOwner(service); // we don't care if the owner is empty
+    hook.signature = signature;               // it might get started later
+    if (!d->prepareHook(hook, key, service, owner, path, interface, name, receiver, slot, 0, false))
+        return false;           // don't connect
+
+    // avoid duplicating:
+    QDBusWriteLocker locker(ConnectAction, d);
+    QDBusConnectionPrivate::SignalHookHash::ConstIterator it = d->signalHooks.find(key);
+    QDBusConnectionPrivate::SignalHookHash::ConstIterator end = d->signalHooks.constEnd();
+    for ( ; it != end && it.key() == key; ++it) {
+        const QDBusConnectionPrivate::SignalHook &entry = it.value();
+        if (entry.service == hook.service &&
+            entry.owner == hook.owner &&
+            entry.path == hook.path &&
+            entry.signature == hook.signature &&
+            entry.obj == hook.obj &&
+            entry.midx == hook.midx) {
+            // no need to compare the parameters if it's the same slot
+            return true;        // already there
+        }
+    }
+
+    d->connectSignal(key, hook);
+    return true;
+}
+
+/*!
+    \overload
+
+    Disconnects the signal from the slot \a slot in object \a
+    receiver. Unlike the other disconnect() overload, this function
+    allows one to specify the parameter signature to be disconnected
+    using the \a signature variable. The function will then verify
+    that this signature is connected to the slot specified by \a slot
+    and return false otherwise.
+*/
+bool QDBusConnection::disconnect(const QString &service, const QString &path, const QString& interface,
+                                 const QString &name, const QString &signature,
+                                 QObject *receiver, const char *slot)
+{
+    if (!receiver || !slot || !d || !d->connection)
+        return false;
+    if (!interface.isEmpty() && !QDBusUtil::isValidInterfaceName(interface))
+        return false;
+    if (interface.isEmpty() && name.isEmpty())
+        return false;
+
+    // check the slot
+    QDBusConnectionPrivate::SignalHook hook;
+    QString key;
+    QString name2 = name;
+    if (name2.isNull())
+        name2.detach();
+
+    QString owner = d->getNameOwner(service); // we don't care of owner is empty
+    hook.signature = signature;
+    if (!d->prepareHook(hook, key, service, owner, path, interface, name, receiver, slot, 0, false))
+        return false;           // don't disconnect
+
+    // avoid duplicating:
+    QDBusWriteLocker locker(DisconnectAction, d);
+    QDBusConnectionPrivate::SignalHookHash::Iterator it = d->signalHooks.find(key);
+    QDBusConnectionPrivate::SignalHookHash::Iterator end = d->signalHooks.end();
+    for ( ; it != end && it.key() == key; ++it) {
+        const QDBusConnectionPrivate::SignalHook &entry = it.value();
+        if (entry.service == hook.service &&
+            entry.owner == hook.owner &&
+            entry.path == hook.path &&
+            entry.signature == hook.signature &&
+            entry.obj == hook.obj &&
+            entry.midx == hook.midx) {
+            // no need to compare the parameters if it's the same slot
+            d->disconnectSignal(it);
+            return true;        // it was there
+        }
+    }
+
+    // the slot was not found
+    return false;
+}
+
+/*!
+    Registers the object \a object at path \a path and returns true if
+    the registration was successful. The \a options parameter
+    specifies how much of the object \a object will be exposed through
+    D-Bus.
+
+    This function does not replace existing objects: if there is already an object registered at
+    path \a path, this function will return false. Use unregisterObject() to unregister it first.
+
+    You cannot register an object as a child object of an object that
+    was registered with QDBusConnection::ExportChildObjects.
+*/
+bool QDBusConnection::registerObject(const QString &path, QObject *object, RegisterOptions options)
+{
+    Q_ASSERT_X(QDBusUtil::isValidObjectPath(path), "QDBusConnection::registerObject",
+               "Invalid object path given");
+    if (!d || !d->connection || !object || !options || !QDBusUtil::isValidObjectPath(path))
+        return false;
+
+    QStringList pathComponents = path.split(QLatin1Char('/'));
+    if (pathComponents.last().isEmpty())
+        pathComponents.removeLast();
+    QDBusWriteLocker locker(RegisterObjectAction, d);
+
+    // lower-bound search for where this object should enter in the tree
+    QDBusConnectionPrivate::ObjectTreeNode *node = &d->rootNode;
+    int i = 1;
+    while (node) {
+        if (pathComponents.count() == i) {
+            // this node exists
+            // consider it free if there's no object here and the user is not trying to
+            // replace the object sub-tree
+            if ((options & ExportChildObjects && !node->children.isEmpty()) || node->obj)
+                return false;
+
+            // we can add the object here
+            node->obj = object;
+            node->flags = options;
+
+            d->registerObject(node);
+            //qDebug("REGISTERED FOR %s", path.toLocal8Bit().constData());
+            return true;
+        }
+
+        // find the position where we'd insert the node
+        QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator it =
+            qLowerBound(node->children.begin(), node->children.end(), pathComponents.at(i));
+        if (it != node->children.end() && it->name == pathComponents.at(i)) {
+            // match: this node exists
+            node = it;
+
+            // are we allowed to go deeper?
+            if (node->flags & ExportChildObjects) {
+                // we're not
+                qDebug("Cannot register object at %s because %s exports its own child objects",
+                       qPrintable(path), qPrintable(pathComponents.at(i)));
+                return false;
+            }
+        } else {
+            // add entry
+            node = node->children.insert(it, pathComponents.at(i));
+        }
+
+        // iterate
+        ++i;
+    }
+
+    Q_ASSERT_X(false, "QDBusConnection::registerObject", "The impossible happened");
+    return false;
+}
+
+/*!
+    Unregisters an object that was registered with the registerObject() at the object path given by
+    \a path and, if \a mode is QDBusConnection::UnregisterTree, all of its sub-objects too.
+
+    Note that you cannot unregister objects that were not registered with registerObject().
+*/
+void QDBusConnection::unregisterObject(const QString &path, UnregisterMode mode)
+{
+    if (!d || !d->connection || !QDBusUtil::isValidObjectPath(path))
+        return;
+
+    QStringList pathComponents = path.split(QLatin1Char('/'));
+    QDBusWriteLocker locker(UnregisterObjectAction, d);
+    QDBusConnectionPrivate::ObjectTreeNode *node = &d->rootNode;
+    int i = 1;
+
+    // find the object
+    while (node) {
+        if (pathComponents.count() == i) {
+            // found it
+            node->obj = 0;
+            node->flags = 0;
+
+            if (mode == UnregisterTree) {
+                // clear the sub-tree as well
+                node->children.clear();  // can't disconnect the objects because we really don't know if they can
+                                // be found somewhere else in the path too
+            }
+
+            return;
+        }
+
+        QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator it =
+            qLowerBound(node->children.begin(), node->children.end(), pathComponents.at(i));
+        if (it == node->children.end() || it->name != pathComponents.at(i))
+            break;              // node not found
+
+        node = it;
+        ++i;
+    }
+}
+
+/*!
+    Return the object that was registered with the registerObject() at the object path given by
+    \a path.
+*/
+QObject *QDBusConnection::objectRegisteredAt(const QString &path) const
+{
+    Q_ASSERT_X(QDBusUtil::isValidObjectPath(path), "QDBusConnection::registeredObject",
+               "Invalid object path given");
+    if (!d || !d->connection || !QDBusUtil::isValidObjectPath(path))
+        return false;
+
+    QStringList pathComponents = path.split(QLatin1Char('/'));
+    if (pathComponents.last().isEmpty())
+        pathComponents.removeLast();
+
+    // lower-bound search for where this object should enter in the tree
+    QDBusReadLocker lock(ObjectRegisteredAtAction, d);
+    const QDBusConnectionPrivate::ObjectTreeNode *node = &d->rootNode;
+
+    int i = 1;
+    while (node) {
+        if (pathComponents.count() == i)
+            return node->obj;
+
+        QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator it =
+            qLowerBound(node->children.constBegin(), node->children.constEnd(), pathComponents.at(i));
+        if (it == node->children.constEnd() || it->name != pathComponents.at(i))
+            break;              // node not found
+
+        node = it;
+        ++i;
+    }
+    return 0;
+}
+
+/*!
+    Returns a QDBusConnectionInterface object that represents the
+    D-Bus server interface on this connection.
+*/
+QDBusConnectionInterface *QDBusConnection::interface() const
+{
+    if (!d)
+        return 0;
+    return d->busService;
+}
+
+/*!
+    Returns true if this QDBusConnection object is connected.
+
+    If it isn't connected, calling connectToBus() on the same
+    connection name will not make be connected. You need to call the
+    QDBusConnection constructor again.
+*/
+bool QDBusConnection::isConnected() const
+{
+    return d && d->connection && q_dbus_connection_get_is_connected(d->connection);
+}
+
+/*!
+    Returns the last error that happened in this connection.
+
+    This function is provided for low-level code. If you're using
+    QDBusInterface::call(), error codes are reported by its return
+    value.
+
+    \sa QDBusInterface, QDBusMessage
+*/
+QDBusError QDBusConnection::lastError() const
+{
+    return d ? d->lastError : QDBusError();
+}
+
+/*!
+    Returns the unique connection name for this connection, if this QDBusConnection object is
+    connected, or an empty QString otherwise.
+
+    A Unique Connection Name is a string in the form ":x.xxx" (where x
+    are decimal digits) that is assigned by the D-Bus server daemon
+    upon connection. It uniquely identifies this client in the bus.
+
+    This function returns an empty QString for peer-to-peer connections.
+*/
+QString QDBusConnection::baseService() const
+{
+    return d ? d->baseService : QString();
+}
+
+/*!
+    \since 4.5
+
+    Returns the connection name for this connection, as given as the
+    name parameter to connectToBus().
+
+    The connection name can be used to uniquely identify actual
+    underlying connections to buses.  Copies made from a single
+    connection will always implicitly share the underlying connection,
+    and hence will have the same connection name.
+
+    Inversely, two connections having different connection names will
+    always either be connected to different buses, or have a different
+    unique name (as returned by baseService()) on that bus.
+
+    \sa connectToBus(), disconnectFromBus()
+*/
+QString QDBusConnection::name() const
+{
+    return d ? d->name : QString();
+}
+
+/*!
+    Attempts to register the \a serviceName on the D-Bus server and
+    returns true if the registration succeded. The registration will
+    fail if the name is already registered by another application.
+
+    \sa unregisterService(), QDBusConnectionInterface::registerService()
+*/
+bool QDBusConnection::registerService(const QString &serviceName)
+{
+    if (interface() && interface()->registerService(serviceName)) {
+        if (d) d->registerService(serviceName);
+        return true;
+    }
+    return false;
+}
+
+/*!
+    Unregisters the service \a serviceName that was previously
+    registered with registerService() and returns true if it
+    succeeded.
+
+    \sa registerService(), QDBusConnectionInterface::unregisterService()
+*/
+bool QDBusConnection::unregisterService(const QString &serviceName)
+{
+    if (interface()->unregisterService(serviceName)) {
+        if (d) d->unregisterService(serviceName);
+        return true;
+    }
+    return false;
+}
+
+static const char _q_sessionBusName[] = "qt_default_session_bus";
+static const char _q_systemBusName[] = "qt_default_system_bus";
+
+class QDBusDefaultConnection: public QDBusConnection
+{
+    const char *ownName;
+public:
+    inline QDBusDefaultConnection(BusType type, const char *name)
+        : QDBusConnection(connectToBus(type, QString::fromLatin1(name))), ownName(name)
+    { }
+
+    inline ~QDBusDefaultConnection()
+    { disconnectFromBus(QString::fromLatin1(ownName)); }
+};
+
+Q_GLOBAL_STATIC_WITH_ARGS(QDBusDefaultConnection, _q_sessionBus,
+                          (QDBusConnection::SessionBus, _q_sessionBusName))
+Q_GLOBAL_STATIC_WITH_ARGS(QDBusDefaultConnection, _q_systemBus,
+                          (QDBusConnection::SystemBus, _q_systemBusName))
+
+QDBusConnection QDBusConnection::sessionBus()
+{
+    return *_q_sessionBus();
+}
+
+QDBusConnection QDBusConnection::systemBus()
+{
+    return *_q_systemBus();
+}
+
+/*!
+  \nonreentrant
+
+  Returns the connection that sent the signal, if called in a slot activated
+  by QDBus; otherwise it returns 0.
+
+  \note Please avoid this function. This function is not thread-safe, so if
+  there's any other thread delivering a D-Bus call, this function may return
+  the wrong connection. In new code, please use QDBusContext::connection()
+  (see that class for a description on how to use it).
+*/
+QDBusConnection QDBusConnection::sender()
+{
+    return QDBusConnection(_q_manager()->sender());
+}
+
+/*!
+  \internal
+*/
+void QDBusConnectionPrivate::setSender(const QDBusConnectionPrivate *s)
+{
+    _q_manager()->setSender(s);
+}
+
+/*!
+  \internal
+*/
+void QDBusConnectionPrivate::setConnection(const QString &name, QDBusConnectionPrivate *c)
+{
+    _q_manager()->setConnection(name, c);
+}
+
+/*!
+  \internal
+*/
+void QDBusConnectionPrivate::setBusService(const QDBusConnection &connection)
+{
+    busService = new QDBusConnectionInterface(connection, this);
+    ref.deref(); // busService has increased the refcounting to us
+                 // avoid cyclic refcounting
+//    if (mode != PeerMode)
+    QObject::connect(busService, SIGNAL(serviceOwnerChanged(QString,QString,QString)),
+                     this, SIGNAL(serviceOwnerChanged(QString,QString,QString)));
+
+    QObject::connect(this, SIGNAL(callWithCallbackFailed(QDBusError,QDBusMessage)),
+                     busService, SIGNAL(callWithCallbackFailed(QDBusError,QDBusMessage)),
+                     Qt::QueuedConnection);
+
+}
+
+/*!
+    \namespace QDBus
+    \inmodule QtDBus
+
+    \brief The QDBus namespace contains miscellaneous identifiers used
+    throughout the QtDBus library.
+*/
+
+/*!
+    \enum QDBus::CallMode
+
+    This enum describes the various ways of placing a function call. The valid modes are:
+
+    \value NoBlock              Place the call but don't wait for the reply (the reply's contents
+                                will be discarded).
+    \value Block                Don't use an event loop to wait for a reply, but instead block on
+                                network operations while waiting. This means the
+                                user-interface may not be updated until the function returns.
+    \value BlockWithGui         Use the Qt event loop to wait for a reply. This means that the
+                                user-interface will stay responsive (processing input events),
+                                but it also means other events may happen, like signal delivery
+                                and other D-Bus method calls.
+    \value AutoDetect           Automatically detect if the called function has a reply.
+
+    When using BlockWithGui, applications must be prepared for reentrancy in any function.
+*/
+
+QT_END_NAMESPACE