summaryrefslogtreecommitdiffstats
path: root/qt/src/qdbusintegrator.cpp
diff options
context:
space:
mode:
authorThiago Macieira <thiago@kde.org>2006-06-04 15:52:05 +0000
committerThiago Macieira <thiago@kde.org>2006-06-04 15:52:05 +0000
commitedbf2bfc109ce94b2604ea20328fda25542e4383 (patch)
tree3281d191fa9dde9f1f26b947b145d02373f031b0 /qt/src/qdbusintegrator.cpp
parent435c7af9b605c3ffc8641142b6d7add18bf7080b (diff)
* 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.
Diffstat (limited to 'qt/src/qdbusintegrator.cpp')
-rw-r--r--qt/src/qdbusintegrator.cpp1552
1 files changed, 1552 insertions, 0 deletions
diff --git a/qt/src/qdbusintegrator.cpp b/qt/src/qdbusintegrator.cpp
new file mode 100644
index 00000000..07921cf9
--- /dev/null
+++ b/qt/src/qdbusintegrator.cpp
@@ -0,0 +1,1552 @@
+/* qdbusintegrator.cpp QDBusConnection private implementation
+ *
+ * Copyright (C) 2005 Harald Fernengel <harry@kdevelop.org>
+ * Copyright (C) 2006 Trolltech AS. All rights reserved.
+ * Author: Thiago Macieira <thiago.macieira@trolltech.com>
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <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"