190c46868455627f9556c0bc5fb3f490e2778e16
[platform/upstream/dbus.git] / qt / 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 static qDBusSpyHook messageSpyHook;
51
52 struct QDBusPendingCall
53 {
54     QPointer<QObject> receiver;
55     QList<int> metaTypes;
56     int methodIdx;
57     DBusPendingCall *pending;
58     const QDBusConnectionPrivate *connection;
59 };
60
61 class CallDeliveryEvent: public QEvent
62 {
63 public:
64     CallDeliveryEvent()
65         : QEvent(QEvent::User), object(0), flags(0), slotIdx(-1)
66         { }
67
68     const QDBusConnectionPrivate *conn;
69     QPointer<QObject> object;
70     QDBusMessage message;
71     QList<int> metaTypes;
72
73     int flags;
74     int slotIdx;
75 };
76
77 static dbus_bool_t qDBusAddTimeout(DBusTimeout *timeout, void *data)
78 {
79     Q_ASSERT(timeout);
80     Q_ASSERT(data);
81
82   //  qDebug("addTimeout %d", dbus_timeout_get_interval(timeout));
83
84     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
85
86     if (!dbus_timeout_get_enabled(timeout))
87         return true;
88
89     if (!QCoreApplication::instance()) {
90         d->pendingTimeouts.append(timeout);
91         return true;
92     }
93     int timerId = d->startTimer(dbus_timeout_get_interval(timeout));
94     if (!timerId)
95         return false;
96
97     d->timeouts[timerId] = timeout;
98     return true;
99 }
100
101 static void qDBusRemoveTimeout(DBusTimeout *timeout, void *data)
102 {
103     Q_ASSERT(timeout);
104     Q_ASSERT(data);
105
106   //  qDebug("removeTimeout");
107
108     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
109     d->pendingTimeouts.removeAll(timeout);
110
111     QDBusConnectionPrivate::TimeoutHash::iterator it = d->timeouts.begin();
112     while (it != d->timeouts.end()) {
113         if (it.value() == timeout) {
114             d->killTimer(it.key());
115             it = d->timeouts.erase(it);
116         } else {
117             ++it;
118         }
119     }
120 }
121
122 static void qDBusToggleTimeout(DBusTimeout *timeout, void *data)
123 {
124     Q_ASSERT(timeout);
125     Q_ASSERT(data);
126
127     //qDebug("ToggleTimeout");
128
129     qDBusRemoveTimeout(timeout, data);
130     qDBusAddTimeout(timeout, data);
131 }
132
133 static dbus_bool_t qDBusAddWatch(DBusWatch *watch, void *data)
134 {
135     Q_ASSERT(watch);
136     Q_ASSERT(data);
137
138     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
139
140     int flags = dbus_watch_get_flags(watch);
141     int fd = dbus_watch_get_fd(watch);
142
143     QDBusConnectionPrivate::Watcher watcher;
144     if (flags & DBUS_WATCH_READABLE) {
145         //qDebug("addReadWatch %d", fd);
146         watcher.watch = watch;
147         if (QCoreApplication::instance()) {
148             watcher.read = new QSocketNotifier(fd, QSocketNotifier::Read, d);
149             watcher.read->setEnabled(dbus_watch_get_enabled(watch));
150             d->connect(watcher.read, SIGNAL(activated(int)), SLOT(socketRead(int)));
151         }
152     }
153     if (flags & DBUS_WATCH_WRITABLE) {
154         //qDebug("addWriteWatch %d", fd);
155         watcher.watch = watch;
156         if (QCoreApplication::instance()) {
157             watcher.write = new QSocketNotifier(fd, QSocketNotifier::Write, d);
158             watcher.write->setEnabled(dbus_watch_get_enabled(watch));
159             d->connect(watcher.write, SIGNAL(activated(int)), SLOT(socketWrite(int)));
160         }
161     }
162     d->watchers.insertMulti(fd, watcher);
163
164     return true;
165 }
166
167 static void qDBusRemoveWatch(DBusWatch *watch, void *data)
168 {
169     Q_ASSERT(watch);
170     Q_ASSERT(data);
171
172     //qDebug("remove watch");
173
174     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
175     int fd = dbus_watch_get_fd(watch);
176
177     QDBusConnectionPrivate::WatcherHash::iterator i = d->watchers.find(fd);
178     while (i != d->watchers.end() && i.key() == fd) {
179         if (i.value().watch == watch) {
180             delete i.value().read;
181             delete i.value().write;
182             d->watchers.erase(i);
183             return;
184         }
185         ++i;
186     }
187 }
188
189 static void qDBusToggleWatch(DBusWatch *watch, void *data)
190 {
191     Q_ASSERT(watch);
192     Q_ASSERT(data);
193
194     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
195     int fd = dbus_watch_get_fd(watch);
196
197     QDBusConnectionPrivate::WatcherHash::iterator i = d->watchers.find(fd);
198     while (i != d->watchers.end() && i.key() == fd) {
199         if (i.value().watch == watch) {
200             bool enabled = dbus_watch_get_enabled(watch);
201             int flags = dbus_watch_get_flags(watch);
202
203             //qDebug("toggle watch %d to %d (write: %d, read: %d)", dbus_watch_get_fd(watch), enabled, flags & DBUS_WATCH_WRITABLE, flags & DBUS_WATCH_READABLE);
204
205             if (flags & DBUS_WATCH_READABLE && i.value().read)
206                 i.value().read->setEnabled(enabled);
207             if (flags & DBUS_WATCH_WRITABLE && i.value().write)
208                 i.value().write->setEnabled(enabled);
209             return;
210         }
211         ++i;
212     }
213 }
214
215 static void qDBusNewConnection(DBusServer *server, DBusConnection *c, void *data)
216 {
217     Q_ASSERT(data); Q_ASSERT(server); Q_ASSERT(c);
218     Q_UNUSED(data); Q_UNUSED(server); Q_UNUSED(c);
219
220     qDebug("SERVER: GOT A NEW CONNECTION"); // TODO
221 }
222
223 extern QDBUS_EXPORT void qDBusSetSpyHook(qDBusSpyHook);
224 void qDBusSetSpyHook(qDBusSpyHook hook)
225 {
226     messageSpyHook = hook;
227 }
228
229 #if USE_OUTSIDE_DISPATCH
230 # define HANDLED     DBUS_HANDLER_RESULT_HANDLED_OUTSIDE_DISPATCH
231 static DBusHandlerResult qDBusSignalFilterOutside(DBusConnection *connection,
232                                                   DBusMessage *message, void *data)
233 {
234     Q_ASSERT(data);
235     Q_UNUSED(connection);
236     Q_UNUSED(message);
237
238     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
239     if (d->mode == QDBusConnectionPrivate::InvalidMode)
240         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; // internal error, actually
241
242     CallDeliveryEvent *e = d->postedCallDeliveryEvent();
243
244     d->deliverCall(*e);
245     delete e;
246
247     return DBUS_HANDLER_RESULT_HANDLED;
248 }
249 #else
250 # define HANDLED     DBUS_HANDLER_RESULT_HANDLED
251 #endif
252
253 extern "C" {
254 static DBusHandlerResult
255 qDBusSignalFilter(DBusConnection *connection, DBusMessage *message, void *data)
256 {
257     return QDBusConnectionPrivate::messageFilter(connection, message, data);
258 }
259 }
260
261 DBusHandlerResult QDBusConnectionPrivate::messageFilter(DBusConnection *connection,
262                                                         DBusMessage *message, void *data)
263 {
264     Q_ASSERT(data);
265     Q_UNUSED(connection);
266
267     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
268     if (d->mode == QDBusConnectionPrivate::InvalidMode)
269         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
270
271     QDBusMessage amsg = QDBusMessage::fromDBusMessage(message, QDBusConnection(d->name));
272     qDebug() << "got message:" << amsg;
273
274     if (messageSpyHook) {
275         qDebug() << "calling the message spy hook";
276         (*messageSpyHook)(amsg);
277     }
278
279     bool handled = false;
280     int msgType = dbus_message_get_type(message);
281     if (msgType == DBUS_MESSAGE_TYPE_SIGNAL) {
282         handled = d->handleSignal(amsg);
283     } else if (msgType == DBUS_MESSAGE_TYPE_METHOD_CALL) {
284         handled = d->handleObjectCall(amsg);
285     }
286
287     return handled ? HANDLED :
288         DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
289 }
290
291 static void huntAndDestroy(QObject *needle, QDBusConnectionPrivate::ObjectTreeNode *haystack)
292 {
293     foreach (const QDBusConnectionPrivate::ObjectTreeNode::Data &entry, haystack->children)
294         huntAndDestroy(needle, entry.node);
295
296     if (needle == haystack->obj) {
297         haystack->obj = 0;
298         haystack->flags = 0;
299     }
300 }
301
302 static void huntAndEmit(DBusConnection *connection, DBusMessage *msg,
303                         QObject *needle, QDBusConnectionPrivate::ObjectTreeNode *haystack,
304                         const QString &path = QString())
305 {
306     foreach (const QDBusConnectionPrivate::ObjectTreeNode::Data &entry, haystack->children)
307         huntAndEmit(connection, msg, needle, entry.node, path + QLatin1String("/") + entry.name);
308
309     if (needle == haystack->obj && haystack->flags & QDBusConnection::ExportAdaptors) {
310         QByteArray p = path.toLatin1();
311         if (p.isEmpty())
312             p = "/";
313         //qDebug() << p;
314         DBusMessage *msg2 = dbus_message_copy(msg);
315         dbus_message_set_path(msg2, p);
316         dbus_connection_send(connection, msg2, 0);
317         dbus_message_unref(msg2);
318     }
319 }
320
321 static bool typesMatch(int metaId, int variantType)
322 {
323     if (metaId == int(variantType))
324         return true;
325
326     if (variantType == QVariant::Int && metaId == QMetaType::Short)
327         return true;
328
329     if (variantType == QVariant::UInt && (metaId == QMetaType::UShort ||
330                                           metaId == QMetaType::UChar))
331         return true;
332
333     if (variantType == QVariant::List) {
334         if (metaId == QDBusTypeHelper<bool>::listId() ||
335             metaId == QDBusTypeHelper<short>::listId() ||
336             metaId == QDBusTypeHelper<ushort>::listId() || 
337             metaId == QDBusTypeHelper<int>::listId() ||
338             metaId == QDBusTypeHelper<uint>::listId() ||
339             metaId == QDBusTypeHelper<qlonglong>::listId() ||
340             metaId == QDBusTypeHelper<qulonglong>::listId() ||
341             metaId == QDBusTypeHelper<double>::listId())
342             return true;
343     }
344
345     return false;               // no match
346 }
347
348 static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags,
349                     const QDBusTypeList &types, QList<int>& metaTypes)
350 {
351     // find the first slot
352     const QMetaObject *super = mo;
353     while (super != &QObject::staticMetaObject &&
354            super != &QDBusAbstractAdaptor::staticMetaObject)
355         super = super->superClass();
356
357     int attributeMask = (flags & QDBusConnection::ExportAllSlots) ?
358                         0 : QMetaMethod::Scriptable;
359
360     for (int idx = super->methodCount() ; idx <= mo->methodCount(); ++idx) {
361         QMetaMethod mm = mo->method(idx);
362
363         // check access:
364         if (mm.access() != QMetaMethod::Public)
365             continue;
366
367         // check type:
368         // unnecessary, since slots are never public:
369         //if (mm.methodType() != QMetaMethod::Slot)
370         //    continue;
371
372         // check name:
373         QByteArray sig = QMetaObject::normalizedSignature(mm.signature());
374         int paren = sig.indexOf('(');
375         if (paren != name.length() || !sig.startsWith( name ))
376             continue;
377
378         int returnType = qDBusNameToTypeId(mm.typeName());
379         bool isAsync = qDBusCheckAsyncTag(mm.tag());
380
381         // consistency check:
382         if (isAsync && returnType != QMetaType::Void)
383             continue;
384
385         int inputCount = qDBusParametersForMethod(mm, metaTypes);
386         if (inputCount == -1)
387             continue;           // problem parsing
388
389         metaTypes[0] = returnType;
390         bool hasMessage = false;
391         if (inputCount > 0 &&
392             metaTypes.at(inputCount) == QDBusConnectionPrivate::messageMetaType) {
393             // "no input parameters" is allowed as long as the message meta type is there
394             hasMessage = true;
395             --inputCount;
396         }
397
398         // try to match the parameters
399         if (inputCount != types.count())
400             continue;       // not enough parameters
401
402         bool matches = true;
403         int i;
404         for (i = 0; i < types.count(); ++i)
405             if ( !typesMatch(metaTypes.at(i + 1), types.at(i).qvariantType()) ) {
406                 matches = false;
407                 break;
408             }
409
410         if (!matches)
411             continue;           // we didn't match them all
412
413         // consistency check:
414         if (isAsync && metaTypes.count() > i + 1)
415             continue;
416
417         if (hasMessage && (mm.attributes() & attributeMask) != attributeMask)
418             continue;           // not exported
419
420         // if we got here, this slot matched
421         return idx;
422     }
423
424     // no slot matched
425     return -1;
426 }
427
428 static CallDeliveryEvent* prepareReply(QObject *object, int idx, const QList<int> &metaTypes,
429                                        const QDBusMessage &msg)
430 {
431     Q_ASSERT(object);
432
433     int n = metaTypes.count() - 1;
434     if (metaTypes[n] == QDBusConnectionPrivate::messageMetaType)
435         --n;
436
437     // check that types match
438     for (int i = 0; i < n; ++i)
439         if (!typesMatch(metaTypes.at(i + 1), msg.at(i).type()))
440             return 0;           // no match
441
442     // we can deliver
443     // prepare for the call
444     CallDeliveryEvent *data = new CallDeliveryEvent;
445     data->object = object;
446     data->flags = 0;
447     data->message = msg;
448     data->metaTypes = metaTypes;
449     data->slotIdx = idx;
450
451     return data;
452 }
453
454 bool QDBusConnectionPrivate::activateSignal(const QDBusConnectionPrivate::SignalHook& hook,
455                                             const QDBusMessage &msg)
456 {
457     // This is called by QDBusConnectionPrivate::handleSignal to deliver a signal
458     // that was received from D-Bus
459     //
460     // Signals are delivered to slots if the parameters match
461     // Slots can have less parameters than there are on the message
462     // Slots can optionally have one final parameter that is a QDBusMessage
463     // Slots receive read-only copies of the message (i.e., pass by value or by const-ref)
464     CallDeliveryEvent *call = prepareReply(hook.obj, hook.midx, hook.params, msg);
465     if (call) {
466         postCallDeliveryEvent(call);
467         return true;
468     }
469     return false;
470 }
471
472 bool QDBusConnectionPrivate::activateCall(QObject* object, int flags,
473                                           const QDBusMessage &msg)
474 {
475     // This is called by QDBusConnectionPrivate::handleObjectCall to place a call
476     // to a slot on the object.
477     //
478     // The call is delivered to the first slot that matches the following conditions:
479     //  - has the same name as the message's target name
480     //  - ALL of the message's types are found in slot's parameter list
481     //  - optionally has one more parameter of type QDBusMessage
482     // If none match, then the slot of the same name as the message target and with
483     // the first type of QDBusMessage is delivered.
484     //
485     // Because the marshalling of D-Bus data into QVariant loses the information on
486     // the original types, the message signature is used to determine the original type.
487     // Aside from that, the "int" and "unsigned" types will be tried as well.
488     //
489     // The D-Bus specification requires that all MethodCall messages be replied to, unless the
490     // caller specifically waived this requirement. This means that we inspect if the user slot
491     // generated a reply and, if it didn't, we will. Obviously, if the user slot doesn't take a
492     // QDBusMessage parameter, it cannot generate a reply.
493     //
494     // When a return message is generated, the slot's return type, if any, will be placed
495     // in the message's first position. If there are non-const reference parameters to the
496     // slot, they must appear at the end and will be placed in the subsequent message
497     // positions.
498
499     if (!object)
500         return false;
501
502     QList<int> metaTypes;
503     int idx;
504
505     {
506         const QMetaObject *mo = object->metaObject();
507         QDBusTypeList typeList(msg.signature().toUtf8());
508         QByteArray name = msg.name().toUtf8();
509
510         // find a slot that matches according to the rules above
511         idx = ::findSlot(mo, name, flags, typeList, metaTypes);
512         if (idx == -1) {
513             // try with no parameters, but with a QDBusMessage
514             idx = ::findSlot(mo, name, flags, QDBusTypeList(), metaTypes);
515             if (metaTypes.count() != 2 || metaTypes.at(1) != messageMetaType)
516                 return false;
517         }
518     }
519
520     // found the slot to be called
521     // prepare for the call:
522     CallDeliveryEvent *call = new CallDeliveryEvent;
523
524     // parameters:
525     call->object = object;
526     call->flags = flags;
527     call->message = msg;
528
529     // save our state:
530     call->metaTypes = metaTypes;
531     call->slotIdx = idx;
532
533     postCallDeliveryEvent(call);
534
535     // ready
536     return true;
537 }
538
539 void QDBusConnectionPrivate::postCallDeliveryEvent(CallDeliveryEvent *data)
540 {
541     Q_ASSERT(data);
542     data->conn = this;    
543 #if USE_OUTSIDE_DISPATCH
544     callDeliveryMutex.lock();
545     callDeliveryState = data;
546 #else
547     QCoreApplication::postEvent( this, data );
548 #endif
549 }
550
551 CallDeliveryEvent *QDBusConnectionPrivate::postedCallDeliveryEvent()
552 {
553     CallDeliveryEvent *e = callDeliveryState;
554     Q_ASSERT(e && e->conn == this);
555
556     // release it:
557     callDeliveryState = 0;
558     callDeliveryMutex.unlock();
559
560     return e;
561 }
562
563 void QDBusConnectionPrivate::deliverCall(const CallDeliveryEvent& data) const
564 {
565     // resume state:
566     const QList<int>& metaTypes = data.metaTypes;
567     const QDBusMessage& msg = data.message;
568
569     QVarLengthArray<void *, 10> params;
570     params.reserve(metaTypes.count());
571
572     QVariantList auxParameters;
573     // let's create the parameter list
574
575     // first one is the return type -- add it below
576     params.append(0);
577
578     // add the input parameters
579     int i;
580     for (i = 1; i <= msg.count(); ++i) {
581         int id = metaTypes[i];
582         if (id == QDBusConnectionPrivate::messageMetaType)
583             break;
584
585         if (id == int(msg.at(i - 1).userType()))
586             // no conversion needed
587             params.append(const_cast<void *>( msg.at(i - 1).constData() ));
588         else {
589             // convert to what the function expects
590             auxParameters.append(QVariant());
591             
592             const QVariant &in = msg.at(i - 1);
593             QVariant &out = auxParameters[auxParameters.count() - 1];
594
595             bool error = false;
596             if (id == QVariant::List) {
597                 int mid = in.userType();
598                 // the only conversion possible here is from a specialised QList<T> to QVariantList
599                 if (mid == QDBusTypeHelper<bool>::listId())
600                     out = qVariantFromValue(QDBusTypeHelper<bool>::toVariantList(in));
601                 else if (mid == QDBusTypeHelper<short>::listId())
602                     out = qVariantFromValue(QDBusTypeHelper<short>::toVariantList(in));
603                 else if (mid == QDBusTypeHelper<ushort>::listId())
604                     out = qVariantFromValue(QDBusTypeHelper<ushort>::toVariantList(in));
605                 else if (mid == QDBusTypeHelper<int>::listId())
606                     out = qVariantFromValue(QDBusTypeHelper<int>::toVariantList(in));
607                 else if (mid == QDBusTypeHelper<uint>::listId())
608                     out = qVariantFromValue(QDBusTypeHelper<uint>::toVariantList(in));
609                 else if (mid == QDBusTypeHelper<qlonglong>::listId())
610                     out = qVariantFromValue(QDBusTypeHelper<qlonglong>::toVariantList(in));
611                 else if (mid == QDBusTypeHelper<qulonglong>::listId())
612                     out = qVariantFromValue(QDBusTypeHelper<qulonglong>::toVariantList(in));
613                 else if (mid == QDBusTypeHelper<double>::listId())
614                     out = qVariantFromValue(QDBusTypeHelper<double>::toVariantList(in));
615                 else
616                     error = true;
617             } else if (in.type() == QVariant::UInt) {
618                 if (id == QMetaType::UChar) {
619                     uchar uc = in.toUInt();
620                     out = qVariantFromValue(uc);
621                 } else if (id == QMetaType::UShort) {
622                     ushort us = in.toUInt();
623                     out = qVariantFromValue(us);
624                 } else {
625                     error = true;
626                 }
627             } else if (in.type() == QVariant::Int) {
628                 if (id == QMetaType::Short) {
629                     short s = in.toInt();
630                     out = qVariantFromValue(s);
631                 } else {
632                     error = true;
633                 }
634             } else {
635                 error = true;
636             }
637
638             if (error)
639                 qFatal("Internal error: got invalid meta type %d when trying to convert to meta type %d",
640                        in.userType(), id);
641
642             params.append( const_cast<void *>(out.constData()) );
643         }
644     }
645
646     bool takesMessage = false;
647     if (metaTypes.count() > i && metaTypes[i] == QDBusConnectionPrivate::messageMetaType) {
648         params.append(const_cast<void*>(static_cast<const void*>(&msg)));
649         takesMessage = true;
650         ++i;
651     }
652
653     // output arguments
654     QVariantList outputArgs;
655     void *null = 0;
656     if (metaTypes[0] != QMetaType::Void) {
657         QVariant arg(metaTypes[0], null);
658         outputArgs.append( arg );
659         params[0] = const_cast<void*>(outputArgs.at( outputArgs.count() - 1 ).constData());
660     }
661     for ( ; i < metaTypes.count(); ++i) {
662         QVariant arg(metaTypes[i], null);
663         outputArgs.append( arg );
664         params.append( const_cast<void*>(outputArgs.at( outputArgs.count() - 1 ).constData()) );
665     }
666
667     // make call:
668     bool fail;
669     if (data.object.isNull())
670         fail = true;
671     else
672         fail = data.object->qt_metacall(QMetaObject::InvokeMetaMethod,
673                                         data.slotIdx, params.data()) >= 0;
674
675     // do we create a reply? Only if the caller is waiting for a reply and one hasn't been sent
676     // yet.
677     if (!msg.noReply() && !msg.wasRepliedTo()) {
678         if (!fail) {
679             // normal reply
680             QDBusMessage reply = QDBusMessage::methodReply(msg);
681             reply += outputArgs;
682
683             qDebug() << "Automatically sending reply:" << reply;
684             send(reply);
685         }
686         else {
687             // generate internal error
688             QDBusMessage reply = QDBusMessage::error(msg, QDBusError(QDBusError::InternalError,
689                     QLatin1String("Failed to deliver message")));
690             qWarning("Internal error: Failed to deliver message");
691             send(reply);
692         }
693     }
694
695     return;
696 }
697
698 void QDBusConnectionPrivate::customEvent(QEvent *event)
699 {
700     // nothing else should be sending custom events at us
701     CallDeliveryEvent* call = static_cast<CallDeliveryEvent *>(event);
702
703     // self check:
704     Q_ASSERT(call->conn == this);
705
706     deliverCall(*call);
707 }
708
709 QDBusConnectionPrivate::QDBusConnectionPrivate(QObject *parent)
710     : QObject(parent), ref(1), mode(InvalidMode), connection(0), server(0), busService(0)
711 {
712     extern bool qDBusInitThreads();
713     static const int msgType = registerMessageMetaType();
714     static const bool threads = qDBusInitThreads();
715     static const bool metatypes = QDBusMetaTypeId::innerInitialize();
716
717     Q_UNUSED(msgType);
718     Q_UNUSED(threads);
719     Q_UNUSED(metatypes);
720
721     dbus_error_init(&error);
722
723     rootNode.flags = 0;
724 }
725
726 QDBusConnectionPrivate::~QDBusConnectionPrivate()
727 {
728     if (dbus_error_is_set(&error))
729         dbus_error_free(&error);
730
731     closeConnection();
732     rootNode.clear();        // free resources
733     qDeleteAll(cachedMetaObjects);
734 }
735
736 void QDBusConnectionPrivate::closeConnection()
737 {
738     QWriteLocker locker(&lock);
739     ConnectionMode oldMode = mode;
740     mode = InvalidMode; // prevent reentrancy
741     if (oldMode == ServerMode) {
742         if (server) {
743             dbus_server_disconnect(server);
744             dbus_server_unref(server);
745             server = 0;
746         }
747     } else if (oldMode == ClientMode) {
748         if (connection) {
749             dbus_connection_close(connection);
750             // send the "close" message
751             while (dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS)
752                 ;
753             dbus_connection_unref(connection);
754             connection = 0;
755         }
756     }
757 }
758
759 bool QDBusConnectionPrivate::handleError()
760 {
761     lastError = QDBusError(&error);
762     if (dbus_error_is_set(&error))
763         dbus_error_free(&error);
764     return lastError.isValid();
765 }
766
767 void QDBusConnectionPrivate::bindToApplication()
768 {
769     // Yay, now that we have an application we are in business
770     Q_ASSERT_X(QCoreApplication::instance(), "QDBusConnection",
771                "qDBusBindToApplication called without an application");
772     moveToThread(QCoreApplication::instance()->thread());
773     
774     // Re-add all watchers
775     WatcherHash oldWatchers = watchers;
776     watchers.clear();
777     QHashIterator<int, QDBusConnectionPrivate::Watcher> it(oldWatchers);
778     while (it.hasNext()) {
779         it.next();
780         if (!it.value().read && !it.value().write) {
781             qDBusAddWatch(it.value().watch, this);
782         } else {
783             watchers.insertMulti(it.key(), it.value());
784         }
785     }
786
787     // Re-add all timeouts
788     while (!pendingTimeouts.isEmpty())
789        qDBusAddTimeout(pendingTimeouts.takeFirst(), this);
790 }
791
792 void QDBusConnectionPrivate::timerEvent(QTimerEvent *e)
793 {
794     DBusTimeout *timeout = timeouts.value(e->timerId(), 0);
795     dbus_timeout_handle(timeout);
796 }
797
798 void QDBusConnectionPrivate::doDispatch()
799 {
800     if (mode == ClientMode)
801         while (dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS);
802 }
803
804 void QDBusConnectionPrivate::socketRead(int fd)
805 {
806     QHashIterator<int, QDBusConnectionPrivate::Watcher> it(watchers);
807     while (it.hasNext()) {
808         it.next();
809         if (it.key() == fd && it.value().read && it.value().read->isEnabled()) {
810             if (!dbus_watch_handle(it.value().watch, DBUS_WATCH_READABLE))
811                 qDebug("OUT OF MEM");
812         }
813     }
814
815     doDispatch();
816 }
817
818 void QDBusConnectionPrivate::socketWrite(int fd)
819 {
820     QHashIterator<int, QDBusConnectionPrivate::Watcher> it(watchers);
821     while (it.hasNext()) {
822         it.next();
823         if (it.key() == fd && it.value().write && it.value().write->isEnabled()) {
824             if (!dbus_watch_handle(it.value().watch, DBUS_WATCH_WRITABLE))
825                 qDebug("OUT OF MEM");
826         }
827     }
828 }
829
830 void QDBusConnectionPrivate::objectDestroyed(QObject *obj)
831 {
832     QWriteLocker locker(&lock);
833     huntAndDestroy(obj, &rootNode);
834
835     SignalHookHash::iterator sit = signalHooks.begin();
836     while (sit != signalHooks.end()) {
837         if (static_cast<QObject *>(sit.value().obj) == obj)
838             sit = signalHooks.erase(sit);
839         else
840             ++sit;
841     }
842
843     obj->disconnect(this);
844 }
845
846 void QDBusConnectionPrivate::relaySignal(QObject *obj, const char *interface, const char *name,
847                                          const QVariantList &args)
848 {
849     QReadLocker locker(&lock);
850     QDBusMessage message = QDBusMessage::signal(QLatin1String("/"), QLatin1String(interface),
851                                                 QLatin1String(name));
852     message += args;
853     DBusMessage *msg = message.toDBusMessage();
854     if (!msg) {
855         qWarning("Could not emit signal %s.%s", interface, name);
856         return;
857     }
858
859     //qDebug() << "Emitting signal" << message;
860     //qDebug() << "for paths:";
861     dbus_message_set_no_reply(msg, true); // the reply would not be delivered to anything
862     huntAndEmit(connection, msg, obj, &rootNode);
863     dbus_message_unref(msg);
864 }
865
866 int QDBusConnectionPrivate::registerMessageMetaType()
867 {
868     int tp = messageMetaType = qRegisterMetaType<QDBusMessage>("QDBusMessage");
869     return tp;
870 }
871
872 int QDBusConnectionPrivate::findSlot(QObject* obj, const QByteArray &normalizedName,
873                                      QList<int> &params)
874 {
875     int midx = obj->metaObject()->indexOfMethod(normalizedName);
876     if (midx == -1) {
877         qWarning("No such slot '%s' while connecting D-Bus", normalizedName.constData());
878         return -1;
879     }
880
881     int inputCount = qDBusParametersForMethod(obj->metaObject()->method(midx), params);
882     if ( inputCount == -1 || inputCount + 1 != params.count() )
883         return -1;              // failed to parse or invalid arguments or output arguments
884
885     return midx;
886 }
887
888 bool QDBusConnectionPrivate::prepareHook(QDBusConnectionPrivate::SignalHook &hook, QString &key,
889                                          const QString &service, const QString &path,
890                                          const QString &interface, const QString &name,
891                                          QObject *receiver, const char *signal, int minMIdx,
892                                          bool buildSignature)
893 {
894     QByteArray normalizedName = QMetaObject::normalizedSignature(signal + 1);
895     hook.midx = findSlot(receiver, normalizedName, hook.params);
896     if (hook.midx < minMIdx)
897         return false;
898
899     hook.sender = service;
900     hook.path = path;
901     hook.obj = receiver;
902
903     // build the D-Bus signal name and signature
904     QString mname = name;
905     if (mname.isEmpty()) {
906         normalizedName.truncate(normalizedName.indexOf('('));        
907         mname = QString::fromUtf8(normalizedName);
908     }
909     key = mname;
910     key.reserve(interface.length() + 1 + mname.length());
911     key += ':';
912     key += interface;
913
914     if (buildSignature) {
915         hook.signature.clear();
916         for (int i = 1; i < hook.params.count(); ++i)
917             if (hook.params.at(i) != messageMetaType)
918                 hook.signature += QLatin1String( QDBusType::dbusSignature( QVariant::Type(hook.params.at(i)) ) );
919     }
920     
921     return true;                // connect to this signal
922 }
923
924 bool QDBusConnectionPrivate::activateInternalFilters(const ObjectTreeNode *node, const QDBusMessage &msg)
925 {
926     // object may be null
927
928     if (msg.interface().isEmpty() || msg.interface() == QLatin1String(DBUS_INTERFACE_INTROSPECTABLE)) {
929         if (msg.method() == QLatin1String("Introspect") && msg.signature().isEmpty())
930             qDBusIntrospectObject(node, msg);
931         if (msg.interface() == QLatin1String(DBUS_INTERFACE_INTROSPECTABLE))
932             return true;
933     }
934
935     if (node->obj && (msg.interface().isEmpty() ||
936                       msg.interface() == QLatin1String(DBUS_INTERFACE_PROPERTIES))) {
937         if (msg.method() == QLatin1String("Get") && msg.signature() == QLatin1String("ss"))
938             qDBusPropertyGet(node, msg);
939         else if (msg.method() == QLatin1String("Set") && msg.signature() == QLatin1String("ssv"))
940             qDBusPropertySet(node, msg);
941
942         if (msg.interface() == QLatin1String(DBUS_INTERFACE_PROPERTIES))
943             return true;
944     }
945
946     return false;
947 }
948
949 bool QDBusConnectionPrivate::activateObject(const ObjectTreeNode *node, const QDBusMessage &msg)
950 {
951     // This is called by QDBusConnectionPrivate::handleObjectCall to place a call to a slot
952     // on the object.
953     //
954     // The call is routed through the adaptor sub-objects if we have any
955
956     // object may be null
957
958     QDBusAdaptorConnector *connector;
959     if (node->flags & QDBusConnection::ExportAdaptors &&
960         (connector = qDBusFindAdaptorConnector(node->obj))) {
961         int newflags = node->flags | QDBusConnection::ExportAllSlots;
962
963         if (msg.interface().isEmpty()) {
964             // place the call in all interfaces
965             // let the first one that handles it to work
966             foreach (const QDBusAdaptorConnector::AdaptorData &entry, connector->adaptors)
967                 if (activateCall(entry.adaptor, newflags, msg))
968                     return true;
969         } else {
970             // check if we have an interface matching the name that was asked:
971             QDBusAdaptorConnector::AdaptorMap::ConstIterator it;
972             it = qLowerBound(connector->adaptors.constBegin(), connector->adaptors.constEnd(),
973                              msg.interface());
974             if (it != connector->adaptors.end() && it->interface == msg.interface())
975                 if (activateCall(it->adaptor, newflags, msg))
976                 return true;
977         }
978     }
979
980     // no adaptors matched
981     // try our standard filters
982     if (activateInternalFilters(node, msg))
983         return true;
984
985     // try the object itself:
986     if (node->flags & QDBusConnection::ExportSlots && activateCall(node->obj, node->flags, msg))
987         return true;
988 #if 0
989     // nothing matched
990     qDebug("Call failed: no match for %s%s%s at %s",
991            qPrintable(msg.interface()), msg.interface().isEmpty() ? "" : ".",
992            qPrintable(msg.name()),
993            qPrintable(msg.path()));
994 #endif
995     return false;
996 }
997
998 template<typename Func>
999 static bool applyForObject(QDBusConnectionPrivate::ObjectTreeNode *root, const QString &fullpath,
1000                            Func& functor)
1001 {
1002     // walk the object tree
1003     QStringList path = fullpath.split(QLatin1Char('/'));
1004     if (path.last().isEmpty())
1005         path.removeLast();      // happens if path is "/"
1006     int i = 1;
1007     QDBusConnectionPrivate::ObjectTreeNode *node = root;
1008
1009     // try our own tree first
1010     while (node && !(node->flags & QDBusConnection::ExportChildObjects) ) {
1011         if (i == path.count()) {
1012             // found our object
1013             functor(node);
1014             return true;
1015         }
1016
1017         QVector<QDBusConnectionPrivate::ObjectTreeNode::Data>::ConstIterator it =
1018             qLowerBound(node->children.constBegin(), node->children.constEnd(), path.at(i));
1019         if (it != node->children.constEnd() && it->name == path.at(i))
1020             // match
1021             node = it->node;
1022         else
1023             node = 0;
1024
1025         ++i;
1026     }
1027
1028     // any object in the tree can tell us to switch to its own object tree:
1029     if (node && node->flags & QDBusConnection::ExportChildObjects) {
1030         QObject *obj = node->obj;
1031
1032         while (obj) {
1033             if (i == path.count()) {
1034                 // we're at the correct level
1035                 QDBusConnectionPrivate::ObjectTreeNode fakenode(*node);
1036                 fakenode.obj = obj;
1037                 functor(&fakenode);
1038                 return true;
1039             }
1040
1041             const QObjectList children = obj->children();
1042
1043             // find a child with the proper name
1044             QObject *next = 0;
1045             foreach (QObject *child, children)
1046                 if (child->objectName() == path.at(i)) {
1047                     next = child;
1048                     break;
1049                 }
1050
1051             if (!next)
1052                 break;
1053
1054             ++i;
1055             obj = next;
1056         }
1057     }
1058
1059     // object not found
1060     return false;
1061 }
1062
1063 struct qdbus_activateObject
1064 {
1065     QDBusConnectionPrivate *self;
1066     const QDBusMessage &msg;
1067     bool returnVal;
1068     inline qdbus_activateObject(QDBusConnectionPrivate *s, const QDBusMessage &m)
1069         : self(s), msg(m)
1070     { }
1071
1072     inline void operator()(QDBusConnectionPrivate::ObjectTreeNode *node)
1073     { returnVal = self->activateObject(node, msg); }
1074 };
1075
1076 bool QDBusConnectionPrivate::handleObjectCall(const QDBusMessage &msg)
1077 {
1078     QReadLocker locker(&lock);
1079
1080     qdbus_activateObject apply(this, msg);
1081     if (applyForObject(&rootNode, msg.path(), apply))
1082         return apply.returnVal;
1083
1084     qDebug("Call failed: no object found at %s", qPrintable(msg.path()));
1085     return false;
1086 }
1087
1088 bool QDBusConnectionPrivate::handleSignal(const QString &key, const QDBusMessage& msg)
1089 {
1090     bool result = false;
1091     SignalHookHash::const_iterator it = signalHooks.find(key);
1092     //qDebug("looking for: %s", path.toLocal8Bit().constData());
1093     //qDebug() << signalHooks.keys();
1094     for ( ; it != signalHooks.constEnd() && it.key() == key; ++it) {
1095         const SignalHook &hook = it.value();
1096         if ( !hook.sender.isEmpty() && hook.sender != msg.sender() )
1097             continue;
1098         if ( !hook.path.isEmpty() && hook.path != msg.path() )
1099             continue;
1100         if ( !hook.signature.isEmpty() && hook.signature != msg.signature() )
1101             continue;
1102         if ( hook.signature.isEmpty() && !hook.signature.isNull() && !msg.signature().isEmpty())
1103             continue;
1104
1105         // yes, |=
1106         result |= activateSignal(hook, msg);
1107     }
1108     return result;
1109 }
1110
1111 bool QDBusConnectionPrivate::handleSignal(const QDBusMessage& msg)
1112 {
1113     QString key = msg.member();
1114     key.reserve(key.length() + 1 + msg.interface().length());
1115     key += ':';
1116     key += msg.interface();
1117
1118     QReadLocker locker(&lock);
1119     bool result = handleSignal(key, msg);    // one try
1120
1121     key.truncate(msg.member().length() + 1); // keep the ':'
1122     result |= handleSignal(key, msg);        // second try
1123     return result;
1124 }
1125
1126 static dbus_int32_t server_slot = -1;
1127
1128 void QDBusConnectionPrivate::setServer(DBusServer *s)
1129 {
1130     if (!server) {
1131         handleError();
1132         return;
1133     }
1134
1135     server = s;
1136     mode = ServerMode;
1137
1138     dbus_server_allocate_data_slot(&server_slot);
1139     if (server_slot < 0)
1140         return;
1141
1142     dbus_server_set_watch_functions(server, qDBusAddWatch, qDBusRemoveWatch,
1143                                     qDBusToggleWatch, this, 0); // ### check return type?
1144     dbus_server_set_timeout_functions(server, qDBusAddTimeout, qDBusRemoveTimeout,
1145                                       qDBusToggleTimeout, this, 0);
1146     dbus_server_set_new_connection_function(server, qDBusNewConnection, this, 0);
1147
1148     dbus_server_set_data(server, server_slot, this, 0);
1149 }
1150
1151 void QDBusConnectionPrivate::setConnection(DBusConnection *dbc)
1152 {
1153     if (!dbc) {
1154         handleError();
1155         return;
1156     }
1157
1158     connection = dbc;
1159     mode = ClientMode;
1160
1161     dbus_connection_set_exit_on_disconnect(connection, false);
1162     dbus_connection_set_watch_functions(connection, qDBusAddWatch, qDBusRemoveWatch,
1163                                         qDBusToggleWatch, this, 0);
1164     dbus_connection_set_timeout_functions(connection, qDBusAddTimeout, qDBusRemoveTimeout,
1165                                           qDBusToggleTimeout, this, 0);
1166 //    dbus_bus_add_match(connection, "type='signal',interface='com.trolltech.dbus.Signal'", &error);
1167 //    dbus_bus_add_match(connection, "type='signal'", &error);
1168
1169     dbus_bus_add_match(connection, "type='signal'", &error);
1170     if (handleError()) {
1171         closeConnection();
1172         return;
1173     }
1174
1175     const char *service = dbus_bus_get_unique_name(connection);
1176     if (service) {
1177         QVarLengthArray<char, 56> filter;
1178         filter.append("destination='", 13);
1179         filter.append(service, qstrlen(service));
1180         filter.append("\'\0", 2);
1181
1182         dbus_bus_add_match(connection, filter.constData(), &error);
1183         if (handleError()) {
1184             closeConnection();
1185             return;
1186         }
1187     } else {
1188         qWarning("QDBusConnectionPrivate::SetConnection: Unable to get base service");
1189     }
1190
1191 #if USE_OUTSIDE_DISPATCH
1192     dbus_connection_add_filter_outside(connection, qDBusSignalFilter, qDBusSignalFilterOutside, this, 0);
1193 #else
1194     dbus_connection_add_filter(connection, qDBusSignalFilter, this, 0);
1195 #endif
1196
1197     //qDebug("base service: %s", service);
1198
1199     // schedule a dispatch:
1200     QMetaObject::invokeMethod(this, "doDispatch", Qt::QueuedConnection);
1201 }
1202
1203 extern "C"{
1204 static void qDBusResultReceived(DBusPendingCall *pending, void *user_data)
1205 {
1206     QDBusConnectionPrivate::messageResultReceived(pending, user_data);
1207 }
1208 }
1209
1210 void QDBusConnectionPrivate::messageResultReceived(DBusPendingCall *pending, void *user_data)
1211 {
1212     QDBusPendingCall *call = reinterpret_cast<QDBusPendingCall *>(user_data);
1213     QDBusConnectionPrivate *connection = const_cast<QDBusConnectionPrivate *>(call->connection);
1214     Q_ASSERT(call->pending == pending);
1215
1216     if (!call->receiver.isNull() && call->methodIdx != -1) {
1217         DBusMessage *reply = dbus_pending_call_steal_reply(pending);
1218
1219         // Deliver the return values of a remote function call.
1220         //
1221         // There is only one connection and it is specified by idx
1222         // The slot must have the same parameter types that the message does
1223         // The slot may have less parameters than the message
1224         // The slot may optionally have one final parameter that is QDBusMessage
1225         // The slot receives read-only copies of the message (i.e., pass by value or by const-ref)
1226
1227         QDBusMessage msg = QDBusMessage::fromDBusMessage(reply, QDBusConnection(connection->name));
1228         qDebug() << "got message: " << msg;
1229         CallDeliveryEvent *e = prepareReply(call->receiver, call->methodIdx, call->metaTypes, msg);
1230         if (e)
1231             connection->postCallDeliveryEvent(e);
1232         else
1233             qDebug() << "Deliver failed!";
1234     }
1235     dbus_pending_call_unref(pending);
1236     delete call;
1237 }
1238
1239 int QDBusConnectionPrivate::send(const QDBusMessage& message) const
1240 {
1241     DBusMessage *msg = message.toDBusMessage();
1242     if (!msg)
1243         return 0;
1244
1245     dbus_message_set_no_reply(msg, true); // the reply would not be delivered to anything
1246
1247     qDebug() << "sending message:" << message;
1248     bool isOk = dbus_connection_send(connection, msg, 0);
1249     int serial = 0;
1250     if (isOk)
1251         serial = dbus_message_get_serial(msg);
1252
1253     dbus_message_unref(msg);
1254     return serial;
1255 }
1256
1257 QDBusMessage QDBusConnectionPrivate::sendWithReply(const QDBusMessage &message,
1258                                                    int mode)
1259 {
1260     if (!QCoreApplication::instance() || mode == QDBusConnection::NoUseEventLoop) {
1261         DBusMessage *msg = message.toDBusMessage();
1262         if (!msg)
1263             return QDBusMessage();
1264
1265         qDebug() << "sending message:" << message;
1266         DBusMessage *reply = dbus_connection_send_with_reply_and_block(connection, msg,
1267                                                                        -1, &error);
1268         handleError();
1269         dbus_message_unref(msg);
1270
1271         if (lastError.isValid())
1272             return QDBusMessage::fromError(lastError);
1273
1274         QDBusMessage amsg = QDBusMessage::fromDBusMessage(reply, QDBusConnection(name));
1275         qDebug() << "got message:" << amsg;
1276
1277         if (dbus_connection_get_dispatch_status(connection) == DBUS_DISPATCH_DATA_REMAINS)
1278             QMetaObject::invokeMethod(this, "doDispatch", Qt::QueuedConnection);
1279         return amsg;
1280     } else {                    // use the event loop
1281         QDBusReplyWaiter waiter;
1282         if (sendWithReplyAsync(message, &waiter, SLOT(reply(const QDBusMessage&))) > 0) {
1283             // enter the event loop and wait for a reply
1284             waiter.exec(QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents);
1285
1286             lastError = waiter.replyMsg; // set or clear error
1287             return waiter.replyMsg;
1288         }
1289
1290         return QDBusMessage();
1291     }
1292 }    
1293
1294 int QDBusConnectionPrivate::sendWithReplyAsync(const QDBusMessage &message, QObject *receiver,
1295                                                const char *method)
1296 {
1297     if (!receiver || !method || !*method)
1298         // would not be able to deliver a reply
1299         return send(message);
1300
1301     int slotIdx = -1;
1302     QList<int> metaTypes;
1303     QByteArray normalizedName = QMetaObject::normalizedSignature(method + 1);
1304     slotIdx = findSlot(receiver, normalizedName, metaTypes);
1305     if (slotIdx == -1)
1306         // would not be able to deliver a reply
1307         return send(message);
1308
1309     DBusMessage *msg = message.toDBusMessage();
1310     if (!msg)
1311         return 0;
1312
1313     qDebug() << "sending message:" << message;
1314     DBusPendingCall *pending = 0;
1315     if (dbus_connection_send_with_reply(connection, msg, &pending, message.timeout())) {
1316         if (slotIdx != -1) {
1317             QDBusPendingCall *pcall = new QDBusPendingCall;
1318             pcall->receiver = receiver;
1319             pcall->metaTypes = metaTypes;
1320             pcall->methodIdx = slotIdx;
1321             pcall->connection = this;
1322             pcall->pending = dbus_pending_call_ref(pending);
1323             dbus_pending_call_set_notify(pending, qDBusResultReceived, pcall, 0);
1324         }
1325         dbus_pending_call_unref(pending);
1326         return dbus_message_get_serial(msg);
1327     }
1328
1329     return 0;
1330 }
1331
1332 void QDBusConnectionPrivate::connectSignal(const QString &key, const SignalHook &hook)
1333 {
1334     signalHooks.insertMulti(key, hook);
1335     connect(hook.obj, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*)));
1336 }
1337
1338 void QDBusConnectionPrivate::registerObject(const ObjectTreeNode *node)
1339 {
1340     connect(node->obj, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*)));
1341
1342     if (node->flags & QDBusConnection::ExportAdaptors) {
1343         QDBusAdaptorConnector *connector = qDBusCreateAdaptorConnector(node->obj);
1344
1345         // disconnect and reconnect to avoid duplicates
1346         connector->disconnect(SIGNAL(relaySignal(QObject*,const char*,const char*,QVariantList)),
1347                               this, SLOT(relaySignal(QObject*,const char*,const char*,QVariantList)));
1348         connect(connector, SIGNAL(relaySignal(QObject*,const char*,const char*,QVariantList)),
1349                 SLOT(relaySignal(QObject*,const char*,const char*,QVariantList)));
1350     }
1351 }
1352
1353 void QDBusConnectionPrivate::connectRelay(const QString &service, const QString &path,
1354                                           const QString &interface,
1355                                           QDBusAbstractInterface *receiver,
1356                                           const char *signal)
1357 {
1358     // this function is called by QDBusAbstractInterface when one of its signals is connected
1359     // we set up a relay from D-Bus into it
1360     SignalHook hook;
1361     QString key;
1362     if (!prepareHook(hook, key, service, path, interface, QString(), receiver, signal,
1363                      QDBusAbstractInterface::staticMetaObject.methodCount(), true))
1364         return;                 // don't connect
1365
1366     // add it to our list:
1367     QWriteLocker locker(&lock);
1368     SignalHookHash::ConstIterator it = signalHooks.find(key);
1369     SignalHookHash::ConstIterator end = signalHooks.end();
1370     for ( ; it != end && it.key() == key; ++it) {
1371         const SignalHook &entry = it.value();
1372         if (entry.sender == hook.sender &&
1373             entry.path == hook.path &&
1374             entry.signature == hook.signature &&
1375             entry.obj == hook.obj &&
1376             entry.midx == hook.midx)
1377             return;             // already there, no need to re-add
1378     }
1379
1380     connectSignal(key, hook);
1381 }
1382
1383 void QDBusConnectionPrivate::disconnectRelay(const QString &service, const QString &path,
1384                                              const QString &interface,
1385                                              QDBusAbstractInterface *receiver,
1386                                              const char *signal)
1387 {
1388     // this function is called by QDBusAbstractInterface when one of its signals is disconnected
1389     // we remove relay from D-Bus into it
1390     SignalHook hook;
1391     QString key;
1392     if (!prepareHook(hook, key, service, path, interface, QString(), receiver, signal,
1393                      QDBusAbstractInterface::staticMetaObject.methodCount(), true))
1394         return;                 // don't connect
1395
1396     // remove it from our list:
1397     QWriteLocker locker(&lock);
1398     SignalHookHash::Iterator it = signalHooks.find(key);
1399     SignalHookHash::Iterator end = signalHooks.end();
1400     for ( ; it != end && it.key() == key; ++it) {
1401         const SignalHook &entry = it.value();
1402         if (entry.sender == hook.sender &&
1403             entry.path == hook.path &&
1404             entry.signature == hook.signature &&
1405             entry.obj == hook.obj &&
1406             entry.midx == hook.midx) {
1407             // found it
1408             signalHooks.erase(it);
1409             return;
1410         }
1411     }
1412
1413     qWarning("QDBusConnectionPrivate::disconnectRelay called for a signal that was not found");
1414 }
1415
1416 QString QDBusConnectionPrivate::getNameOwner(const QString& name)
1417 {
1418     if (QDBusUtil::isValidUniqueConnectionName(name))
1419         return name;
1420     if (!connection || !QDBusUtil::isValidBusName(name))
1421         return QString();
1422
1423     QDBusMessage msg = QDBusMessage::methodCall(QLatin1String(DBUS_SERVICE_DBUS),
1424             QLatin1String(DBUS_PATH_DBUS), QLatin1String(DBUS_INTERFACE_DBUS),
1425             QLatin1String("GetNameOwner"));
1426     msg << name;
1427     QDBusMessage reply = sendWithReply(msg, QDBusConnection::NoUseEventLoop);
1428     if (!lastError.isValid() && reply.type() == QDBusMessage::ReplyMessage)
1429         return reply.first().toString();
1430     return QString();
1431 }
1432
1433 QDBusInterfacePrivate *
1434 QDBusConnectionPrivate::findInterface(const QString &service,
1435                                       const QString &path,
1436                                       const QString &interface)
1437 {
1438     // check if it's there first -- FIXME: add binding mode
1439     QDBusMetaObject *mo = 0;
1440     QString owner = getNameOwner(service);
1441     if (connection && !owner.isEmpty() && QDBusUtil::isValidObjectPath(path) &&
1442         (interface.isEmpty() || QDBusUtil::isValidInterfaceName(interface)))
1443         // always call here with the unique connection name
1444         mo = findMetaObject(owner, path, interface);
1445
1446     QDBusInterfacePrivate *p = new QDBusInterfacePrivate(QDBusConnection(name), this, owner, path, interface, mo);
1447
1448     if (!mo) {
1449         // invalid object
1450         p->isValid = false;
1451         p->lastError = lastError;
1452         if (!lastError.isValid()) {
1453             // try to determine why we couldn't get the data
1454             if (!connection)
1455                 p->lastError = QDBusError(QDBusError::Disconnected,
1456                                           QLatin1String("Not connected to D-Bus server"));
1457             else if (owner.isEmpty())
1458                 p->lastError = QDBusError(QDBusError::ServiceUnknown,
1459                                           QString(QLatin1String("Service %1 is unknown")).arg(service));
1460             else if (!QDBusUtil::isValidObjectPath(path))
1461                 p->lastError = QDBusError(QDBusError::InvalidArgs,
1462                                           QString(QLatin1String("Object path %1 is invalid")).arg(path));
1463             else if (!interface.isEmpty() && !QDBusUtil::isValidInterfaceName(interface))
1464                 p->lastError = QDBusError(QDBusError::InvalidArgs,
1465                                           QString(QLatin1String("Interface %1 is invalid")).arg(interface));
1466             else
1467                 p->lastError = QDBusError(QDBusError::Other, QLatin1String("Unknown error"));
1468         }
1469     }
1470
1471     return p;
1472 }
1473
1474 struct qdbus_Introspect
1475 {
1476     QString xml;
1477     inline void operator()(QDBusConnectionPrivate::ObjectTreeNode *node)
1478     { xml = qDBusIntrospectObject(node); }
1479 };
1480
1481 QDBusMetaObject *
1482 QDBusConnectionPrivate::findMetaObject(const QString &service, const QString &path,
1483                                        const QString &interface)
1484 {
1485     // service must be a unique connection name
1486     if (!interface.isEmpty()) {
1487         QReadLocker locker(&lock);
1488         QDBusMetaObject *mo = cachedMetaObjects.value(interface, 0);
1489         if (mo)
1490             return mo;
1491     }
1492     if (service == QString::fromUtf8(dbus_bus_get_unique_name(connection))) {
1493         // it's one of our own
1494         QWriteLocker locker(&lock);
1495         QDBusMetaObject *mo = 0;
1496         if (!interface.isEmpty())
1497             mo = cachedMetaObjects.value(interface, 0);
1498         if (mo)
1499             // maybe it got created when we switched from read to write lock
1500             return mo;
1501
1502         qdbus_Introspect apply;
1503         if (!applyForObject(&rootNode, path, apply)) {
1504             lastError = QDBusError(QDBusError::InvalidArgs,
1505                                    QString(QLatin1String("No object at %1")).arg(path));
1506             return 0;           // no object at path
1507         }
1508
1509         // release the lock and return
1510         return QDBusMetaObject::createMetaObject(interface, apply.xml, cachedMetaObjects, lastError);
1511     }
1512
1513     // not local: introspect the target object:
1514     QDBusMessage msg = QDBusMessage::methodCall(service, path,
1515                                                 QLatin1String(DBUS_INTERFACE_INTROSPECTABLE),
1516                                                 QLatin1String("Introspect"));
1517
1518
1519     QDBusMessage reply = sendWithReply(msg, QDBusConnection::NoUseEventLoop);
1520
1521     // it doesn't exist yet, we have to create it
1522     QWriteLocker locker(&lock);
1523     QDBusMetaObject *mo = 0;
1524     if (!interface.isEmpty())
1525         mo = cachedMetaObjects.value(interface, 0);
1526     if (mo)
1527         // maybe it got created when we switched from read to write lock
1528         return mo;
1529
1530     QString xml;
1531     if (reply.type() == QDBusMessage::ReplyMessage)
1532         // fetch the XML description
1533         xml = reply.first().toString();
1534     else {
1535         lastError = reply;
1536         if (reply.type() != QDBusMessage::ErrorMessage || lastError != QDBusError::UnknownMethod)
1537             return 0;           // error
1538     }
1539
1540     // release the lock and return
1541     return QDBusMetaObject::createMetaObject(interface, xml, cachedMetaObjects, lastError);
1542 }
1543
1544 void QDBusReplyWaiter::reply(const QDBusMessage &msg)
1545 {
1546     replyMsg = msg;
1547     QTimer::singleShot(0, this, SLOT(quit()));
1548 }
1549
1550 #include "qdbusconnection_p.moc"