1 /* qdbusintegrator.cpp QDBusConnection private implementation
3 * Copyright (C) 2005 Harald Fernengel <harry@kdevelop.org>
4 * Copyright (C) 2006 Trolltech AS. All rights reserved.
5 * Author: Thiago Macieira <thiago.macieira@trolltech.com>
7 * Licensed under the Academic Free License version 2.1
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software Foundation
21 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 #include <qcoreapplication.h>
26 #include <qcoreevent.h>
28 #include <qmetaobject.h>
30 #include <qsocketnotifier.h>
31 #include <qstringlist.h>
34 #include "qdbusconnection_p.h"
35 #include "qdbusinterface_p.h"
36 #include "qdbusmessage.h"
37 #include "qdbusabstractadaptor.h"
38 #include "qdbusabstractadaptor_p.h"
39 #include "qdbustypehelper_p.h"
40 #include "qdbusutil.h"
41 #include "qdbustype_p.h"
43 #ifndef USE_OUTSIDE_DISPATCH
44 # define USE_OUTSIDE_DISPATCH 0
47 int QDBusConnectionPrivate::messageMetaType = 0;
49 typedef void (*QDBusSpyHook)(const QDBusMessage&);
50 typedef QVarLengthArray<QDBusSpyHook, 4> QDBusSpyHookList;
51 Q_GLOBAL_STATIC(QDBusSpyHookList, qDBusSpyHookList)
53 struct QDBusPendingCall
55 QPointer<QObject> receiver;
58 DBusPendingCall *pending;
59 const QDBusConnectionPrivate *connection;
62 class CallDeliveryEvent: public QEvent
66 : QEvent(QEvent::User), object(0), flags(0), slotIdx(-1)
69 const QDBusConnectionPrivate *conn;
70 QPointer<QObject> object;
78 static dbus_bool_t qDBusAddTimeout(DBusTimeout *timeout, void *data)
83 // qDebug("addTimeout %d", dbus_timeout_get_interval(timeout));
85 QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
87 if (!dbus_timeout_get_enabled(timeout))
90 if (!QCoreApplication::instance()) {
91 d->pendingTimeouts.append(timeout);
94 int timerId = d->startTimer(dbus_timeout_get_interval(timeout));
98 d->timeouts[timerId] = timeout;
102 static void qDBusRemoveTimeout(DBusTimeout *timeout, void *data)
107 // qDebug("removeTimeout");
109 QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
110 d->pendingTimeouts.removeAll(timeout);
112 QDBusConnectionPrivate::TimeoutHash::iterator it = d->timeouts.begin();
113 while (it != d->timeouts.end()) {
114 if (it.value() == timeout) {
115 d->killTimer(it.key());
116 it = d->timeouts.erase(it);
123 static void qDBusToggleTimeout(DBusTimeout *timeout, void *data)
128 //qDebug("ToggleTimeout");
130 qDBusRemoveTimeout(timeout, data);
131 qDBusAddTimeout(timeout, data);
134 static dbus_bool_t qDBusAddWatch(DBusWatch *watch, void *data)
139 QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
141 int flags = dbus_watch_get_flags(watch);
142 int fd = dbus_watch_get_fd(watch);
144 QDBusConnectionPrivate::Watcher watcher;
145 if (flags & DBUS_WATCH_READABLE) {
146 //qDebug("addReadWatch %d", fd);
147 watcher.watch = watch;
148 if (QCoreApplication::instance()) {
149 watcher.read = new QSocketNotifier(fd, QSocketNotifier::Read, d);
150 watcher.read->setEnabled(dbus_watch_get_enabled(watch));
151 d->connect(watcher.read, SIGNAL(activated(int)), SLOT(socketRead(int)));
154 if (flags & DBUS_WATCH_WRITABLE) {
155 //qDebug("addWriteWatch %d", fd);
156 watcher.watch = watch;
157 if (QCoreApplication::instance()) {
158 watcher.write = new QSocketNotifier(fd, QSocketNotifier::Write, d);
159 watcher.write->setEnabled(dbus_watch_get_enabled(watch));
160 d->connect(watcher.write, SIGNAL(activated(int)), SLOT(socketWrite(int)));
163 d->watchers.insertMulti(fd, watcher);
168 static void qDBusRemoveWatch(DBusWatch *watch, void *data)
173 //qDebug("remove watch");
175 QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
176 int fd = dbus_watch_get_fd(watch);
178 QDBusConnectionPrivate::WatcherHash::iterator i = d->watchers.find(fd);
179 while (i != d->watchers.end() && i.key() == fd) {
180 if (i.value().watch == watch) {
181 delete i.value().read;
182 delete i.value().write;
183 d->watchers.erase(i);
190 static void qDBusToggleWatch(DBusWatch *watch, void *data)
195 QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
196 int fd = dbus_watch_get_fd(watch);
198 QDBusConnectionPrivate::WatcherHash::iterator i = d->watchers.find(fd);
199 while (i != d->watchers.end() && i.key() == fd) {
200 if (i.value().watch == watch) {
201 bool enabled = dbus_watch_get_enabled(watch);
202 int flags = dbus_watch_get_flags(watch);
204 //qDebug("toggle watch %d to %d (write: %d, read: %d)", dbus_watch_get_fd(watch), enabled, flags & DBUS_WATCH_WRITABLE, flags & DBUS_WATCH_READABLE);
206 if (flags & DBUS_WATCH_READABLE && i.value().read)
207 i.value().read->setEnabled(enabled);
208 if (flags & DBUS_WATCH_WRITABLE && i.value().write)
209 i.value().write->setEnabled(enabled);
216 static void qDBusNewConnection(DBusServer *server, DBusConnection *c, void *data)
218 Q_ASSERT(data); Q_ASSERT(server); Q_ASSERT(c);
219 Q_UNUSED(data); Q_UNUSED(server); Q_UNUSED(c);
221 qDebug("SERVER: GOT A NEW CONNECTION"); // TODO
224 extern QDBUS_EXPORT void qDBusAddSpyHook(QDBusSpyHook);
225 void qDBusAddSpyHook(QDBusSpyHook hook)
227 qDBusSpyHookList()->append(hook);
230 #if USE_OUTSIDE_DISPATCH
231 # define HANDLED DBUS_HANDLER_RESULT_HANDLED_OUTSIDE_DISPATCH
232 static DBusHandlerResult qDBusSignalFilterOutside(DBusConnection *connection,
233 DBusMessage *message, void *data)
236 Q_UNUSED(connection);
239 QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
240 if (d->mode == QDBusConnectionPrivate::InvalidMode)
241 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; // internal error, actually
243 CallDeliveryEvent *e = d->postedCallDeliveryEvent();
248 return DBUS_HANDLER_RESULT_HANDLED;
251 # define HANDLED DBUS_HANDLER_RESULT_HANDLED
255 static DBusHandlerResult
256 qDBusSignalFilter(DBusConnection *connection, DBusMessage *message, void *data)
258 return QDBusConnectionPrivate::messageFilter(connection, message, data);
262 DBusHandlerResult QDBusConnectionPrivate::messageFilter(DBusConnection *connection,
263 DBusMessage *message, void *data)
266 Q_UNUSED(connection);
268 QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
269 if (d->mode == QDBusConnectionPrivate::InvalidMode)
270 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
272 QDBusMessage amsg = QDBusMessage::fromDBusMessage(message, QDBusConnection(d->name));
273 qDebug() << "got message:" << amsg;
275 const QDBusSpyHookList *list = qDBusSpyHookList();
276 for (int i = 0; i < list->size(); ++i) {
277 qDebug() << "calling the message spy hook";
281 bool handled = false;
282 int msgType = dbus_message_get_type(message);
283 if (msgType == DBUS_MESSAGE_TYPE_SIGNAL) {
284 handled = d->handleSignal(amsg);
285 } else if (msgType == DBUS_MESSAGE_TYPE_METHOD_CALL) {
286 handled = d->handleObjectCall(amsg);
289 return handled ? HANDLED :
290 DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
293 static void huntAndDestroy(QObject *needle, QDBusConnectionPrivate::ObjectTreeNode *haystack)
295 foreach (const QDBusConnectionPrivate::ObjectTreeNode::Data &entry, haystack->children)
296 huntAndDestroy(needle, entry.node);
298 if (needle == haystack->obj) {
304 static void huntAndEmit(DBusConnection *connection, DBusMessage *msg,
305 QObject *needle, QDBusConnectionPrivate::ObjectTreeNode *haystack,
306 const QString &path = QString())
308 foreach (const QDBusConnectionPrivate::ObjectTreeNode::Data &entry, haystack->children)
309 huntAndEmit(connection, msg, needle, entry.node, path + QLatin1String("/") + entry.name);
311 if (needle == haystack->obj && haystack->flags & QDBusConnection::ExportAdaptors) {
312 QByteArray p = path.toLatin1();
316 DBusMessage *msg2 = dbus_message_copy(msg);
317 dbus_message_set_path(msg2, p);
318 dbus_connection_send(connection, msg2, 0);
319 dbus_message_unref(msg2);
323 static bool typesMatch(int metaId, int variantType)
325 if (metaId == int(variantType))
328 if (variantType == QVariant::Int && metaId == QMetaType::Short)
331 if (variantType == QVariant::UInt && (metaId == QMetaType::UShort ||
332 metaId == QMetaType::UChar))
335 if (variantType == QVariant::List) {
336 if (metaId == QDBusTypeHelper<bool>::listId() ||
337 metaId == QDBusTypeHelper<short>::listId() ||
338 metaId == QDBusTypeHelper<ushort>::listId() ||
339 metaId == QDBusTypeHelper<int>::listId() ||
340 metaId == QDBusTypeHelper<uint>::listId() ||
341 metaId == QDBusTypeHelper<qlonglong>::listId() ||
342 metaId == QDBusTypeHelper<qulonglong>::listId() ||
343 metaId == QDBusTypeHelper<double>::listId())
347 return false; // no match
350 static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags,
351 const QDBusTypeList &types, QList<int>& metaTypes)
353 // find the first slot
354 const QMetaObject *super = mo;
355 while (super != &QObject::staticMetaObject &&
356 super != &QDBusAbstractAdaptor::staticMetaObject)
357 super = super->superClass();
359 int attributeMask = (flags & QDBusConnection::ExportAllSlots) ?
360 0 : QMetaMethod::Scriptable;
362 for (int idx = super->methodCount() ; idx <= mo->methodCount(); ++idx) {
363 QMetaMethod mm = mo->method(idx);
366 if (mm.access() != QMetaMethod::Public)
370 // unnecessary, since slots are never public:
371 //if (mm.methodType() != QMetaMethod::Slot)
375 QByteArray sig = QMetaObject::normalizedSignature(mm.signature());
376 int paren = sig.indexOf('(');
377 if (paren != name.length() || !sig.startsWith( name ))
380 int returnType = qDBusNameToTypeId(mm.typeName());
381 bool isAsync = qDBusCheckAsyncTag(mm.tag());
383 // consistency check:
384 if (isAsync && returnType != QMetaType::Void)
387 int inputCount = qDBusParametersForMethod(mm, metaTypes);
388 if (inputCount == -1)
389 continue; // problem parsing
391 metaTypes[0] = returnType;
392 bool hasMessage = false;
393 if (inputCount > 0 &&
394 metaTypes.at(inputCount) == QDBusConnectionPrivate::messageMetaType) {
395 // "no input parameters" is allowed as long as the message meta type is there
400 // try to match the parameters
401 if (inputCount != types.count())
402 continue; // not enough parameters
406 for (i = 0; i < types.count(); ++i)
407 if ( !typesMatch(metaTypes.at(i + 1), types.at(i).qvariantType()) ) {
413 continue; // we didn't match them all
415 // consistency check:
416 if (isAsync && metaTypes.count() > i + 1)
419 if (hasMessage && (mm.attributes() & attributeMask) != attributeMask)
420 continue; // not exported
422 // if we got here, this slot matched
430 static CallDeliveryEvent* prepareReply(QObject *object, int idx, const QList<int> &metaTypes,
431 const QDBusMessage &msg)
435 int n = metaTypes.count() - 1;
436 if (metaTypes[n] == QDBusConnectionPrivate::messageMetaType)
439 // check that types match
440 for (int i = 0; i < n; ++i)
441 if (!typesMatch(metaTypes.at(i + 1), msg.at(i).type()))
442 return 0; // no match
445 // prepare for the call
446 CallDeliveryEvent *data = new CallDeliveryEvent;
447 data->object = object;
450 data->metaTypes = metaTypes;
456 bool QDBusConnectionPrivate::activateSignal(const QDBusConnectionPrivate::SignalHook& hook,
457 const QDBusMessage &msg)
459 // This is called by QDBusConnectionPrivate::handleSignal to deliver a signal
460 // that was received from D-Bus
462 // Signals are delivered to slots if the parameters match
463 // Slots can have less parameters than there are on the message
464 // Slots can optionally have one final parameter that is a QDBusMessage
465 // Slots receive read-only copies of the message (i.e., pass by value or by const-ref)
466 CallDeliveryEvent *call = prepareReply(hook.obj, hook.midx, hook.params, msg);
468 postCallDeliveryEvent(call);
474 bool QDBusConnectionPrivate::activateCall(QObject* object, int flags,
475 const QDBusMessage &msg)
477 // This is called by QDBusConnectionPrivate::handleObjectCall to place a call
478 // to a slot on the object.
480 // The call is delivered to the first slot that matches the following conditions:
481 // - has the same name as the message's target name
482 // - ALL of the message's types are found in slot's parameter list
483 // - optionally has one more parameter of type QDBusMessage
484 // If none match, then the slot of the same name as the message target and with
485 // the first type of QDBusMessage is delivered.
487 // Because the marshalling of D-Bus data into QVariant loses the information on
488 // the original types, the message signature is used to determine the original type.
489 // Aside from that, the "int" and "unsigned" types will be tried as well.
491 // The D-Bus specification requires that all MethodCall messages be replied to, unless the
492 // caller specifically waived this requirement. This means that we inspect if the user slot
493 // generated a reply and, if it didn't, we will. Obviously, if the user slot doesn't take a
494 // QDBusMessage parameter, it cannot generate a reply.
496 // When a return message is generated, the slot's return type, if any, will be placed
497 // in the message's first position. If there are non-const reference parameters to the
498 // slot, they must appear at the end and will be placed in the subsequent message
504 QList<int> metaTypes;
508 const QMetaObject *mo = object->metaObject();
509 QDBusTypeList typeList(msg.signature().toUtf8());
510 QByteArray name = msg.name().toUtf8();
512 // find a slot that matches according to the rules above
513 idx = ::findSlot(mo, name, flags, typeList, metaTypes);
515 // try with no parameters, but with a QDBusMessage
516 idx = ::findSlot(mo, name, flags, QDBusTypeList(), metaTypes);
517 if (metaTypes.count() != 2 || metaTypes.at(1) != messageMetaType)
522 // found the slot to be called
523 // prepare for the call:
524 CallDeliveryEvent *call = new CallDeliveryEvent;
527 call->object = object;
532 call->metaTypes = metaTypes;
535 postCallDeliveryEvent(call);
541 void QDBusConnectionPrivate::postCallDeliveryEvent(CallDeliveryEvent *data)
545 #if USE_OUTSIDE_DISPATCH
546 callDeliveryMutex.lock();
547 callDeliveryState = data;
549 QCoreApplication::postEvent( this, data );
553 CallDeliveryEvent *QDBusConnectionPrivate::postedCallDeliveryEvent()
555 CallDeliveryEvent *e = callDeliveryState;
556 Q_ASSERT(e && e->conn == this);
559 callDeliveryState = 0;
560 callDeliveryMutex.unlock();
565 void QDBusConnectionPrivate::deliverCall(const CallDeliveryEvent& data) const
568 const QList<int>& metaTypes = data.metaTypes;
569 const QDBusMessage& msg = data.message;
571 QVarLengthArray<void *, 10> params;
572 params.reserve(metaTypes.count());
574 QVariantList auxParameters;
575 // let's create the parameter list
577 // first one is the return type -- add it below
580 // add the input parameters
582 for (i = 1; i <= msg.count(); ++i) {
583 int id = metaTypes[i];
584 if (id == QDBusConnectionPrivate::messageMetaType)
587 if (id == int(msg.at(i - 1).userType()))
588 // no conversion needed
589 params.append(const_cast<void *>( msg.at(i - 1).constData() ));
591 // convert to what the function expects
592 auxParameters.append(QVariant());
594 const QVariant &in = msg.at(i - 1);
595 QVariant &out = auxParameters[auxParameters.count() - 1];
598 if (id == QVariant::List) {
599 int mid = in.userType();
600 // the only conversion possible here is from a specialised QList<T> to QVariantList
601 if (mid == QDBusTypeHelper<bool>::listId())
602 out = qVariantFromValue(QDBusTypeHelper<bool>::toVariantList(in));
603 else if (mid == QDBusTypeHelper<short>::listId())
604 out = qVariantFromValue(QDBusTypeHelper<short>::toVariantList(in));
605 else if (mid == QDBusTypeHelper<ushort>::listId())
606 out = qVariantFromValue(QDBusTypeHelper<ushort>::toVariantList(in));
607 else if (mid == QDBusTypeHelper<int>::listId())
608 out = qVariantFromValue(QDBusTypeHelper<int>::toVariantList(in));
609 else if (mid == QDBusTypeHelper<uint>::listId())
610 out = qVariantFromValue(QDBusTypeHelper<uint>::toVariantList(in));
611 else if (mid == QDBusTypeHelper<qlonglong>::listId())
612 out = qVariantFromValue(QDBusTypeHelper<qlonglong>::toVariantList(in));
613 else if (mid == QDBusTypeHelper<qulonglong>::listId())
614 out = qVariantFromValue(QDBusTypeHelper<qulonglong>::toVariantList(in));
615 else if (mid == QDBusTypeHelper<double>::listId())
616 out = qVariantFromValue(QDBusTypeHelper<double>::toVariantList(in));
619 } else if (in.type() == QVariant::UInt) {
620 if (id == QMetaType::UChar) {
621 uchar uc = in.toUInt();
622 out = qVariantFromValue(uc);
623 } else if (id == QMetaType::UShort) {
624 ushort us = in.toUInt();
625 out = qVariantFromValue(us);
629 } else if (in.type() == QVariant::Int) {
630 if (id == QMetaType::Short) {
631 short s = in.toInt();
632 out = qVariantFromValue(s);
641 qFatal("Internal error: got invalid meta type %d when trying to convert to meta type %d",
644 params.append( const_cast<void *>(out.constData()) );
648 bool takesMessage = false;
649 if (metaTypes.count() > i && metaTypes[i] == QDBusConnectionPrivate::messageMetaType) {
650 params.append(const_cast<void*>(static_cast<const void*>(&msg)));
656 QVariantList outputArgs;
658 if (metaTypes[0] != QMetaType::Void) {
659 QVariant arg(metaTypes[0], null);
660 outputArgs.append( arg );
661 params[0] = const_cast<void*>(outputArgs.at( outputArgs.count() - 1 ).constData());
663 for ( ; i < metaTypes.count(); ++i) {
664 QVariant arg(metaTypes[i], null);
665 outputArgs.append( arg );
666 params.append( const_cast<void*>(outputArgs.at( outputArgs.count() - 1 ).constData()) );
671 if (data.object.isNull())
674 fail = data.object->qt_metacall(QMetaObject::InvokeMetaMethod,
675 data.slotIdx, params.data()) >= 0;
677 // do we create a reply? Only if the caller is waiting for a reply and one hasn't been sent
679 if (!msg.noReply() && !msg.wasRepliedTo()) {
682 QDBusMessage reply = QDBusMessage::methodReply(msg);
685 qDebug() << "Automatically sending reply:" << reply;
689 // generate internal error
690 QDBusMessage reply = QDBusMessage::error(msg, QDBusError(QDBusError::InternalError,
691 QLatin1String("Failed to deliver message")));
692 qWarning("Internal error: Failed to deliver message");
700 void QDBusConnectionPrivate::customEvent(QEvent *event)
702 // nothing else should be sending custom events at us
703 CallDeliveryEvent* call = static_cast<CallDeliveryEvent *>(event);
706 Q_ASSERT(call->conn == this);
711 QDBusConnectionPrivate::QDBusConnectionPrivate(QObject *parent)
712 : QObject(parent), ref(1), mode(InvalidMode), connection(0), server(0), busService(0)
714 extern bool qDBusInitThreads();
715 static const int msgType = registerMessageMetaType();
716 static const bool threads = qDBusInitThreads();
717 static const bool metatypes = QDBusMetaTypeId::innerInitialize();
723 dbus_error_init(&error);
728 QDBusConnectionPrivate::~QDBusConnectionPrivate()
730 if (dbus_error_is_set(&error))
731 dbus_error_free(&error);
734 rootNode.clear(); // free resources
735 qDeleteAll(cachedMetaObjects);
738 void QDBusConnectionPrivate::closeConnection()
740 QWriteLocker locker(&lock);
741 ConnectionMode oldMode = mode;
742 mode = InvalidMode; // prevent reentrancy
743 if (oldMode == ServerMode) {
745 dbus_server_disconnect(server);
746 dbus_server_unref(server);
749 } else if (oldMode == ClientMode) {
751 dbus_connection_close(connection);
752 // send the "close" message
753 while (dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS)
755 dbus_connection_unref(connection);
761 bool QDBusConnectionPrivate::handleError()
763 lastError = QDBusError(&error);
764 if (dbus_error_is_set(&error))
765 dbus_error_free(&error);
766 return lastError.isValid();
769 void QDBusConnectionPrivate::bindToApplication()
771 // Yay, now that we have an application we are in business
772 Q_ASSERT_X(QCoreApplication::instance(), "QDBusConnection",
773 "qDBusBindToApplication called without an application");
774 moveToThread(QCoreApplication::instance()->thread());
776 // Re-add all watchers
777 WatcherHash oldWatchers = watchers;
779 QHashIterator<int, QDBusConnectionPrivate::Watcher> it(oldWatchers);
780 while (it.hasNext()) {
782 if (!it.value().read && !it.value().write) {
783 qDBusAddWatch(it.value().watch, this);
785 watchers.insertMulti(it.key(), it.value());
789 // Re-add all timeouts
790 while (!pendingTimeouts.isEmpty())
791 qDBusAddTimeout(pendingTimeouts.takeFirst(), this);
794 void QDBusConnectionPrivate::timerEvent(QTimerEvent *e)
796 DBusTimeout *timeout = timeouts.value(e->timerId(), 0);
797 dbus_timeout_handle(timeout);
800 void QDBusConnectionPrivate::doDispatch()
802 if (mode == ClientMode)
803 while (dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS);
806 void QDBusConnectionPrivate::socketRead(int fd)
808 QHashIterator<int, QDBusConnectionPrivate::Watcher> it(watchers);
809 while (it.hasNext()) {
811 if (it.key() == fd && it.value().read && it.value().read->isEnabled()) {
812 if (!dbus_watch_handle(it.value().watch, DBUS_WATCH_READABLE))
813 qDebug("OUT OF MEM");
820 void QDBusConnectionPrivate::socketWrite(int fd)
822 QHashIterator<int, QDBusConnectionPrivate::Watcher> it(watchers);
823 while (it.hasNext()) {
825 if (it.key() == fd && it.value().write && it.value().write->isEnabled()) {
826 if (!dbus_watch_handle(it.value().watch, DBUS_WATCH_WRITABLE))
827 qDebug("OUT OF MEM");
832 void QDBusConnectionPrivate::objectDestroyed(QObject *obj)
834 QWriteLocker locker(&lock);
835 huntAndDestroy(obj, &rootNode);
837 SignalHookHash::iterator sit = signalHooks.begin();
838 while (sit != signalHooks.end()) {
839 if (static_cast<QObject *>(sit.value().obj) == obj)
840 sit = signalHooks.erase(sit);
845 obj->disconnect(this);
848 void QDBusConnectionPrivate::relaySignal(QObject *obj, const char *interface, const char *name,
849 const QVariantList &args)
851 QReadLocker locker(&lock);
852 QDBusMessage message = QDBusMessage::signal(QLatin1String("/"), QLatin1String(interface),
853 QLatin1String(name));
855 DBusMessage *msg = message.toDBusMessage();
857 qWarning("Could not emit signal %s.%s", interface, name);
861 //qDebug() << "Emitting signal" << message;
862 //qDebug() << "for paths:";
863 dbus_message_set_no_reply(msg, true); // the reply would not be delivered to anything
864 huntAndEmit(connection, msg, obj, &rootNode);
865 dbus_message_unref(msg);
868 int QDBusConnectionPrivate::registerMessageMetaType()
870 int tp = messageMetaType = qRegisterMetaType<QDBusMessage>("QDBusMessage");
874 int QDBusConnectionPrivate::findSlot(QObject* obj, const QByteArray &normalizedName,
877 int midx = obj->metaObject()->indexOfMethod(normalizedName);
879 qWarning("No such slot '%s' while connecting D-Bus", normalizedName.constData());
883 int inputCount = qDBusParametersForMethod(obj->metaObject()->method(midx), params);
884 if ( inputCount == -1 || inputCount + 1 != params.count() )
885 return -1; // failed to parse or invalid arguments or output arguments
890 bool QDBusConnectionPrivate::prepareHook(QDBusConnectionPrivate::SignalHook &hook, QString &key,
891 const QString &service, const QString &path,
892 const QString &interface, const QString &name,
893 QObject *receiver, const char *signal, int minMIdx,
896 QByteArray normalizedName = QMetaObject::normalizedSignature(signal + 1);
897 hook.midx = findSlot(receiver, normalizedName, hook.params);
898 if (hook.midx < minMIdx)
901 hook.sender = service;
905 // build the D-Bus signal name and signature
906 QString mname = name;
907 if (mname.isEmpty()) {
908 normalizedName.truncate(normalizedName.indexOf('('));
909 mname = QString::fromUtf8(normalizedName);
912 key.reserve(interface.length() + 1 + mname.length());
916 if (buildSignature) {
917 hook.signature.clear();
918 for (int i = 1; i < hook.params.count(); ++i)
919 if (hook.params.at(i) != messageMetaType)
920 hook.signature += QLatin1String( QDBusType::dbusSignature( QVariant::Type(hook.params.at(i)) ) );
923 return true; // connect to this signal
926 bool QDBusConnectionPrivate::activateInternalFilters(const ObjectTreeNode *node, const QDBusMessage &msg)
928 // object may be null
930 if (msg.interface().isEmpty() || msg.interface() == QLatin1String(DBUS_INTERFACE_INTROSPECTABLE)) {
931 if (msg.method() == QLatin1String("Introspect") && msg.signature().isEmpty())
932 qDBusIntrospectObject(node, msg);
933 if (msg.interface() == QLatin1String(DBUS_INTERFACE_INTROSPECTABLE))
937 if (node->obj && (msg.interface().isEmpty() ||
938 msg.interface() == QLatin1String(DBUS_INTERFACE_PROPERTIES))) {
939 if (msg.method() == QLatin1String("Get") && msg.signature() == QLatin1String("ss"))
940 qDBusPropertyGet(node, msg);
941 else if (msg.method() == QLatin1String("Set") && msg.signature() == QLatin1String("ssv"))
942 qDBusPropertySet(node, msg);
944 if (msg.interface() == QLatin1String(DBUS_INTERFACE_PROPERTIES))
951 bool QDBusConnectionPrivate::activateObject(const ObjectTreeNode *node, const QDBusMessage &msg)
953 // This is called by QDBusConnectionPrivate::handleObjectCall to place a call to a slot
956 // The call is routed through the adaptor sub-objects if we have any
958 // object may be null
960 QDBusAdaptorConnector *connector;
961 if (node->flags & QDBusConnection::ExportAdaptors &&
962 (connector = qDBusFindAdaptorConnector(node->obj))) {
963 int newflags = node->flags | QDBusConnection::ExportAllSlots;
965 if (msg.interface().isEmpty()) {
966 // place the call in all interfaces
967 // let the first one that handles it to work
968 foreach (const QDBusAdaptorConnector::AdaptorData &entry, connector->adaptors)
969 if (activateCall(entry.adaptor, newflags, msg))
972 // check if we have an interface matching the name that was asked:
973 QDBusAdaptorConnector::AdaptorMap::ConstIterator it;
974 it = qLowerBound(connector->adaptors.constBegin(), connector->adaptors.constEnd(),
976 if (it != connector->adaptors.end() && it->interface == msg.interface())
977 if (activateCall(it->adaptor, newflags, msg))
982 // no adaptors matched
983 // try our standard filters
984 if (activateInternalFilters(node, msg))
987 // try the object itself:
988 if (node->flags & QDBusConnection::ExportSlots && activateCall(node->obj, node->flags, msg))
992 qDebug("Call failed: no match for %s%s%s at %s",
993 qPrintable(msg.interface()), msg.interface().isEmpty() ? "" : ".",
994 qPrintable(msg.name()),
995 qPrintable(msg.path()));
1000 template<typename Func>
1001 static bool applyForObject(QDBusConnectionPrivate::ObjectTreeNode *root, const QString &fullpath,
1004 // walk the object tree
1005 QStringList path = fullpath.split(QLatin1Char('/'));
1006 if (path.last().isEmpty())
1007 path.removeLast(); // happens if path is "/"
1009 QDBusConnectionPrivate::ObjectTreeNode *node = root;
1011 // try our own tree first
1012 while (node && !(node->flags & QDBusConnection::ExportChildObjects) ) {
1013 if (i == path.count()) {
1019 QVector<QDBusConnectionPrivate::ObjectTreeNode::Data>::ConstIterator it =
1020 qLowerBound(node->children.constBegin(), node->children.constEnd(), path.at(i));
1021 if (it != node->children.constEnd() && it->name == path.at(i))
1030 // any object in the tree can tell us to switch to its own object tree:
1031 if (node && node->flags & QDBusConnection::ExportChildObjects) {
1032 QObject *obj = node->obj;
1035 if (i == path.count()) {
1036 // we're at the correct level
1037 QDBusConnectionPrivate::ObjectTreeNode fakenode(*node);
1043 const QObjectList children = obj->children();
1045 // find a child with the proper name
1047 foreach (QObject *child, children)
1048 if (child->objectName() == path.at(i)) {
1065 struct qdbus_activateObject
1067 QDBusConnectionPrivate *self;
1068 const QDBusMessage &msg;
1070 inline qdbus_activateObject(QDBusConnectionPrivate *s, const QDBusMessage &m)
1074 inline void operator()(QDBusConnectionPrivate::ObjectTreeNode *node)
1075 { returnVal = self->activateObject(node, msg); }
1078 bool QDBusConnectionPrivate::handleObjectCall(const QDBusMessage &msg)
1080 QReadLocker locker(&lock);
1082 qdbus_activateObject apply(this, msg);
1083 if (applyForObject(&rootNode, msg.path(), apply))
1084 return apply.returnVal;
1086 qDebug("Call failed: no object found at %s", qPrintable(msg.path()));
1090 bool QDBusConnectionPrivate::handleSignal(const QString &key, const QDBusMessage& msg)
1092 bool result = false;
1093 SignalHookHash::const_iterator it = signalHooks.find(key);
1094 //qDebug("looking for: %s", path.toLocal8Bit().constData());
1095 //qDebug() << signalHooks.keys();
1096 for ( ; it != signalHooks.constEnd() && it.key() == key; ++it) {
1097 const SignalHook &hook = it.value();
1098 if ( !hook.sender.isEmpty() && hook.sender != msg.sender() )
1100 if ( !hook.path.isEmpty() && hook.path != msg.path() )
1102 if ( !hook.signature.isEmpty() && hook.signature != msg.signature() )
1104 if ( hook.signature.isEmpty() && !hook.signature.isNull() && !msg.signature().isEmpty())
1108 result |= activateSignal(hook, msg);
1113 bool QDBusConnectionPrivate::handleSignal(const QDBusMessage& msg)
1115 QString key = msg.member();
1116 key.reserve(key.length() + 1 + msg.interface().length());
1118 key += msg.interface();
1120 QReadLocker locker(&lock);
1121 bool result = handleSignal(key, msg); // one try
1123 key.truncate(msg.member().length() + 1); // keep the ':'
1124 result |= handleSignal(key, msg); // second try
1128 static dbus_int32_t server_slot = -1;
1130 void QDBusConnectionPrivate::setServer(DBusServer *s)
1140 dbus_server_allocate_data_slot(&server_slot);
1141 if (server_slot < 0)
1144 dbus_server_set_watch_functions(server, qDBusAddWatch, qDBusRemoveWatch,
1145 qDBusToggleWatch, this, 0); // ### check return type?
1146 dbus_server_set_timeout_functions(server, qDBusAddTimeout, qDBusRemoveTimeout,
1147 qDBusToggleTimeout, this, 0);
1148 dbus_server_set_new_connection_function(server, qDBusNewConnection, this, 0);
1150 dbus_server_set_data(server, server_slot, this, 0);
1153 void QDBusConnectionPrivate::setConnection(DBusConnection *dbc)
1163 dbus_connection_set_exit_on_disconnect(connection, false);
1164 dbus_connection_set_watch_functions(connection, qDBusAddWatch, qDBusRemoveWatch,
1165 qDBusToggleWatch, this, 0);
1166 dbus_connection_set_timeout_functions(connection, qDBusAddTimeout, qDBusRemoveTimeout,
1167 qDBusToggleTimeout, this, 0);
1168 // dbus_bus_add_match(connection, "type='signal',interface='com.trolltech.dbus.Signal'", &error);
1169 // dbus_bus_add_match(connection, "type='signal'", &error);
1171 dbus_bus_add_match(connection, "type='signal'", &error);
1172 if (handleError()) {
1177 const char *service = dbus_bus_get_unique_name(connection);
1179 QVarLengthArray<char, 56> filter;
1180 filter.append("destination='", 13);
1181 filter.append(service, qstrlen(service));
1182 filter.append("\'\0", 2);
1184 dbus_bus_add_match(connection, filter.constData(), &error);
1185 if (handleError()) {
1190 qWarning("QDBusConnectionPrivate::SetConnection: Unable to get base service");
1193 #if USE_OUTSIDE_DISPATCH
1194 dbus_connection_add_filter_outside(connection, qDBusSignalFilter, qDBusSignalFilterOutside, this, 0);
1196 dbus_connection_add_filter(connection, qDBusSignalFilter, this, 0);
1199 //qDebug("base service: %s", service);
1201 // schedule a dispatch:
1202 QMetaObject::invokeMethod(this, "doDispatch", Qt::QueuedConnection);
1206 static void qDBusResultReceived(DBusPendingCall *pending, void *user_data)
1208 QDBusConnectionPrivate::messageResultReceived(pending, user_data);
1212 void QDBusConnectionPrivate::messageResultReceived(DBusPendingCall *pending, void *user_data)
1214 QDBusPendingCall *call = reinterpret_cast<QDBusPendingCall *>(user_data);
1215 QDBusConnectionPrivate *connection = const_cast<QDBusConnectionPrivate *>(call->connection);
1216 Q_ASSERT(call->pending == pending);
1218 if (!call->receiver.isNull() && call->methodIdx != -1) {
1219 DBusMessage *reply = dbus_pending_call_steal_reply(pending);
1221 // Deliver the return values of a remote function call.
1223 // There is only one connection and it is specified by idx
1224 // The slot must have the same parameter types that the message does
1225 // The slot may have less parameters than the message
1226 // The slot may optionally have one final parameter that is QDBusMessage
1227 // The slot receives read-only copies of the message (i.e., pass by value or by const-ref)
1229 QDBusMessage msg = QDBusMessage::fromDBusMessage(reply, QDBusConnection(connection->name));
1230 qDebug() << "got message: " << msg;
1231 CallDeliveryEvent *e = prepareReply(call->receiver, call->methodIdx, call->metaTypes, msg);
1233 connection->postCallDeliveryEvent(e);
1235 qDebug() << "Deliver failed!";
1237 dbus_pending_call_unref(pending);
1241 int QDBusConnectionPrivate::send(const QDBusMessage& message) const
1243 DBusMessage *msg = message.toDBusMessage();
1247 dbus_message_set_no_reply(msg, true); // the reply would not be delivered to anything
1249 qDebug() << "sending message:" << message;
1250 bool isOk = dbus_connection_send(connection, msg, 0);
1253 serial = dbus_message_get_serial(msg);
1255 dbus_message_unref(msg);
1259 QDBusMessage QDBusConnectionPrivate::sendWithReply(const QDBusMessage &message,
1262 if (!QCoreApplication::instance() || mode == QDBusConnection::NoUseEventLoop) {
1263 DBusMessage *msg = message.toDBusMessage();
1265 return QDBusMessage();
1267 qDebug() << "sending message:" << message;
1268 DBusMessage *reply = dbus_connection_send_with_reply_and_block(connection, msg,
1271 dbus_message_unref(msg);
1273 if (lastError.isValid())
1274 return QDBusMessage::fromError(lastError);
1276 QDBusMessage amsg = QDBusMessage::fromDBusMessage(reply, QDBusConnection(name));
1277 qDebug() << "got message:" << amsg;
1279 if (dbus_connection_get_dispatch_status(connection) == DBUS_DISPATCH_DATA_REMAINS)
1280 QMetaObject::invokeMethod(this, "doDispatch", Qt::QueuedConnection);
1282 } else { // use the event loop
1283 QDBusReplyWaiter waiter;
1284 if (sendWithReplyAsync(message, &waiter, SLOT(reply(const QDBusMessage&))) > 0) {
1285 // enter the event loop and wait for a reply
1286 waiter.exec(QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents);
1288 lastError = waiter.replyMsg; // set or clear error
1289 return waiter.replyMsg;
1292 return QDBusMessage();
1296 int QDBusConnectionPrivate::sendWithReplyAsync(const QDBusMessage &message, QObject *receiver,
1299 if (!receiver || !method || !*method)
1300 // would not be able to deliver a reply
1301 return send(message);
1304 QList<int> metaTypes;
1305 QByteArray normalizedName = QMetaObject::normalizedSignature(method + 1);
1306 slotIdx = findSlot(receiver, normalizedName, metaTypes);
1308 // would not be able to deliver a reply
1309 return send(message);
1311 DBusMessage *msg = message.toDBusMessage();
1315 qDebug() << "sending message:" << message;
1316 DBusPendingCall *pending = 0;
1317 if (dbus_connection_send_with_reply(connection, msg, &pending, message.timeout())) {
1318 if (slotIdx != -1) {
1319 QDBusPendingCall *pcall = new QDBusPendingCall;
1320 pcall->receiver = receiver;
1321 pcall->metaTypes = metaTypes;
1322 pcall->methodIdx = slotIdx;
1323 pcall->connection = this;
1324 pcall->pending = dbus_pending_call_ref(pending);
1325 dbus_pending_call_set_notify(pending, qDBusResultReceived, pcall, 0);
1327 dbus_pending_call_unref(pending);
1328 return dbus_message_get_serial(msg);
1334 void QDBusConnectionPrivate::connectSignal(const QString &key, const SignalHook &hook)
1336 signalHooks.insertMulti(key, hook);
1337 connect(hook.obj, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*)));
1340 void QDBusConnectionPrivate::registerObject(const ObjectTreeNode *node)
1342 connect(node->obj, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*)));
1344 if (node->flags & QDBusConnection::ExportAdaptors) {
1345 QDBusAdaptorConnector *connector = qDBusCreateAdaptorConnector(node->obj);
1347 // disconnect and reconnect to avoid duplicates
1348 connector->disconnect(SIGNAL(relaySignal(QObject*,const char*,const char*,QVariantList)),
1349 this, SLOT(relaySignal(QObject*,const char*,const char*,QVariantList)));
1350 connect(connector, SIGNAL(relaySignal(QObject*,const char*,const char*,QVariantList)),
1351 SLOT(relaySignal(QObject*,const char*,const char*,QVariantList)));
1355 void QDBusConnectionPrivate::connectRelay(const QString &service, const QString &path,
1356 const QString &interface,
1357 QDBusAbstractInterface *receiver,
1360 // this function is called by QDBusAbstractInterface when one of its signals is connected
1361 // we set up a relay from D-Bus into it
1364 if (!prepareHook(hook, key, service, path, interface, QString(), receiver, signal,
1365 QDBusAbstractInterface::staticMetaObject.methodCount(), true))
1366 return; // don't connect
1368 // add it to our list:
1369 QWriteLocker locker(&lock);
1370 SignalHookHash::ConstIterator it = signalHooks.find(key);
1371 SignalHookHash::ConstIterator end = signalHooks.end();
1372 for ( ; it != end && it.key() == key; ++it) {
1373 const SignalHook &entry = it.value();
1374 if (entry.sender == hook.sender &&
1375 entry.path == hook.path &&
1376 entry.signature == hook.signature &&
1377 entry.obj == hook.obj &&
1378 entry.midx == hook.midx)
1379 return; // already there, no need to re-add
1382 connectSignal(key, hook);
1385 void QDBusConnectionPrivate::disconnectRelay(const QString &service, const QString &path,
1386 const QString &interface,
1387 QDBusAbstractInterface *receiver,
1390 // this function is called by QDBusAbstractInterface when one of its signals is disconnected
1391 // we remove relay from D-Bus into it
1394 if (!prepareHook(hook, key, service, path, interface, QString(), receiver, signal,
1395 QDBusAbstractInterface::staticMetaObject.methodCount(), true))
1396 return; // don't connect
1398 // remove it from our list:
1399 QWriteLocker locker(&lock);
1400 SignalHookHash::Iterator it = signalHooks.find(key);
1401 SignalHookHash::Iterator end = signalHooks.end();
1402 for ( ; it != end && it.key() == key; ++it) {
1403 const SignalHook &entry = it.value();
1404 if (entry.sender == hook.sender &&
1405 entry.path == hook.path &&
1406 entry.signature == hook.signature &&
1407 entry.obj == hook.obj &&
1408 entry.midx == hook.midx) {
1410 signalHooks.erase(it);
1415 qWarning("QDBusConnectionPrivate::disconnectRelay called for a signal that was not found");
1418 QString QDBusConnectionPrivate::getNameOwner(const QString& name)
1420 if (QDBusUtil::isValidUniqueConnectionName(name))
1422 if (!connection || !QDBusUtil::isValidBusName(name))
1425 QDBusMessage msg = QDBusMessage::methodCall(QLatin1String(DBUS_SERVICE_DBUS),
1426 QLatin1String(DBUS_PATH_DBUS), QLatin1String(DBUS_INTERFACE_DBUS),
1427 QLatin1String("GetNameOwner"));
1429 QDBusMessage reply = sendWithReply(msg, QDBusConnection::NoUseEventLoop);
1430 if (!lastError.isValid() && reply.type() == QDBusMessage::ReplyMessage)
1431 return reply.first().toString();
1435 QDBusInterfacePrivate *
1436 QDBusConnectionPrivate::findInterface(const QString &service,
1437 const QString &path,
1438 const QString &interface)
1440 // check if it's there first -- FIXME: add binding mode
1441 QDBusMetaObject *mo = 0;
1442 QString owner = getNameOwner(service);
1443 if (connection && !owner.isEmpty() && QDBusUtil::isValidObjectPath(path) &&
1444 (interface.isEmpty() || QDBusUtil::isValidInterfaceName(interface)))
1445 // always call here with the unique connection name
1446 mo = findMetaObject(owner, path, interface);
1448 QDBusInterfacePrivate *p = new QDBusInterfacePrivate(QDBusConnection(name), this, owner, path, interface, mo);
1453 p->lastError = lastError;
1454 if (!lastError.isValid()) {
1455 // try to determine why we couldn't get the data
1457 p->lastError = QDBusError(QDBusError::Disconnected,
1458 QLatin1String("Not connected to D-Bus server"));
1459 else if (owner.isEmpty())
1460 p->lastError = QDBusError(QDBusError::ServiceUnknown,
1461 QString(QLatin1String("Service %1 is unknown")).arg(service));
1462 else if (!QDBusUtil::isValidObjectPath(path))
1463 p->lastError = QDBusError(QDBusError::InvalidArgs,
1464 QString(QLatin1String("Object path %1 is invalid")).arg(path));
1465 else if (!interface.isEmpty() && !QDBusUtil::isValidInterfaceName(interface))
1466 p->lastError = QDBusError(QDBusError::InvalidArgs,
1467 QString(QLatin1String("Interface %1 is invalid")).arg(interface));
1469 p->lastError = QDBusError(QDBusError::Other, QLatin1String("Unknown error"));
1476 struct qdbus_Introspect
1479 inline void operator()(QDBusConnectionPrivate::ObjectTreeNode *node)
1480 { xml = qDBusIntrospectObject(node); }
1484 QDBusConnectionPrivate::findMetaObject(const QString &service, const QString &path,
1485 const QString &interface)
1487 // service must be a unique connection name
1488 if (!interface.isEmpty()) {
1489 QReadLocker locker(&lock);
1490 QDBusMetaObject *mo = cachedMetaObjects.value(interface, 0);
1494 if (service == QString::fromUtf8(dbus_bus_get_unique_name(connection))) {
1495 // it's one of our own
1496 QWriteLocker locker(&lock);
1497 QDBusMetaObject *mo = 0;
1498 if (!interface.isEmpty())
1499 mo = cachedMetaObjects.value(interface, 0);
1501 // maybe it got created when we switched from read to write lock
1504 qdbus_Introspect apply;
1505 if (!applyForObject(&rootNode, path, apply)) {
1506 lastError = QDBusError(QDBusError::InvalidArgs,
1507 QString(QLatin1String("No object at %1")).arg(path));
1508 return 0; // no object at path
1511 // release the lock and return
1512 return QDBusMetaObject::createMetaObject(interface, apply.xml, cachedMetaObjects, lastError);
1515 // not local: introspect the target object:
1516 QDBusMessage msg = QDBusMessage::methodCall(service, path,
1517 QLatin1String(DBUS_INTERFACE_INTROSPECTABLE),
1518 QLatin1String("Introspect"));
1521 QDBusMessage reply = sendWithReply(msg, QDBusConnection::NoUseEventLoop);
1523 // it doesn't exist yet, we have to create it
1524 QWriteLocker locker(&lock);
1525 QDBusMetaObject *mo = 0;
1526 if (!interface.isEmpty())
1527 mo = cachedMetaObjects.value(interface, 0);
1529 // maybe it got created when we switched from read to write lock
1533 if (reply.type() == QDBusMessage::ReplyMessage)
1534 // fetch the XML description
1535 xml = reply.first().toString();
1538 if (reply.type() != QDBusMessage::ErrorMessage || lastError != QDBusError::UnknownMethod)
1542 // release the lock and return
1543 return QDBusMetaObject::createMetaObject(interface, xml, cachedMetaObjects, lastError);
1546 void QDBusReplyWaiter::reply(const QDBusMessage &msg)
1549 QTimer::singleShot(0, this, SLOT(quit()));
1552 #include "qdbusconnection_p.moc"