* qt/: Update to Subversion r548032.
[platform/upstream/dbus.git] / qt / src / qdbusintegrator.cpp
1 /* qdbusintegrator.cpp QDBusConnection private implementation
2  *
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>
6  *
7  * Licensed under the Academic Free License version 2.1
8  *
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.
13  *
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.
18  *
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.
22  *
23  */
24
25 #include <qcoreapplication.h>
26 #include <qcoreevent.h>
27 #include <qdebug.h>
28 #include <qmetaobject.h>
29 #include <qobject.h>
30 #include <qsocketnotifier.h>
31 #include <qstringlist.h>
32 #include <qtimer.h>
33
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"
42
43 #ifndef USE_OUTSIDE_DISPATCH
44 # define USE_OUTSIDE_DISPATCH    0
45 #endif
46
47 int QDBusConnectionPrivate::messageMetaType = 0;
48
49 typedef void (*QDBusSpyHook)(const QDBusMessage&);
50 typedef QVarLengthArray<QDBusSpyHook, 4> QDBusSpyHookList;
51 Q_GLOBAL_STATIC(QDBusSpyHookList, qDBusSpyHookList)
52
53 struct QDBusPendingCall
54 {
55     QPointer<QObject> receiver;
56     QList<int> metaTypes;
57     int methodIdx;
58     DBusPendingCall *pending;
59     const QDBusConnectionPrivate *connection;
60 };
61
62 class CallDeliveryEvent: public QEvent
63 {
64 public:
65     CallDeliveryEvent()
66         : QEvent(QEvent::User), object(0), flags(0), slotIdx(-1)
67         { }
68
69     const QDBusConnectionPrivate *conn;
70     QPointer<QObject> object;
71     QDBusMessage message;
72     QList<int> metaTypes;
73
74     int flags;
75     int slotIdx;
76 };
77
78 static dbus_bool_t qDBusAddTimeout(DBusTimeout *timeout, void *data)
79 {
80     Q_ASSERT(timeout);
81     Q_ASSERT(data);
82
83   //  qDebug("addTimeout %d", dbus_timeout_get_interval(timeout));
84
85     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
86
87     if (!dbus_timeout_get_enabled(timeout))
88         return true;
89
90     if (!QCoreApplication::instance()) {
91         d->pendingTimeouts.append(timeout);
92         return true;
93     }
94     int timerId = d->startTimer(dbus_timeout_get_interval(timeout));
95     if (!timerId)
96         return false;
97
98     d->timeouts[timerId] = timeout;
99     return true;
100 }
101
102 static void qDBusRemoveTimeout(DBusTimeout *timeout, void *data)
103 {
104     Q_ASSERT(timeout);
105     Q_ASSERT(data);
106
107   //  qDebug("removeTimeout");
108
109     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
110     d->pendingTimeouts.removeAll(timeout);
111
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);
117         } else {
118             ++it;
119         }
120     }
121 }
122
123 static void qDBusToggleTimeout(DBusTimeout *timeout, void *data)
124 {
125     Q_ASSERT(timeout);
126     Q_ASSERT(data);
127
128     //qDebug("ToggleTimeout");
129
130     qDBusRemoveTimeout(timeout, data);
131     qDBusAddTimeout(timeout, data);
132 }
133
134 static dbus_bool_t qDBusAddWatch(DBusWatch *watch, void *data)
135 {
136     Q_ASSERT(watch);
137     Q_ASSERT(data);
138
139     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
140
141     int flags = dbus_watch_get_flags(watch);
142     int fd = dbus_watch_get_fd(watch);
143
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)));
152         }
153     }
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)));
161         }
162     }
163     d->watchers.insertMulti(fd, watcher);
164
165     return true;
166 }
167
168 static void qDBusRemoveWatch(DBusWatch *watch, void *data)
169 {
170     Q_ASSERT(watch);
171     Q_ASSERT(data);
172
173     //qDebug("remove watch");
174
175     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
176     int fd = dbus_watch_get_fd(watch);
177
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);
184             return;
185         }
186         ++i;
187     }
188 }
189
190 static void qDBusToggleWatch(DBusWatch *watch, void *data)
191 {
192     Q_ASSERT(watch);
193     Q_ASSERT(data);
194
195     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
196     int fd = dbus_watch_get_fd(watch);
197
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);
203
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);
205
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);
210             return;
211         }
212         ++i;
213     }
214 }
215
216 static void qDBusNewConnection(DBusServer *server, DBusConnection *c, void *data)
217 {
218     Q_ASSERT(data); Q_ASSERT(server); Q_ASSERT(c);
219     Q_UNUSED(data); Q_UNUSED(server); Q_UNUSED(c);
220
221     qDebug("SERVER: GOT A NEW CONNECTION"); // TODO
222 }
223
224 extern QDBUS_EXPORT void qDBusAddSpyHook(QDBusSpyHook);
225 void qDBusAddSpyHook(QDBusSpyHook hook)
226 {
227     qDBusSpyHookList()->append(hook);
228 }
229
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)
234 {
235     Q_ASSERT(data);
236     Q_UNUSED(connection);
237     Q_UNUSED(message);
238
239     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
240     if (d->mode == QDBusConnectionPrivate::InvalidMode)
241         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; // internal error, actually
242
243     CallDeliveryEvent *e = d->postedCallDeliveryEvent();
244
245     d->deliverCall(*e);
246     delete e;
247
248     return DBUS_HANDLER_RESULT_HANDLED;
249 }
250 #else
251 # define HANDLED     DBUS_HANDLER_RESULT_HANDLED
252 #endif
253
254 extern "C" {
255 static DBusHandlerResult
256 qDBusSignalFilter(DBusConnection *connection, DBusMessage *message, void *data)
257 {
258     return QDBusConnectionPrivate::messageFilter(connection, message, data);
259 }
260 }
261
262 DBusHandlerResult QDBusConnectionPrivate::messageFilter(DBusConnection *connection,
263                                                         DBusMessage *message, void *data)
264 {
265     Q_ASSERT(data);
266     Q_UNUSED(connection);
267
268     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
269     if (d->mode == QDBusConnectionPrivate::InvalidMode)
270         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
271
272     QDBusMessage amsg = QDBusMessage::fromDBusMessage(message, QDBusConnection(d->name));
273     qDebug() << "got message:" << amsg;
274
275     const QDBusSpyHookList *list = qDBusSpyHookList();
276     for (int i = 0; i < list->size(); ++i) {
277         qDebug() << "calling the message spy hook";
278         (*(*list)[i])(amsg);
279     }
280
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);
287     }
288
289     return handled ? HANDLED :
290         DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
291 }
292
293 static void huntAndDestroy(QObject *needle, QDBusConnectionPrivate::ObjectTreeNode *haystack)
294 {
295     foreach (const QDBusConnectionPrivate::ObjectTreeNode::Data &entry, haystack->children)
296         huntAndDestroy(needle, entry.node);
297
298     if (needle == haystack->obj) {
299         haystack->obj = 0;
300         haystack->flags = 0;
301     }
302 }
303
304 static void huntAndEmit(DBusConnection *connection, DBusMessage *msg,
305                         QObject *needle, QDBusConnectionPrivate::ObjectTreeNode *haystack,
306                         const QString &path = QString())
307 {
308     foreach (const QDBusConnectionPrivate::ObjectTreeNode::Data &entry, haystack->children)
309         huntAndEmit(connection, msg, needle, entry.node, path + QLatin1String("/") + entry.name);
310
311     if (needle == haystack->obj && haystack->flags & QDBusConnection::ExportAdaptors) {
312         QByteArray p = path.toLatin1();
313         if (p.isEmpty())
314             p = "/";
315         //qDebug() << p;
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);
320     }
321 }
322
323 static bool typesMatch(int metaId, int variantType)
324 {
325     if (metaId == int(variantType))
326         return true;
327
328     if (variantType == QVariant::Int && metaId == QMetaType::Short)
329         return true;
330
331     if (variantType == QVariant::UInt && (metaId == QMetaType::UShort ||
332                                           metaId == QMetaType::UChar))
333         return true;
334
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())
344             return true;
345     }
346
347     return false;               // no match
348 }
349
350 static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags,
351                     const QDBusTypeList &types, QList<int>& metaTypes)
352 {
353     // find the first slot
354     const QMetaObject *super = mo;
355     while (super != &QObject::staticMetaObject &&
356            super != &QDBusAbstractAdaptor::staticMetaObject)
357         super = super->superClass();
358
359     int attributeMask = (flags & QDBusConnection::ExportAllSlots) ?
360                         0 : QMetaMethod::Scriptable;
361
362     for (int idx = super->methodCount() ; idx <= mo->methodCount(); ++idx) {
363         QMetaMethod mm = mo->method(idx);
364
365         // check access:
366         if (mm.access() != QMetaMethod::Public)
367             continue;
368
369         // check type:
370         // unnecessary, since slots are never public:
371         //if (mm.methodType() != QMetaMethod::Slot)
372         //    continue;
373
374         // check name:
375         QByteArray sig = QMetaObject::normalizedSignature(mm.signature());
376         int paren = sig.indexOf('(');
377         if (paren != name.length() || !sig.startsWith( name ))
378             continue;
379
380         int returnType = qDBusNameToTypeId(mm.typeName());
381         bool isAsync = qDBusCheckAsyncTag(mm.tag());
382
383         // consistency check:
384         if (isAsync && returnType != QMetaType::Void)
385             continue;
386
387         int inputCount = qDBusParametersForMethod(mm, metaTypes);
388         if (inputCount == -1)
389             continue;           // problem parsing
390
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
396             hasMessage = true;
397             --inputCount;
398         }
399
400         // try to match the parameters
401         if (inputCount != types.count())
402             continue;       // not enough parameters
403
404         bool matches = true;
405         int i;
406         for (i = 0; i < types.count(); ++i)
407             if ( !typesMatch(metaTypes.at(i + 1), types.at(i).qvariantType()) ) {
408                 matches = false;
409                 break;
410             }
411
412         if (!matches)
413             continue;           // we didn't match them all
414
415         // consistency check:
416         if (isAsync && metaTypes.count() > i + 1)
417             continue;
418
419         if (hasMessage && (mm.attributes() & attributeMask) != attributeMask)
420             continue;           // not exported
421
422         // if we got here, this slot matched
423         return idx;
424     }
425
426     // no slot matched
427     return -1;
428 }
429
430 static CallDeliveryEvent* prepareReply(QObject *object, int idx, const QList<int> &metaTypes,
431                                        const QDBusMessage &msg)
432 {
433     Q_ASSERT(object);
434
435     int n = metaTypes.count() - 1;
436     if (metaTypes[n] == QDBusConnectionPrivate::messageMetaType)
437         --n;
438
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
443
444     // we can deliver
445     // prepare for the call
446     CallDeliveryEvent *data = new CallDeliveryEvent;
447     data->object = object;
448     data->flags = 0;
449     data->message = msg;
450     data->metaTypes = metaTypes;
451     data->slotIdx = idx;
452
453     return data;
454 }
455
456 bool QDBusConnectionPrivate::activateSignal(const QDBusConnectionPrivate::SignalHook& hook,
457                                             const QDBusMessage &msg)
458 {
459     // This is called by QDBusConnectionPrivate::handleSignal to deliver a signal
460     // that was received from D-Bus
461     //
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);
467     if (call) {
468         postCallDeliveryEvent(call);
469         return true;
470     }
471     return false;
472 }
473
474 bool QDBusConnectionPrivate::activateCall(QObject* object, int flags,
475                                           const QDBusMessage &msg)
476 {
477     // This is called by QDBusConnectionPrivate::handleObjectCall to place a call
478     // to a slot on the object.
479     //
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.
486     //
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.
490     //
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.
495     //
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
499     // positions.
500
501     if (!object)
502         return false;
503
504     QList<int> metaTypes;
505     int idx;
506
507     {
508         const QMetaObject *mo = object->metaObject();
509         QDBusTypeList typeList(msg.signature().toUtf8());
510         QByteArray name = msg.name().toUtf8();
511
512         // find a slot that matches according to the rules above
513         idx = ::findSlot(mo, name, flags, typeList, metaTypes);
514         if (idx == -1) {
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)
518                 return false;
519         }
520     }
521
522     // found the slot to be called
523     // prepare for the call:
524     CallDeliveryEvent *call = new CallDeliveryEvent;
525
526     // parameters:
527     call->object = object;
528     call->flags = flags;
529     call->message = msg;
530
531     // save our state:
532     call->metaTypes = metaTypes;
533     call->slotIdx = idx;
534
535     postCallDeliveryEvent(call);
536
537     // ready
538     return true;
539 }
540
541 void QDBusConnectionPrivate::postCallDeliveryEvent(CallDeliveryEvent *data)
542 {
543     Q_ASSERT(data);
544     data->conn = this;    
545 #if USE_OUTSIDE_DISPATCH
546     callDeliveryMutex.lock();
547     callDeliveryState = data;
548 #else
549     QCoreApplication::postEvent( this, data );
550 #endif
551 }
552
553 CallDeliveryEvent *QDBusConnectionPrivate::postedCallDeliveryEvent()
554 {
555     CallDeliveryEvent *e = callDeliveryState;
556     Q_ASSERT(e && e->conn == this);
557
558     // release it:
559     callDeliveryState = 0;
560     callDeliveryMutex.unlock();
561
562     return e;
563 }
564
565 void QDBusConnectionPrivate::deliverCall(const CallDeliveryEvent& data) const
566 {
567     // resume state:
568     const QList<int>& metaTypes = data.metaTypes;
569     const QDBusMessage& msg = data.message;
570
571     QVarLengthArray<void *, 10> params;
572     params.reserve(metaTypes.count());
573
574     QVariantList auxParameters;
575     // let's create the parameter list
576
577     // first one is the return type -- add it below
578     params.append(0);
579
580     // add the input parameters
581     int i;
582     for (i = 1; i <= msg.count(); ++i) {
583         int id = metaTypes[i];
584         if (id == QDBusConnectionPrivate::messageMetaType)
585             break;
586
587         if (id == int(msg.at(i - 1).userType()))
588             // no conversion needed
589             params.append(const_cast<void *>( msg.at(i - 1).constData() ));
590         else {
591             // convert to what the function expects
592             auxParameters.append(QVariant());
593             
594             const QVariant &in = msg.at(i - 1);
595             QVariant &out = auxParameters[auxParameters.count() - 1];
596
597             bool error = false;
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));
617                 else
618                     error = true;
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);
626                 } else {
627                     error = true;
628                 }
629             } else if (in.type() == QVariant::Int) {
630                 if (id == QMetaType::Short) {
631                     short s = in.toInt();
632                     out = qVariantFromValue(s);
633                 } else {
634                     error = true;
635                 }
636             } else {
637                 error = true;
638             }
639
640             if (error)
641                 qFatal("Internal error: got invalid meta type %d when trying to convert to meta type %d",
642                        in.userType(), id);
643
644             params.append( const_cast<void *>(out.constData()) );
645         }
646     }
647
648     bool takesMessage = false;
649     if (metaTypes.count() > i && metaTypes[i] == QDBusConnectionPrivate::messageMetaType) {
650         params.append(const_cast<void*>(static_cast<const void*>(&msg)));
651         takesMessage = true;
652         ++i;
653     }
654
655     // output arguments
656     QVariantList outputArgs;
657     void *null = 0;
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());
662     }
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()) );
667     }
668
669     // make call:
670     bool fail;
671     if (data.object.isNull())
672         fail = true;
673     else
674         fail = data.object->qt_metacall(QMetaObject::InvokeMetaMethod,
675                                         data.slotIdx, params.data()) >= 0;
676
677     // do we create a reply? Only if the caller is waiting for a reply and one hasn't been sent
678     // yet.
679     if (!msg.noReply() && !msg.wasRepliedTo()) {
680         if (!fail) {
681             // normal reply
682             QDBusMessage reply = QDBusMessage::methodReply(msg);
683             reply += outputArgs;
684
685             qDebug() << "Automatically sending reply:" << reply;
686             send(reply);
687         }
688         else {
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");
693             send(reply);
694         }
695     }
696
697     return;
698 }
699
700 void QDBusConnectionPrivate::customEvent(QEvent *event)
701 {
702     // nothing else should be sending custom events at us
703     CallDeliveryEvent* call = static_cast<CallDeliveryEvent *>(event);
704
705     // self check:
706     Q_ASSERT(call->conn == this);
707
708     deliverCall(*call);
709 }
710
711 QDBusConnectionPrivate::QDBusConnectionPrivate(QObject *parent)
712     : QObject(parent), ref(1), mode(InvalidMode), connection(0), server(0), busService(0)
713 {
714     extern bool qDBusInitThreads();
715     static const int msgType = registerMessageMetaType();
716     static const bool threads = qDBusInitThreads();
717     static const bool metatypes = QDBusMetaTypeId::innerInitialize();
718
719     Q_UNUSED(msgType);
720     Q_UNUSED(threads);
721     Q_UNUSED(metatypes);
722
723     dbus_error_init(&error);
724
725     rootNode.flags = 0;
726 }
727
728 QDBusConnectionPrivate::~QDBusConnectionPrivate()
729 {
730     if (dbus_error_is_set(&error))
731         dbus_error_free(&error);
732
733     closeConnection();
734     rootNode.clear();        // free resources
735     qDeleteAll(cachedMetaObjects);
736 }
737
738 void QDBusConnectionPrivate::closeConnection()
739 {
740     QWriteLocker locker(&lock);
741     ConnectionMode oldMode = mode;
742     mode = InvalidMode; // prevent reentrancy
743     if (oldMode == ServerMode) {
744         if (server) {
745             dbus_server_disconnect(server);
746             dbus_server_unref(server);
747             server = 0;
748         }
749     } else if (oldMode == ClientMode) {
750         if (connection) {
751             dbus_connection_close(connection);
752             // send the "close" message
753             while (dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS)
754                 ;
755             dbus_connection_unref(connection);
756             connection = 0;
757         }
758     }
759 }
760
761 bool QDBusConnectionPrivate::handleError()
762 {
763     lastError = QDBusError(&error);
764     if (dbus_error_is_set(&error))
765         dbus_error_free(&error);
766     return lastError.isValid();
767 }
768
769 void QDBusConnectionPrivate::bindToApplication()
770 {
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());
775     
776     // Re-add all watchers
777     WatcherHash oldWatchers = watchers;
778     watchers.clear();
779     QHashIterator<int, QDBusConnectionPrivate::Watcher> it(oldWatchers);
780     while (it.hasNext()) {
781         it.next();
782         if (!it.value().read && !it.value().write) {
783             qDBusAddWatch(it.value().watch, this);
784         } else {
785             watchers.insertMulti(it.key(), it.value());
786         }
787     }
788
789     // Re-add all timeouts
790     while (!pendingTimeouts.isEmpty())
791        qDBusAddTimeout(pendingTimeouts.takeFirst(), this);
792 }
793
794 void QDBusConnectionPrivate::timerEvent(QTimerEvent *e)
795 {
796     DBusTimeout *timeout = timeouts.value(e->timerId(), 0);
797     dbus_timeout_handle(timeout);
798 }
799
800 void QDBusConnectionPrivate::doDispatch()
801 {
802     if (mode == ClientMode)
803         while (dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS);
804 }
805
806 void QDBusConnectionPrivate::socketRead(int fd)
807 {
808     QHashIterator<int, QDBusConnectionPrivate::Watcher> it(watchers);
809     while (it.hasNext()) {
810         it.next();
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");
814         }
815     }
816
817     doDispatch();
818 }
819
820 void QDBusConnectionPrivate::socketWrite(int fd)
821 {
822     QHashIterator<int, QDBusConnectionPrivate::Watcher> it(watchers);
823     while (it.hasNext()) {
824         it.next();
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");
828         }
829     }
830 }
831
832 void QDBusConnectionPrivate::objectDestroyed(QObject *obj)
833 {
834     QWriteLocker locker(&lock);
835     huntAndDestroy(obj, &rootNode);
836
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);
841         else
842             ++sit;
843     }
844
845     obj->disconnect(this);
846 }
847
848 void QDBusConnectionPrivate::relaySignal(QObject *obj, const char *interface, const char *name,
849                                          const QVariantList &args)
850 {
851     QReadLocker locker(&lock);
852     QDBusMessage message = QDBusMessage::signal(QLatin1String("/"), QLatin1String(interface),
853                                                 QLatin1String(name));
854     message += args;
855     DBusMessage *msg = message.toDBusMessage();
856     if (!msg) {
857         qWarning("Could not emit signal %s.%s", interface, name);
858         return;
859     }
860
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);
866 }
867
868 int QDBusConnectionPrivate::registerMessageMetaType()
869 {
870     int tp = messageMetaType = qRegisterMetaType<QDBusMessage>("QDBusMessage");
871     return tp;
872 }
873
874 int QDBusConnectionPrivate::findSlot(QObject* obj, const QByteArray &normalizedName,
875                                      QList<int> &params)
876 {
877     int midx = obj->metaObject()->indexOfMethod(normalizedName);
878     if (midx == -1) {
879         qWarning("No such slot '%s' while connecting D-Bus", normalizedName.constData());
880         return -1;
881     }
882
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
886
887     return midx;
888 }
889
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,
894                                          bool buildSignature)
895 {
896     QByteArray normalizedName = QMetaObject::normalizedSignature(signal + 1);
897     hook.midx = findSlot(receiver, normalizedName, hook.params);
898     if (hook.midx < minMIdx)
899         return false;
900
901     hook.sender = service;
902     hook.path = path;
903     hook.obj = receiver;
904
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);
910     }
911     key = mname;
912     key.reserve(interface.length() + 1 + mname.length());
913     key += ':';
914     key += interface;
915
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)) ) );
921     }
922     
923     return true;                // connect to this signal
924 }
925
926 bool QDBusConnectionPrivate::activateInternalFilters(const ObjectTreeNode *node, const QDBusMessage &msg)
927 {
928     // object may be null
929
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))
934             return true;
935     }
936
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);
943
944         if (msg.interface() == QLatin1String(DBUS_INTERFACE_PROPERTIES))
945             return true;
946     }
947
948     return false;
949 }
950
951 bool QDBusConnectionPrivate::activateObject(const ObjectTreeNode *node, const QDBusMessage &msg)
952 {
953     // This is called by QDBusConnectionPrivate::handleObjectCall to place a call to a slot
954     // on the object.
955     //
956     // The call is routed through the adaptor sub-objects if we have any
957
958     // object may be null
959
960     QDBusAdaptorConnector *connector;
961     if (node->flags & QDBusConnection::ExportAdaptors &&
962         (connector = qDBusFindAdaptorConnector(node->obj))) {
963         int newflags = node->flags | QDBusConnection::ExportAllSlots;
964
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))
970                     return true;
971         } else {
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(),
975                              msg.interface());
976             if (it != connector->adaptors.end() && it->interface == msg.interface())
977                 if (activateCall(it->adaptor, newflags, msg))
978                 return true;
979         }
980     }
981
982     // no adaptors matched
983     // try our standard filters
984     if (activateInternalFilters(node, msg))
985         return true;
986
987     // try the object itself:
988     if (node->flags & QDBusConnection::ExportSlots && activateCall(node->obj, node->flags, msg))
989         return true;
990 #if 0
991     // nothing matched
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()));
996 #endif
997     return false;
998 }
999
1000 template<typename Func>
1001 static bool applyForObject(QDBusConnectionPrivate::ObjectTreeNode *root, const QString &fullpath,
1002                            Func& functor)
1003 {
1004     // walk the object tree
1005     QStringList path = fullpath.split(QLatin1Char('/'));
1006     if (path.last().isEmpty())
1007         path.removeLast();      // happens if path is "/"
1008     int i = 1;
1009     QDBusConnectionPrivate::ObjectTreeNode *node = root;
1010
1011     // try our own tree first
1012     while (node && !(node->flags & QDBusConnection::ExportChildObjects) ) {
1013         if (i == path.count()) {
1014             // found our object
1015             functor(node);
1016             return true;
1017         }
1018
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))
1022             // match
1023             node = it->node;
1024         else
1025             node = 0;
1026
1027         ++i;
1028     }
1029
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;
1033
1034         while (obj) {
1035             if (i == path.count()) {
1036                 // we're at the correct level
1037                 QDBusConnectionPrivate::ObjectTreeNode fakenode(*node);
1038                 fakenode.obj = obj;
1039                 functor(&fakenode);
1040                 return true;
1041             }
1042
1043             const QObjectList children = obj->children();
1044
1045             // find a child with the proper name
1046             QObject *next = 0;
1047             foreach (QObject *child, children)
1048                 if (child->objectName() == path.at(i)) {
1049                     next = child;
1050                     break;
1051                 }
1052
1053             if (!next)
1054                 break;
1055
1056             ++i;
1057             obj = next;
1058         }
1059     }
1060
1061     // object not found
1062     return false;
1063 }
1064
1065 struct qdbus_activateObject
1066 {
1067     QDBusConnectionPrivate *self;
1068     const QDBusMessage &msg;
1069     bool returnVal;
1070     inline qdbus_activateObject(QDBusConnectionPrivate *s, const QDBusMessage &m)
1071         : self(s), msg(m)
1072     { }
1073
1074     inline void operator()(QDBusConnectionPrivate::ObjectTreeNode *node)
1075     { returnVal = self->activateObject(node, msg); }
1076 };
1077
1078 bool QDBusConnectionPrivate::handleObjectCall(const QDBusMessage &msg)
1079 {
1080     QReadLocker locker(&lock);
1081
1082     qdbus_activateObject apply(this, msg);
1083     if (applyForObject(&rootNode, msg.path(), apply))
1084         return apply.returnVal;
1085
1086     qDebug("Call failed: no object found at %s", qPrintable(msg.path()));
1087     return false;
1088 }
1089
1090 bool QDBusConnectionPrivate::handleSignal(const QString &key, const QDBusMessage& msg)
1091 {
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() )
1099             continue;
1100         if ( !hook.path.isEmpty() && hook.path != msg.path() )
1101             continue;
1102         if ( !hook.signature.isEmpty() && hook.signature != msg.signature() )
1103             continue;
1104         if ( hook.signature.isEmpty() && !hook.signature.isNull() && !msg.signature().isEmpty())
1105             continue;
1106
1107         // yes, |=
1108         result |= activateSignal(hook, msg);
1109     }
1110     return result;
1111 }
1112
1113 bool QDBusConnectionPrivate::handleSignal(const QDBusMessage& msg)
1114 {
1115     QString key = msg.member();
1116     key.reserve(key.length() + 1 + msg.interface().length());
1117     key += ':';
1118     key += msg.interface();
1119
1120     QReadLocker locker(&lock);
1121     bool result = handleSignal(key, msg);    // one try
1122
1123     key.truncate(msg.member().length() + 1); // keep the ':'
1124     result |= handleSignal(key, msg);        // second try
1125     return result;
1126 }
1127
1128 static dbus_int32_t server_slot = -1;
1129
1130 void QDBusConnectionPrivate::setServer(DBusServer *s)
1131 {
1132     if (!server) {
1133         handleError();
1134         return;
1135     }
1136
1137     server = s;
1138     mode = ServerMode;
1139
1140     dbus_server_allocate_data_slot(&server_slot);
1141     if (server_slot < 0)
1142         return;
1143
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);
1149
1150     dbus_server_set_data(server, server_slot, this, 0);
1151 }
1152
1153 void QDBusConnectionPrivate::setConnection(DBusConnection *dbc)
1154 {
1155     if (!dbc) {
1156         handleError();
1157         return;
1158     }
1159
1160     connection = dbc;
1161     mode = ClientMode;
1162
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);
1170
1171     dbus_bus_add_match(connection, "type='signal'", &error);
1172     if (handleError()) {
1173         closeConnection();
1174         return;
1175     }
1176
1177     const char *service = dbus_bus_get_unique_name(connection);
1178     if (service) {
1179         QVarLengthArray<char, 56> filter;
1180         filter.append("destination='", 13);
1181         filter.append(service, qstrlen(service));
1182         filter.append("\'\0", 2);
1183
1184         dbus_bus_add_match(connection, filter.constData(), &error);
1185         if (handleError()) {
1186             closeConnection();
1187             return;
1188         }
1189     } else {
1190         qWarning("QDBusConnectionPrivate::SetConnection: Unable to get base service");
1191     }
1192
1193 #if USE_OUTSIDE_DISPATCH
1194     dbus_connection_add_filter_outside(connection, qDBusSignalFilter, qDBusSignalFilterOutside, this, 0);
1195 #else
1196     dbus_connection_add_filter(connection, qDBusSignalFilter, this, 0);
1197 #endif
1198
1199     //qDebug("base service: %s", service);
1200
1201     // schedule a dispatch:
1202     QMetaObject::invokeMethod(this, "doDispatch", Qt::QueuedConnection);
1203 }
1204
1205 extern "C"{
1206 static void qDBusResultReceived(DBusPendingCall *pending, void *user_data)
1207 {
1208     QDBusConnectionPrivate::messageResultReceived(pending, user_data);
1209 }
1210 }
1211
1212 void QDBusConnectionPrivate::messageResultReceived(DBusPendingCall *pending, void *user_data)
1213 {
1214     QDBusPendingCall *call = reinterpret_cast<QDBusPendingCall *>(user_data);
1215     QDBusConnectionPrivate *connection = const_cast<QDBusConnectionPrivate *>(call->connection);
1216     Q_ASSERT(call->pending == pending);
1217
1218     if (!call->receiver.isNull() && call->methodIdx != -1) {
1219         DBusMessage *reply = dbus_pending_call_steal_reply(pending);
1220
1221         // Deliver the return values of a remote function call.
1222         //
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)
1228
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);
1232         if (e)
1233             connection->postCallDeliveryEvent(e);
1234         else
1235             qDebug() << "Deliver failed!";
1236     }
1237     dbus_pending_call_unref(pending);
1238     delete call;
1239 }
1240
1241 int QDBusConnectionPrivate::send(const QDBusMessage& message) const
1242 {
1243     DBusMessage *msg = message.toDBusMessage();
1244     if (!msg)
1245         return 0;
1246
1247     dbus_message_set_no_reply(msg, true); // the reply would not be delivered to anything
1248
1249     qDebug() << "sending message:" << message;
1250     bool isOk = dbus_connection_send(connection, msg, 0);
1251     int serial = 0;
1252     if (isOk)
1253         serial = dbus_message_get_serial(msg);
1254
1255     dbus_message_unref(msg);
1256     return serial;
1257 }
1258
1259 QDBusMessage QDBusConnectionPrivate::sendWithReply(const QDBusMessage &message,
1260                                                    int mode)
1261 {
1262     if (!QCoreApplication::instance() || mode == QDBusConnection::NoUseEventLoop) {
1263         DBusMessage *msg = message.toDBusMessage();
1264         if (!msg)
1265             return QDBusMessage();
1266
1267         qDebug() << "sending message:" << message;
1268         DBusMessage *reply = dbus_connection_send_with_reply_and_block(connection, msg,
1269                                                                        -1, &error);
1270         handleError();
1271         dbus_message_unref(msg);
1272
1273         if (lastError.isValid())
1274             return QDBusMessage::fromError(lastError);
1275
1276         QDBusMessage amsg = QDBusMessage::fromDBusMessage(reply, QDBusConnection(name));
1277         qDebug() << "got message:" << amsg;
1278
1279         if (dbus_connection_get_dispatch_status(connection) == DBUS_DISPATCH_DATA_REMAINS)
1280             QMetaObject::invokeMethod(this, "doDispatch", Qt::QueuedConnection);
1281         return amsg;
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);
1287
1288             lastError = waiter.replyMsg; // set or clear error
1289             return waiter.replyMsg;
1290         }
1291
1292         return QDBusMessage();
1293     }
1294 }    
1295
1296 int QDBusConnectionPrivate::sendWithReplyAsync(const QDBusMessage &message, QObject *receiver,
1297                                                const char *method)
1298 {
1299     if (!receiver || !method || !*method)
1300         // would not be able to deliver a reply
1301         return send(message);
1302
1303     int slotIdx = -1;
1304     QList<int> metaTypes;
1305     QByteArray normalizedName = QMetaObject::normalizedSignature(method + 1);
1306     slotIdx = findSlot(receiver, normalizedName, metaTypes);
1307     if (slotIdx == -1)
1308         // would not be able to deliver a reply
1309         return send(message);
1310
1311     DBusMessage *msg = message.toDBusMessage();
1312     if (!msg)
1313         return 0;
1314
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);
1326         }
1327         dbus_pending_call_unref(pending);
1328         return dbus_message_get_serial(msg);
1329     }
1330
1331     return 0;
1332 }
1333
1334 void QDBusConnectionPrivate::connectSignal(const QString &key, const SignalHook &hook)
1335 {
1336     signalHooks.insertMulti(key, hook);
1337     connect(hook.obj, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*)));
1338 }
1339
1340 void QDBusConnectionPrivate::registerObject(const ObjectTreeNode *node)
1341 {
1342     connect(node->obj, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*)));
1343
1344     if (node->flags & QDBusConnection::ExportAdaptors) {
1345         QDBusAdaptorConnector *connector = qDBusCreateAdaptorConnector(node->obj);
1346
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)));
1352     }
1353 }
1354
1355 void QDBusConnectionPrivate::connectRelay(const QString &service, const QString &path,
1356                                           const QString &interface,
1357                                           QDBusAbstractInterface *receiver,
1358                                           const char *signal)
1359 {
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
1362     SignalHook hook;
1363     QString key;
1364     if (!prepareHook(hook, key, service, path, interface, QString(), receiver, signal,
1365                      QDBusAbstractInterface::staticMetaObject.methodCount(), true))
1366         return;                 // don't connect
1367
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
1380     }
1381
1382     connectSignal(key, hook);
1383 }
1384
1385 void QDBusConnectionPrivate::disconnectRelay(const QString &service, const QString &path,
1386                                              const QString &interface,
1387                                              QDBusAbstractInterface *receiver,
1388                                              const char *signal)
1389 {
1390     // this function is called by QDBusAbstractInterface when one of its signals is disconnected
1391     // we remove relay from D-Bus into it
1392     SignalHook hook;
1393     QString key;
1394     if (!prepareHook(hook, key, service, path, interface, QString(), receiver, signal,
1395                      QDBusAbstractInterface::staticMetaObject.methodCount(), true))
1396         return;                 // don't connect
1397
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) {
1409             // found it
1410             signalHooks.erase(it);
1411             return;
1412         }
1413     }
1414
1415     qWarning("QDBusConnectionPrivate::disconnectRelay called for a signal that was not found");
1416 }
1417
1418 QString QDBusConnectionPrivate::getNameOwner(const QString& name)
1419 {
1420     if (QDBusUtil::isValidUniqueConnectionName(name))
1421         return name;
1422     if (!connection || !QDBusUtil::isValidBusName(name))
1423         return QString();
1424
1425     QDBusMessage msg = QDBusMessage::methodCall(QLatin1String(DBUS_SERVICE_DBUS),
1426             QLatin1String(DBUS_PATH_DBUS), QLatin1String(DBUS_INTERFACE_DBUS),
1427             QLatin1String("GetNameOwner"));
1428     msg << name;
1429     QDBusMessage reply = sendWithReply(msg, QDBusConnection::NoUseEventLoop);
1430     if (!lastError.isValid() && reply.type() == QDBusMessage::ReplyMessage)
1431         return reply.first().toString();
1432     return QString();
1433 }
1434
1435 QDBusInterfacePrivate *
1436 QDBusConnectionPrivate::findInterface(const QString &service,
1437                                       const QString &path,
1438                                       const QString &interface)
1439 {
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);
1447
1448     QDBusInterfacePrivate *p = new QDBusInterfacePrivate(QDBusConnection(name), this, owner, path, interface, mo);
1449
1450     if (!mo) {
1451         // invalid object
1452         p->isValid = false;
1453         p->lastError = lastError;
1454         if (!lastError.isValid()) {
1455             // try to determine why we couldn't get the data
1456             if (!connection)
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));
1468             else
1469                 p->lastError = QDBusError(QDBusError::Other, QLatin1String("Unknown error"));
1470         }
1471     }
1472
1473     return p;
1474 }
1475
1476 struct qdbus_Introspect
1477 {
1478     QString xml;
1479     inline void operator()(QDBusConnectionPrivate::ObjectTreeNode *node)
1480     { xml = qDBusIntrospectObject(node); }
1481 };
1482
1483 QDBusMetaObject *
1484 QDBusConnectionPrivate::findMetaObject(const QString &service, const QString &path,
1485                                        const QString &interface)
1486 {
1487     // service must be a unique connection name
1488     if (!interface.isEmpty()) {
1489         QReadLocker locker(&lock);
1490         QDBusMetaObject *mo = cachedMetaObjects.value(interface, 0);
1491         if (mo)
1492             return mo;
1493     }
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);
1500         if (mo)
1501             // maybe it got created when we switched from read to write lock
1502             return mo;
1503
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
1509         }
1510
1511         // release the lock and return
1512         return QDBusMetaObject::createMetaObject(interface, apply.xml, cachedMetaObjects, lastError);
1513     }
1514
1515     // not local: introspect the target object:
1516     QDBusMessage msg = QDBusMessage::methodCall(service, path,
1517                                                 QLatin1String(DBUS_INTERFACE_INTROSPECTABLE),
1518                                                 QLatin1String("Introspect"));
1519
1520
1521     QDBusMessage reply = sendWithReply(msg, QDBusConnection::NoUseEventLoop);
1522
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);
1528     if (mo)
1529         // maybe it got created when we switched from read to write lock
1530         return mo;
1531
1532     QString xml;
1533     if (reply.type() == QDBusMessage::ReplyMessage)
1534         // fetch the XML description
1535         xml = reply.first().toString();
1536     else {
1537         lastError = reply;
1538         if (reply.type() != QDBusMessage::ErrorMessage || lastError != QDBusError::UnknownMethod)
1539             return 0;           // error
1540     }
1541
1542     // release the lock and return
1543     return QDBusMetaObject::createMetaObject(interface, xml, cachedMetaObjects, lastError);
1544 }
1545
1546 void QDBusReplyWaiter::reply(const QDBusMessage &msg)
1547 {
1548     replyMsg = msg;
1549     QTimer::singleShot(0, this, SLOT(quit()));
1550 }
1551
1552 #include "qdbusconnection_p.moc"