ff6927ccfe3ad6524901ee535f00d063494adf50
[profile/ivi/qtbase.git] / src / dbus / qdbusintegrator.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtDBus module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qdbusintegrator_p.h"
43
44 #include <qcoreapplication.h>
45 #include <qdebug.h>
46 #include <qmetaobject.h>
47 #include <qobject.h>
48 #include <qsocketnotifier.h>
49 #include <qstringlist.h>
50 #include <qtimer.h>
51 #include <qthread.h>
52
53 #include "qdbusargument.h"
54 #include "qdbusconnection_p.h"
55 #include "qdbusconnectionmanager_p.h"
56 #include "qdbusinterface_p.h"
57 #include "qdbusmessage.h"
58 #include "qdbusmetatype.h"
59 #include "qdbusmetatype_p.h"
60 #include "qdbusabstractadaptor.h"
61 #include "qdbusabstractadaptor_p.h"
62 #include "qdbusutil_p.h"
63 #include "qdbusvirtualobject.h"
64 #include "qdbusmessage_p.h"
65 #include "qdbuscontext_p.h"
66 #include "qdbuspendingcall_p.h"
67
68 #include "qdbusthreaddebug_p.h"
69
70 #include <algorithm>
71
72 #ifndef QT_NO_DBUS
73
74 QT_BEGIN_NAMESPACE
75
76 static bool isDebugging;
77 #define qDBusDebug              if (!::isDebugging); else qDebug
78
79 Q_GLOBAL_STATIC_WITH_ARGS(const QString, orgFreedesktopDBusString, (QLatin1String(DBUS_SERVICE_DBUS)))
80
81 static inline QString dbusServiceString()
82 { return *orgFreedesktopDBusString(); }
83 static inline QString dbusInterfaceString()
84 {
85     // it's the same string, but just be sure
86     Q_ASSERT(*orgFreedesktopDBusString() == QLatin1String(DBUS_INTERFACE_DBUS));
87     return *orgFreedesktopDBusString();
88 }
89
90 static inline QDebug operator<<(QDebug dbg, const QThread *th)
91 {
92     dbg.nospace() << "QThread(ptr=" << (void*)th;
93     if (th && !th->objectName().isEmpty())
94         dbg.nospace() << ", name=" << th->objectName();
95     dbg.nospace() << ')';
96     return dbg.space();
97 }
98
99 #if QDBUS_THREAD_DEBUG
100 static inline QDebug operator<<(QDebug dbg, const QDBusConnectionPrivate *conn)
101 {
102     dbg.nospace() << "QDBusConnection("
103                   << "ptr=" << (void*)conn
104                   << ", name=" << conn->name
105                   << ", baseService=" << conn->baseService
106                   << ", thread=";
107     if (conn->thread() == QThread::currentThread())
108         dbg.nospace() << "same thread";
109     else
110         dbg.nospace() << conn->thread();
111     dbg.nospace() << ')';
112     return dbg.space();
113 }
114
115 Q_AUTOTEST_EXPORT void qdbusDefaultThreadDebug(int action, int condition, QDBusConnectionPrivate *conn)
116 {
117     qDBusDebug() << QThread::currentThread()
118                  << "QtDBus threading action" << action
119                  << (condition == QDBusLockerBase::BeforeLock ? "before lock" :
120                      condition == QDBusLockerBase::AfterLock ? "after lock" :
121                      condition == QDBusLockerBase::BeforeUnlock ? "before unlock" :
122                      condition == QDBusLockerBase::AfterUnlock ? "after unlock" :
123                      condition == QDBusLockerBase::BeforePost ? "before event posting" :
124                      condition == QDBusLockerBase::AfterPost ? "after event posting" :
125                      condition == QDBusLockerBase::BeforeDeliver ? "before event delivery" :
126                      condition == QDBusLockerBase::AfterDeliver ? "after event delivery" :
127                      condition == QDBusLockerBase::BeforeAcquire ? "before acquire" :
128                      condition == QDBusLockerBase::AfterAcquire ? "after acquire" :
129                      condition == QDBusLockerBase::BeforeRelease ? "before release" :
130                      condition == QDBusLockerBase::AfterRelease ? "after release" :
131                      "condition unknown")
132                  << "in connection" << conn;
133 }
134 Q_AUTOTEST_EXPORT qdbusThreadDebugFunc qdbusThreadDebug = 0;
135 #endif
136
137 typedef void (*QDBusSpyHook)(const QDBusMessage&);
138 typedef QVarLengthArray<QDBusSpyHook, 4> QDBusSpyHookList;
139 Q_GLOBAL_STATIC(QDBusSpyHookList, qDBusSpyHookList)
140
141 extern "C" {
142
143     // libdbus-1 callbacks
144
145 static bool qDBusRealAddTimeout(QDBusConnectionPrivate *d, DBusTimeout *timeout, int ms);
146 static dbus_bool_t qDBusAddTimeout(DBusTimeout *timeout, void *data)
147 {
148     Q_ASSERT(timeout);
149     Q_ASSERT(data);
150
151   //  qDebug("addTimeout %d", q_dbus_timeout_get_interval(timeout));
152
153     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
154
155     if (!q_dbus_timeout_get_enabled(timeout))
156         return true;
157
158     QDBusWatchAndTimeoutLocker locker(AddTimeoutAction, d);
159     if (QCoreApplication::instance() && QThread::currentThread() == d->thread()) {
160         // correct thread
161         return qDBusRealAddTimeout(d, timeout, q_dbus_timeout_get_interval(timeout));
162     } else {
163         // wrong thread: sync back
164         QDBusConnectionCallbackEvent *ev = new QDBusConnectionCallbackEvent;
165         ev->subtype = QDBusConnectionCallbackEvent::AddTimeout;
166         d->timeoutsPendingAdd.append(qMakePair(timeout, q_dbus_timeout_get_interval(timeout)));
167         d->postEventToThread(AddTimeoutAction, d, ev);
168         return true;
169     }
170 }
171
172 static bool qDBusRealAddTimeout(QDBusConnectionPrivate *d, DBusTimeout *timeout, int ms)
173 {
174     Q_ASSERT(d->timeouts.keys(timeout).isEmpty());
175
176     int timerId = d->startTimer(ms);
177     if (!timerId)
178         return false;
179
180     d->timeouts[timerId] = timeout;
181     return true;
182 }
183
184 static void qDBusRemoveTimeout(DBusTimeout *timeout, void *data)
185 {
186     Q_ASSERT(timeout);
187     Q_ASSERT(data);
188
189   //  qDebug("removeTimeout");
190
191     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
192
193     QDBusWatchAndTimeoutLocker locker(RemoveTimeoutAction, d);
194
195     // is it pending addition?
196     QDBusConnectionPrivate::PendingTimeoutList::iterator pit = d->timeoutsPendingAdd.begin();
197     while (pit != d->timeoutsPendingAdd.end()) {
198         if (pit->first == timeout)
199             pit = d->timeoutsPendingAdd.erase(pit);
200         else
201             ++pit;
202     }
203
204     // is it a running timer?
205     bool correctThread = QCoreApplication::instance() && QThread::currentThread() == d->thread();
206     QDBusConnectionPrivate::TimeoutHash::iterator it = d->timeouts.begin();
207     while (it != d->timeouts.end()) {
208         if (it.value() == timeout) {
209             if (correctThread) {
210                 // correct thread
211                 d->killTimer(it.key());
212             } else {
213                 // incorrect thread or no application, post an event for later
214                 QDBusConnectionCallbackEvent *ev = new QDBusConnectionCallbackEvent;
215                 ev->subtype = QDBusConnectionCallbackEvent::KillTimer;
216                 ev->timerId = it.key();
217                 d->postEventToThread(KillTimerAction, d, ev);
218             }
219             it = d->timeouts.erase(it);
220             break;
221         } else {
222             ++it;
223         }
224     }
225 }
226
227 static void qDBusToggleTimeout(DBusTimeout *timeout, void *data)
228 {
229     Q_ASSERT(timeout);
230     Q_ASSERT(data);
231
232     //qDebug("ToggleTimeout");
233
234     qDBusRemoveTimeout(timeout, data);
235     qDBusAddTimeout(timeout, data);
236 }
237
238 static bool qDBusRealAddWatch(QDBusConnectionPrivate *d, DBusWatch *watch, int flags, int fd);
239 static dbus_bool_t qDBusAddWatch(DBusWatch *watch, void *data)
240 {
241     Q_ASSERT(watch);
242     Q_ASSERT(data);
243
244     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
245
246     int flags = q_dbus_watch_get_flags(watch);
247     int fd = q_dbus_watch_get_fd(watch);
248
249     if (QCoreApplication::instance() && QThread::currentThread() == d->thread()) {
250         return qDBusRealAddWatch(d, watch, flags, fd);
251     } else {
252         QDBusConnectionCallbackEvent *ev = new QDBusConnectionCallbackEvent;
253         ev->subtype = QDBusConnectionCallbackEvent::AddWatch;
254         ev->watch = watch;
255         ev->fd = fd;
256         ev->extra = flags;
257         d->postEventToThread(AddWatchAction, d, ev);
258         return true;
259     }
260 }
261
262 static bool qDBusRealAddWatch(QDBusConnectionPrivate *d, DBusWatch *watch, int flags, int fd)
263 {
264     QDBusConnectionPrivate::Watcher watcher;
265
266     QDBusWatchAndTimeoutLocker locker(AddWatchAction, d);
267     if (flags & DBUS_WATCH_READABLE) {
268         //qDebug("addReadWatch %d", fd);
269         watcher.watch = watch;
270         if (QCoreApplication::instance()) {
271             watcher.read = new QSocketNotifier(fd, QSocketNotifier::Read, d);
272             watcher.read->setEnabled(q_dbus_watch_get_enabled(watch));
273             d->connect(watcher.read, SIGNAL(activated(int)), SLOT(socketRead(int)));
274         }
275     }
276     if (flags & DBUS_WATCH_WRITABLE) {
277         //qDebug("addWriteWatch %d", fd);
278         watcher.watch = watch;
279         if (QCoreApplication::instance()) {
280             watcher.write = new QSocketNotifier(fd, QSocketNotifier::Write, d);
281             watcher.write->setEnabled(q_dbus_watch_get_enabled(watch));
282             d->connect(watcher.write, SIGNAL(activated(int)), SLOT(socketWrite(int)));
283         }
284     }
285     d->watchers.insertMulti(fd, watcher);
286
287     return true;
288 }
289
290 static void qDBusRemoveWatch(DBusWatch *watch, void *data)
291 {
292     Q_ASSERT(watch);
293     Q_ASSERT(data);
294
295     //qDebug("remove watch");
296
297     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
298     int fd = q_dbus_watch_get_fd(watch);
299
300     QDBusWatchAndTimeoutLocker locker(RemoveWatchAction, d);
301     QDBusConnectionPrivate::WatcherHash::iterator i = d->watchers.find(fd);
302     while (i != d->watchers.end() && i.key() == fd) {
303         if (i.value().watch == watch) {
304             if (QCoreApplication::instance() && QThread::currentThread() == d->thread()) {
305                 // correct thread, delete the socket notifiers
306                 delete i.value().read;
307                 delete i.value().write;
308             } else {
309                 // incorrect thread or no application, use delete later
310                 if (i->read)
311                     i->read->deleteLater();
312                 if (i->write)
313                     i->write->deleteLater();
314             }
315             i = d->watchers.erase(i);
316         } else {
317             ++i;
318         }
319     }
320 }
321
322 static void qDBusRealToggleWatch(QDBusConnectionPrivate *d, DBusWatch *watch, int fd);
323 static void qDBusToggleWatch(DBusWatch *watch, void *data)
324 {
325     Q_ASSERT(watch);
326     Q_ASSERT(data);
327
328     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
329     int fd = q_dbus_watch_get_fd(watch);
330
331     if (QCoreApplication::instance() && QThread::currentThread() == d->thread()) {
332         qDBusRealToggleWatch(d, watch, fd);
333     } else {
334         QDBusConnectionCallbackEvent *ev = new QDBusConnectionCallbackEvent;
335         ev->subtype = QDBusConnectionCallbackEvent::ToggleWatch;
336         ev->watch = watch;
337         ev->fd = fd;
338         d->postEventToThread(ToggleWatchAction, d, ev);
339     }
340 }
341
342 static void qDBusRealToggleWatch(QDBusConnectionPrivate *d, DBusWatch *watch, int fd)
343 {
344     QDBusWatchAndTimeoutLocker locker(ToggleWatchAction, d);
345
346     QDBusConnectionPrivate::WatcherHash::iterator i = d->watchers.find(fd);
347     while (i != d->watchers.end() && i.key() == fd) {
348         if (i.value().watch == watch) {
349             bool enabled = q_dbus_watch_get_enabled(watch);
350             int flags = q_dbus_watch_get_flags(watch);
351
352             //qDebug("toggle watch %d to %d (write: %d, read: %d)", q_dbus_watch_get_fd(watch), enabled, flags & DBUS_WATCH_WRITABLE, flags & DBUS_WATCH_READABLE);
353
354             if (flags & DBUS_WATCH_READABLE && i.value().read)
355                 i.value().read->setEnabled(enabled);
356             if (flags & DBUS_WATCH_WRITABLE && i.value().write)
357                 i.value().write->setEnabled(enabled);
358             return;
359         }
360         ++i;
361     }
362 }
363
364 static void qDBusUpdateDispatchStatus(DBusConnection *connection, DBusDispatchStatus new_status, void *data)
365 {
366     Q_ASSERT(connection);
367     Q_UNUSED(connection);
368     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
369
370     static int slotId; // 0 is QObject::deleteLater()
371     if (!slotId) {
372         // it's ok to do this: there's no race condition because the store is atomic
373         // and we always set to the same value
374         slotId = QDBusConnectionPrivate::staticMetaObject.indexOfSlot("doDispatch()");
375     }
376
377     //qDBusDebug() << "Updating dispatcher status" << slotId;
378     if (new_status == DBUS_DISPATCH_DATA_REMAINS)
379         QDBusConnectionPrivate::staticMetaObject.method(slotId).
380             invoke(d, Qt::QueuedConnection);
381 }
382
383 static void qDBusNewConnection(DBusServer *server, DBusConnection *connection, void *data)
384 {
385     // ### We may want to separate the server from the QDBusConnectionPrivate
386     Q_ASSERT(server); Q_UNUSED(server);
387     Q_ASSERT(connection);
388     Q_ASSERT(data);
389
390     // keep the connection alive
391     q_dbus_connection_ref(connection);
392     QDBusConnectionPrivate *serverConnection = static_cast<QDBusConnectionPrivate *>(data);
393
394     QDBusConnectionPrivate *newConnection = new QDBusConnectionPrivate(serverConnection->parent());
395     QMutexLocker locker(&QDBusConnectionManager::instance()->mutex);
396     QDBusConnectionManager::instance()->setConnection(QLatin1String("QDBusServer-") + QString::number(reinterpret_cast<qulonglong>(newConnection)), newConnection);
397     serverConnection->serverConnectionNames << newConnection->name;
398
399     // setPeer does the error handling for us
400     QDBusErrorInternal error;
401     newConnection->setPeer(connection, error);
402
403     QDBusConnection retval = QDBusConnectionPrivate::q(newConnection);
404
405     // make QDBusServer emit the newConnection signal
406     serverConnection->serverConnection(retval);
407 }
408
409 } // extern "C"
410
411 static QByteArray buildMatchRule(const QString &service,
412                                  const QString &objectPath, const QString &interface,
413                                  const QString &member, const QStringList &argMatch, const QString & /*signature*/)
414 {
415     QString result = QLatin1String("type='signal',");
416     QString keyValue = QLatin1String("%1='%2',");
417
418     if (!service.isEmpty())
419         result += keyValue.arg(QLatin1String("sender"), service);
420     if (!objectPath.isEmpty())
421         result += keyValue.arg(QLatin1String("path"), objectPath);
422     if (!interface.isEmpty())
423         result += keyValue.arg(QLatin1String("interface"), interface);
424     if (!member.isEmpty())
425         result += keyValue.arg(QLatin1String("member"), member);
426
427     // add the argument string-matching now
428     if (!argMatch.isEmpty()) {
429         keyValue = QLatin1String("arg%1='%2',");
430         for (int i = 0; i < argMatch.count(); ++i)
431             if (!argMatch.at(i).isNull())
432                 result += keyValue.arg(i).arg(argMatch.at(i));
433     }
434
435     result.chop(1);             // remove ending comma
436     return result.toLatin1();
437 }
438
439 static bool findObject(const QDBusConnectionPrivate::ObjectTreeNode *root,
440                        const QString &fullpath, int &usedLength,
441                        QDBusConnectionPrivate::ObjectTreeNode &result)
442 {
443     if (!fullpath.compare(QLatin1String("/")) && root->obj) {
444         usedLength = 1;
445         result = *root;
446         return root;
447     }
448     int start = 0;
449     int length = fullpath.length();
450     if (fullpath.at(0) == QLatin1Char('/'))
451         start = 1;
452
453     // walk the object tree
454     QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator node = root;
455     while (start < length && node) {
456         if (node->flags & QDBusConnection::ExportChildObjects)
457             break;
458         if ((node->flags & QDBusConnectionPrivate::VirtualObject) && (node->flags & QDBusConnection::SubPath))
459             break;
460         int end = fullpath.indexOf(QLatin1Char('/'), start);
461         end = (end == -1 ? length : end);
462         QStringRef pathComponent(&fullpath, start, end - start);
463
464         QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator it =
465             std::lower_bound(node->children.constBegin(), node->children.constEnd(), pathComponent);
466         if (it != node->children.constEnd() && it->name == pathComponent)
467             // match
468             node = it;
469         else
470             node = 0;
471
472         start = end + 1;
473     }
474
475     // found our object
476     usedLength = (start > length ? length : start);
477     if (node) {
478         if (node->obj || !node->children.isEmpty())
479             result = *node;
480         else
481             // there really is no object here
482             // we're just looking at an unused space in the QVector
483             node = 0;
484     }
485     return node;
486 }
487
488 static QObject *findChildObject(const QDBusConnectionPrivate::ObjectTreeNode *root,
489                                 const QString &fullpath, int start)
490 {
491     int length = fullpath.length();
492
493     // any object in the tree can tell us to switch to its own object tree:
494     const QDBusConnectionPrivate::ObjectTreeNode *node = root;
495     if (node && node->flags & QDBusConnection::ExportChildObjects) {
496         QObject *obj = node->obj;
497
498         while (obj) {
499             if (start >= length)
500                 // we're at the correct level
501                 return obj;
502
503             int pos = fullpath.indexOf(QLatin1Char('/'), start);
504             pos = (pos == -1 ? length : pos);
505             QStringRef pathComponent(&fullpath, start, pos - start);
506
507             const QObjectList children = obj->children();
508
509             // find a child with the proper name
510             QObject *next = 0;
511             QObjectList::ConstIterator it = children.constBegin();
512             QObjectList::ConstIterator end = children.constEnd();
513             for ( ; it != end; ++it)
514                 if ((*it)->objectName() == pathComponent) {
515                     next = *it;
516                     break;
517                 }
518
519             if (!next)
520                 break;
521
522             obj = next;
523             start = pos + 1;
524         }
525     }
526
527     // object not found
528     return 0;
529 }
530
531 static bool shouldWatchService(const QString &service)
532 {
533     return !service.isEmpty() && !service.startsWith(QLatin1Char(':'));
534 }
535
536 extern Q_DBUS_EXPORT void qDBusAddSpyHook(QDBusSpyHook);
537 void qDBusAddSpyHook(QDBusSpyHook hook)
538 {
539     qDBusSpyHookList()->append(hook);
540 }
541
542 extern "C" {
543 static DBusHandlerResult
544 qDBusSignalFilter(DBusConnection *connection, DBusMessage *message, void *data)
545 {
546     Q_ASSERT(data);
547     Q_UNUSED(connection);
548     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
549     if (d->mode == QDBusConnectionPrivate::InvalidMode)
550         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
551
552     QDBusMessage amsg = QDBusMessagePrivate::fromDBusMessage(message, d->capabilities);
553     qDBusDebug() << d << "got message (signal):" << amsg;
554
555     return d->handleMessage(amsg) ?
556         DBUS_HANDLER_RESULT_HANDLED :
557         DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
558 }
559 }
560
561 bool QDBusConnectionPrivate::handleMessage(const QDBusMessage &amsg)
562 {
563     const QDBusSpyHookList *list = qDBusSpyHookList();
564     for (int i = 0; i < list->size(); ++i) {
565         qDBusDebug() << "calling the message spy hook";
566         (*(*list)[i])(amsg);
567     }
568
569     if (!ref.load())
570         return false;
571
572     switch (amsg.type()) {
573     case QDBusMessage::SignalMessage:
574         handleSignal(amsg);
575         // if there are any other filters in this DBusConnection,
576         // let them see the signal too
577         return false;
578     case QDBusMessage::MethodCallMessage:
579         handleObjectCall(amsg);
580         return true;
581     case QDBusMessage::ReplyMessage:
582     case QDBusMessage::ErrorMessage:
583     case QDBusMessage::InvalidMessage:
584         return false;           // we don't handle those here
585     }
586
587     return false;
588 }
589
590 static void huntAndDestroy(QObject *needle, QDBusConnectionPrivate::ObjectTreeNode &haystack)
591 {
592     QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator it = haystack.children.begin();
593     QDBusConnectionPrivate::ObjectTreeNode::DataList::Iterator end = haystack.children.end();
594     for ( ; it != end; ++it)
595         huntAndDestroy(needle, *it);
596
597     if (needle == haystack.obj) {
598         haystack.obj = 0;
599         haystack.flags = 0;
600     }
601 }
602
603 static void huntAndEmit(DBusConnection *connection, DBusMessage *msg,
604                         QObject *needle, const QDBusConnectionPrivate::ObjectTreeNode &haystack,
605                         bool isScriptable, bool isAdaptor, const QString &path = QString())
606 {
607     QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator it = haystack.children.constBegin();
608     QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator end = haystack.children.constEnd();
609     for ( ; it != end; ++it)
610         huntAndEmit(connection, msg, needle, *it, isScriptable, isAdaptor, path + QLatin1Char('/') + it->name);
611
612     if (needle == haystack.obj) {
613         // is this a signal we should relay?
614         if (isAdaptor && (haystack.flags & QDBusConnection::ExportAdaptors) == 0)
615             return;             // no: it comes from an adaptor and we're not exporting adaptors
616         else if (!isAdaptor) {
617             int mask = isScriptable
618                        ? QDBusConnection::ExportScriptableSignals
619                        : QDBusConnection::ExportNonScriptableSignals;
620             if ((haystack.flags & mask) == 0)
621                 return;         // signal was not exported
622         }
623
624         QByteArray p = path.toLatin1();
625         if (p.isEmpty())
626             p = "/";
627         qDBusDebug() << QThread::currentThread() << "emitting signal at" << p;
628         DBusMessage *msg2 = q_dbus_message_copy(msg);
629         q_dbus_message_set_path(msg2, p);
630         q_dbus_connection_send(connection, msg2, 0);
631         q_dbus_message_unref(msg2);
632     }
633 }
634
635 static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags,
636                     const QString &signature_, QVector<int> &metaTypes)
637 {
638     QByteArray msgSignature = signature_.toLatin1();
639
640     for (int idx = mo->methodCount() - 1 ; idx >= QObject::staticMetaObject.methodCount(); --idx) {
641         QMetaMethod mm = mo->method(idx);
642
643         // check access:
644         if (mm.access() != QMetaMethod::Public)
645             continue;
646
647         // check type:
648         if (mm.methodType() != QMetaMethod::Slot && mm.methodType() != QMetaMethod::Method)
649             continue;
650
651         // check name:
652         if (mm.name() != name)
653             continue;
654
655         int returnType = mm.returnType();
656         bool isAsync = qDBusCheckAsyncTag(mm.tag());
657         bool isScriptable = mm.attributes() & QMetaMethod::Scriptable;
658
659         // consistency check:
660         if (isAsync && returnType != QMetaType::Void)
661             continue;
662
663         int inputCount = qDBusParametersForMethod(mm, metaTypes);
664         if (inputCount == -1)
665             continue;           // problem parsing
666
667         metaTypes[0] = returnType;
668         bool hasMessage = false;
669         if (inputCount > 0 &&
670             metaTypes.at(inputCount) == QDBusMetaTypeId::message) {
671             // "no input parameters" is allowed as long as the message meta type is there
672             hasMessage = true;
673             --inputCount;
674         }
675
676         // try to match the parameters
677         int i;
678         QByteArray reconstructedSignature;
679         for (i = 1; i <= inputCount; ++i) {
680             const char *typeSignature = QDBusMetaType::typeToSignature( metaTypes.at(i) );
681             if (!typeSignature)
682                 break;          // invalid
683
684             reconstructedSignature += typeSignature;
685             if (!msgSignature.startsWith(reconstructedSignature))
686                 break;
687         }
688
689         if (reconstructedSignature != msgSignature)
690             continue;           // we didn't match them all
691
692         if (hasMessage)
693             ++i;
694
695         // make sure that the output parameters have signatures too
696         if (returnType != QMetaType::UnknownType && returnType != QMetaType::Void && QDBusMetaType::typeToSignature(returnType) == 0)
697             continue;
698
699         bool ok = true;
700         for (int j = i; ok && j < metaTypes.count(); ++j)
701             if (QDBusMetaType::typeToSignature(metaTypes.at(i)) == 0)
702                 ok = false;
703         if (!ok)
704             continue;
705
706         // consistency check:
707         if (isAsync && metaTypes.count() > i + 1)
708             continue;
709
710         if (mm.methodType() == QMetaMethod::Slot) {
711             if (isScriptable && (flags & QDBusConnection::ExportScriptableSlots) == 0)
712                 continue;           // scriptable slots not exported
713             if (!isScriptable && (flags & QDBusConnection::ExportNonScriptableSlots) == 0)
714                 continue;           // non-scriptable slots not exported
715         } else {
716             if (isScriptable && (flags & QDBusConnection::ExportScriptableInvokables) == 0)
717                 continue;           // scriptable invokables not exported
718             if (!isScriptable && (flags & QDBusConnection::ExportNonScriptableInvokables) == 0)
719                 continue;           // non-scriptable invokables not exported
720         }
721
722         // if we got here, this slot matched
723         return idx;
724     }
725
726     // no slot matched
727     return -1;
728 }
729
730 static QDBusCallDeliveryEvent * const DIRECT_DELIVERY = (QDBusCallDeliveryEvent *)1;
731
732 QDBusCallDeliveryEvent* QDBusConnectionPrivate::prepareReply(QDBusConnectionPrivate *target,
733                                                              QObject *object, int idx,
734                                                              const QVector<int> &metaTypes,
735                                                              const QDBusMessage &msg)
736 {
737     Q_ASSERT(object);
738     Q_UNUSED(object);
739
740     int n = metaTypes.count() - 1;
741     if (metaTypes[n] == QDBusMetaTypeId::message)
742         --n;
743
744     if (msg.arguments().count() < n)
745         return 0;               // too few arguments
746
747     // check that types match
748     for (int i = 0; i < n; ++i)
749         if (metaTypes.at(i + 1) != msg.arguments().at(i).userType() &&
750             msg.arguments().at(i).userType() != qMetaTypeId<QDBusArgument>())
751             return 0;           // no match
752
753     // we can deliver
754     // prepare for the call
755     if (target == object)
756         return DIRECT_DELIVERY;
757     return new QDBusCallDeliveryEvent(QDBusConnection(target), idx, target, msg, metaTypes);
758 }
759
760 void QDBusConnectionPrivate::activateSignal(const QDBusConnectionPrivate::SignalHook& hook,
761                                             const QDBusMessage &msg)
762 {
763     // This is called by QDBusConnectionPrivate::handleSignal to deliver a signal
764     // that was received from D-Bus
765     //
766     // Signals are delivered to slots if the parameters match
767     // Slots can have less parameters than there are on the message
768     // Slots can optionally have one final parameter that is a QDBusMessage
769     // Slots receive read-only copies of the message (i.e., pass by value or by const-ref)
770     QDBusCallDeliveryEvent *call = prepareReply(this, hook.obj, hook.midx, hook.params, msg);
771     if (call == DIRECT_DELIVERY) {
772         // short-circuit delivery
773         Q_ASSERT(this == hook.obj);
774         deliverCall(this, 0, msg, hook.params, hook.midx);
775         return;
776     }
777     if (call)
778         postEventToThread(ActivateSignalAction, hook.obj, call);
779 }
780
781 bool QDBusConnectionPrivate::activateCall(QObject* object, int flags, const QDBusMessage &msg)
782 {
783     // This is called by QDBusConnectionPrivate::handleObjectCall to place a call
784     // to a slot on the object.
785     //
786     // The call is delivered to the first slot that matches the following conditions:
787     //  - has the same name as the message's target member
788     //  - ALL of the message's types are found in slot's parameter list
789     //  - optionally has one more parameter of type QDBusMessage
790     // If none match, then the slot of the same name as the message target and with
791     // the first type of QDBusMessage is delivered.
792     //
793     // The D-Bus specification requires that all MethodCall messages be replied to, unless the
794     // caller specifically waived this requirement. This means that we inspect if the user slot
795     // generated a reply and, if it didn't, we will. Obviously, if the user slot doesn't take a
796     // QDBusMessage parameter, it cannot generate a reply.
797     //
798     // When a return message is generated, the slot's return type, if any, will be placed
799     // in the message's first position. If there are non-const reference parameters to the
800     // slot, they must appear at the end and will be placed in the subsequent message
801     // positions.
802
803     static const char cachePropertyName[] = "_qdbus_slotCache";
804
805     if (!object)
806         return false;
807
808 #ifndef QT_NO_PROPERTIES
809     Q_ASSERT_X(QThread::currentThread() == object->thread(),
810                "QDBusConnection: internal threading error",
811                "function called for an object that is in another thread!!");
812
813     QDBusSlotCache slotCache =
814         qvariant_cast<QDBusSlotCache>(object->property(cachePropertyName));
815     QString cacheKey = msg.member(), signature = msg.signature();
816     if (!signature.isEmpty()) {
817         cacheKey.reserve(cacheKey.length() + 1 + signature.length());
818         cacheKey += QLatin1Char('.');
819         cacheKey += signature;
820     }
821
822     QDBusSlotCache::Hash::ConstIterator cacheIt = slotCache.hash.constFind(cacheKey);
823     while (cacheIt != slotCache.hash.constEnd() && cacheIt->flags != flags &&
824            cacheIt.key() == cacheKey)
825         ++cacheIt;
826     if (cacheIt == slotCache.hash.constEnd() || cacheIt.key() != cacheKey)
827     {
828         // not cached, analyze the meta object
829         const QMetaObject *mo = object->metaObject();
830         QByteArray memberName = msg.member().toUtf8();
831
832         // find a slot that matches according to the rules above
833         QDBusSlotCache::Data slotData;
834         slotData.flags = flags;
835         slotData.slotIdx = ::findSlot(mo, memberName, flags, msg.signature(), slotData.metaTypes);
836         if (slotData.slotIdx == -1) {
837             // ### this is where we want to add the connection as an arg too
838             // try with no parameters, but with a QDBusMessage
839             slotData.slotIdx = ::findSlot(mo, memberName, flags, QString(), slotData.metaTypes);
840             if (slotData.metaTypes.count() != 2 ||
841                 slotData.metaTypes.at(1) != QDBusMetaTypeId::message) {
842                 // not found
843                 // save the negative lookup
844                 slotData.slotIdx = -1;
845                 slotData.metaTypes.clear();
846                 slotCache.hash.insert(cacheKey, slotData);
847                 object->setProperty(cachePropertyName, QVariant::fromValue(slotCache));
848                 return false;
849             }
850         }
851
852         // save to the cache
853         slotCache.hash.insert(cacheKey, slotData);
854         object->setProperty(cachePropertyName, QVariant::fromValue(slotCache));
855
856         // found the slot to be called
857         deliverCall(object, flags, msg, slotData.metaTypes, slotData.slotIdx);
858         return true;
859     } else if (cacheIt->slotIdx == -1) {
860         // negative cache
861         return false;
862     } else {
863         // use the cache
864         deliverCall(object, flags, msg, cacheIt->metaTypes, cacheIt->slotIdx);
865         return true;
866     }
867 #endif // QT_NO_PROPERTIES
868     return false;
869 }
870
871 void QDBusConnectionPrivate::deliverCall(QObject *object, int /*flags*/, const QDBusMessage &msg,
872                                          const QVector<int> &metaTypes, int slotIdx)
873 {
874     Q_ASSERT_X(!object || QThread::currentThread() == object->thread(),
875                "QDBusConnection: internal threading error",
876                "function called for an object that is in another thread!!");
877
878     QVarLengthArray<void *, 10> params;
879     params.reserve(metaTypes.count());
880
881     QVariantList auxParameters;
882     // let's create the parameter list
883
884     // first one is the return type -- add it below
885     params.append(0);
886
887     // add the input parameters
888     int i;
889     int pCount = qMin(msg.arguments().count(), metaTypes.count() - 1);
890     for (i = 1; i <= pCount; ++i) {
891         int id = metaTypes[i];
892         if (id == QDBusMetaTypeId::message)
893             break;
894
895         const QVariant &arg = msg.arguments().at(i - 1);
896         if (arg.userType() == id)
897             // no conversion needed
898             params.append(const_cast<void *>(arg.constData()));
899         else if (arg.userType() == qMetaTypeId<QDBusArgument>()) {
900             // convert to what the function expects
901             void *null = 0;
902             auxParameters.append(QVariant(id, null));
903
904             const QDBusArgument &in =
905                 *reinterpret_cast<const QDBusArgument *>(arg.constData());
906             QVariant &out = auxParameters[auxParameters.count() - 1];
907
908             if (!QDBusMetaType::demarshall(in, out.userType(), out.data()))
909                 qFatal("Internal error: demarshalling function for type '%s' (%d) failed!",
910                        out.typeName(), out.userType());
911
912             params.append(const_cast<void *>(out.constData()));
913         } else {
914             qFatal("Internal error: got invalid meta type %d (%s) "
915                    "when trying to convert to meta type %d (%s)",
916                    arg.userType(), QMetaType::typeName(arg.userType()),
917                    id, QMetaType::typeName(id));
918         }
919     }
920
921     if (metaTypes.count() > i && metaTypes[i] == QDBusMetaTypeId::message) {
922         params.append(const_cast<void*>(static_cast<const void*>(&msg)));
923         ++i;
924     }
925
926     // output arguments
927     QVariantList outputArgs;
928     void *null = 0;
929     if (metaTypes[0] != QMetaType::Void && metaTypes[0] != QMetaType::UnknownType) {
930         QVariant arg(metaTypes[0], null);
931         outputArgs.append( arg );
932         params[0] = const_cast<void*>(outputArgs.at( outputArgs.count() - 1 ).constData());
933     }
934     for ( ; i < metaTypes.count(); ++i) {
935         QVariant arg(metaTypes[i], null);
936         outputArgs.append( arg );
937         params.append(const_cast<void*>(outputArgs.at( outputArgs.count() - 1 ).constData()));
938     }
939
940     // make call:
941     bool fail;
942     if (!object) {
943         fail = true;
944     } else {
945         // FIXME: save the old sender!
946         QDBusContextPrivate context(QDBusConnection(this), msg);
947         QDBusContextPrivate *old = QDBusContextPrivate::set(object, &context);
948         QDBusConnectionPrivate::setSender(this);
949
950         QPointer<QObject> ptr = object;
951         fail = object->qt_metacall(QMetaObject::InvokeMetaMethod,
952                                    slotIdx, params.data()) >= 0;
953         QDBusConnectionPrivate::setSender(0);
954         // the object might be deleted in the slot
955         if (!ptr.isNull())
956             QDBusContextPrivate::set(object, old);
957     }
958
959     // do we create a reply? Only if the caller is waiting for a reply and one hasn't been sent
960     // yet.
961     if (msg.isReplyRequired() && !msg.isDelayedReply()) {
962         if (!fail) {
963             // normal reply
964             qDBusDebug() << this << "Automatically sending reply:" << outputArgs;
965             send(msg.createReply(outputArgs));
966         } else {
967             // generate internal error
968             qWarning("Internal error: Failed to deliver message");
969             send(msg.createErrorReply(QDBusError::InternalError,
970                                       QLatin1String("Failed to deliver message")));
971         }
972     }
973
974     return;
975 }
976
977 extern bool qDBusInitThreads();
978
979 QDBusConnectionPrivate::QDBusConnectionPrivate(QObject *p)
980     : QObject(p), ref(1), capabilities(0), mode(InvalidMode), connection(0), server(0), busService(0),
981       watchAndTimeoutLock(QMutex::Recursive),
982       rootNode(QString(QLatin1Char('/')))
983 {
984     static const bool threads = q_dbus_threads_init_default();
985     static const int debugging = qgetenv("QDBUS_DEBUG").toInt();
986     ::isDebugging = debugging;
987     Q_UNUSED(threads)
988     Q_UNUSED(debugging)
989
990 #ifdef QDBUS_THREAD_DEBUG
991     if (debugging > 1)
992         qdbusThreadDebug = qdbusDefaultThreadDebug;
993 #endif
994
995     QDBusMetaTypeId::init();
996
997     rootNode.flags = 0;
998
999     // prepopulate watchedServices:
1000     // we know that the owner of org.freedesktop.DBus is itself
1001     watchedServices.insert(dbusServiceString(), WatchedServiceData(dbusServiceString(), 1));
1002
1003     // prepopulate matchRefCounts:
1004     // we know that org.freedesktop.DBus will never change owners
1005     matchRefCounts.insert("type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='org.freedesktop.DBus'", 1);
1006 }
1007
1008 QDBusConnectionPrivate::~QDBusConnectionPrivate()
1009 {
1010     if (thread() && thread() != QThread::currentThread())
1011         qWarning("QDBusConnection(name=\"%s\")'s last reference in not in its creation thread! "
1012                  "Timer and socket errors will follow and the program will probably crash",
1013                  qPrintable(name));
1014
1015     closeConnection();
1016     rootNode.children.clear();  // free resources
1017     qDeleteAll(cachedMetaObjects);
1018
1019     if (server)
1020         q_dbus_server_unref(server);
1021     if (connection)
1022         q_dbus_connection_unref(connection);
1023
1024     connection = 0;
1025     server = 0;
1026 }
1027
1028 void QDBusConnectionPrivate::deleteYourself()
1029 {
1030     if (thread() && thread() != QThread::currentThread()) {
1031         // last reference dropped while not in the correct thread
1032         // ask the correct thread to delete
1033
1034         // note: since we're posting an event to another thread, we
1035         // must consider deleteLater() to take effect immediately
1036         deleteLater();
1037     } else {
1038         delete this;
1039     }
1040 }
1041
1042 void QDBusConnectionPrivate::closeConnection()
1043 {
1044     QDBusWriteLocker locker(CloseConnectionAction, this);
1045     ConnectionMode oldMode = mode;
1046     mode = InvalidMode; // prevent reentrancy
1047     baseService.clear();
1048
1049     if (server)
1050         q_dbus_server_disconnect(server);
1051
1052     if (oldMode == ClientMode || oldMode == PeerMode) {
1053         if (connection) {
1054             q_dbus_connection_close(connection);
1055             // send the "close" message
1056             while (q_dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS)
1057                 ;
1058         }
1059     }
1060     qDBusDebug() << this << "Disconnected";
1061 }
1062
1063 void QDBusConnectionPrivate::checkThread()
1064 {
1065     if (!thread()) {
1066         if (QCoreApplication::instance())
1067             moveToThread(QCoreApplication::instance()->thread());
1068         else
1069             qWarning("The thread that had QDBusConnection('%s') has died and there is no main thread",
1070                      qPrintable(name));
1071     }
1072 }
1073
1074 bool QDBusConnectionPrivate::handleError(const QDBusErrorInternal &error)
1075 {
1076     if (!error)
1077         return false;           // no error
1078
1079     //lock.lockForWrite();
1080     lastError = error;
1081     //lock.unlock();
1082     return true;
1083 }
1084
1085 void QDBusConnectionPrivate::timerEvent(QTimerEvent *e)
1086 {
1087     {
1088         QDBusWatchAndTimeoutLocker locker(TimerEventAction, this);
1089         DBusTimeout *timeout = timeouts.value(e->timerId(), 0);
1090         if (timeout)
1091             q_dbus_timeout_handle(timeout);
1092     }
1093
1094     doDispatch();
1095 }
1096
1097 void QDBusConnectionPrivate::customEvent(QEvent *e)
1098 {
1099     Q_ASSERT(e->type() == QEvent::User);
1100
1101     QDBusConnectionCallbackEvent *ev = static_cast<QDBusConnectionCallbackEvent *>(e);
1102     QDBusLockerBase::reportThreadAction(int(AddTimeoutAction) + int(ev->subtype),
1103                                         QDBusLockerBase::BeforeDeliver, this);
1104     switch (ev->subtype)
1105     {
1106     case QDBusConnectionCallbackEvent::AddTimeout: {
1107         QDBusWatchAndTimeoutLocker locker(RealAddTimeoutAction, this);
1108         while (!timeoutsPendingAdd.isEmpty()) {
1109             QPair<DBusTimeout *, int> entry = timeoutsPendingAdd.takeFirst();
1110             qDBusRealAddTimeout(this, entry.first, entry.second);
1111         }
1112         break;
1113     }
1114
1115     case QDBusConnectionCallbackEvent::KillTimer:
1116         killTimer(ev->timerId);
1117         break;
1118
1119     case QDBusConnectionCallbackEvent::AddWatch:
1120         qDBusRealAddWatch(this, ev->watch, ev->extra, ev->fd);
1121         break;
1122
1123     case QDBusConnectionCallbackEvent::ToggleWatch:
1124         qDBusRealToggleWatch(this, ev->watch, ev->fd);
1125         break;
1126     }
1127     QDBusLockerBase::reportThreadAction(int(AddTimeoutAction) + int(ev->subtype),
1128                                         QDBusLockerBase::AfterDeliver, this);
1129 }
1130
1131 void QDBusConnectionPrivate::doDispatch()
1132 {
1133     QDBusDispatchLocker locker(DoDispatchAction, this);
1134     if (mode == ClientMode || mode == PeerMode)
1135         while (q_dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS) ;
1136 }
1137
1138 void QDBusConnectionPrivate::socketRead(int fd)
1139 {
1140     QVarLengthArray<DBusWatch *, 2> pendingWatches;
1141
1142     {
1143         QDBusWatchAndTimeoutLocker locker(SocketReadAction, this);
1144         WatcherHash::ConstIterator it = watchers.constFind(fd);
1145         while (it != watchers.constEnd() && it.key() == fd) {
1146             if (it->watch && it->read && it->read->isEnabled())
1147                 pendingWatches.append(it.value().watch);
1148             ++it;
1149         }
1150     }
1151
1152     for (int i = 0; i < pendingWatches.size(); ++i)
1153         if (!q_dbus_watch_handle(pendingWatches[i], DBUS_WATCH_READABLE))
1154             qDebug("OUT OF MEM");
1155     doDispatch();
1156 }
1157
1158 void QDBusConnectionPrivate::socketWrite(int fd)
1159 {
1160     QVarLengthArray<DBusWatch *, 2> pendingWatches;
1161
1162     {
1163         QDBusWatchAndTimeoutLocker locker(SocketWriteAction, this);
1164         WatcherHash::ConstIterator it = watchers.constFind(fd);
1165         while (it != watchers.constEnd() && it.key() == fd) {
1166             if (it->watch && it->write && it->write->isEnabled())
1167                 pendingWatches.append(it.value().watch);
1168             ++it;
1169         }
1170     }
1171
1172     for (int i = 0; i < pendingWatches.size(); ++i)
1173         if (!q_dbus_watch_handle(pendingWatches[i], DBUS_WATCH_WRITABLE))
1174             qDebug("OUT OF MEM");
1175 }
1176
1177 void QDBusConnectionPrivate::objectDestroyed(QObject *obj)
1178 {
1179     QDBusWriteLocker locker(ObjectDestroyedAction, this);
1180     huntAndDestroy(obj, rootNode);
1181
1182     SignalHookHash::iterator sit = signalHooks.begin();
1183     while (sit != signalHooks.end()) {
1184         if (static_cast<QObject *>(sit.value().obj) == obj)
1185             sit = disconnectSignal(sit);
1186         else
1187             ++sit;
1188     }
1189
1190     obj->disconnect(this);
1191 }
1192
1193 void QDBusConnectionPrivate::relaySignal(QObject *obj, const QMetaObject *mo, int signalId,
1194                                          const QVariantList &args)
1195 {
1196     QString interface = qDBusInterfaceFromMetaObject(mo);
1197
1198     QMetaMethod mm = mo->method(signalId);
1199     QByteArray memberName = mm.name();
1200
1201     // check if it's scriptable
1202     bool isScriptable = mm.attributes() & QMetaMethod::Scriptable;
1203     bool isAdaptor = false;
1204     for ( ; mo; mo = mo->superClass())
1205         if (mo == &QDBusAbstractAdaptor::staticMetaObject) {
1206             isAdaptor = true;
1207             break;
1208         }
1209
1210     QDBusReadLocker locker(RelaySignalAction, this);
1211     QDBusMessage message = QDBusMessage::createSignal(QLatin1String("/"), interface,
1212                                                       QLatin1String(memberName));
1213     QDBusMessagePrivate::setParametersValidated(message, true);
1214     message.setArguments(args);
1215     QDBusError error;
1216     DBusMessage *msg = QDBusMessagePrivate::toDBusMessage(message, capabilities, &error);
1217     if (!msg) {
1218         qWarning("QDBusConnection: Could not emit signal %s.%s: %s", qPrintable(interface), memberName.constData(),
1219                  qPrintable(error.message()));
1220         lastError = error;
1221         return;
1222     }
1223
1224     //qDBusDebug() << "Emitting signal" << message;
1225     //qDBusDebug() << "for paths:";
1226     q_dbus_message_set_no_reply(msg, true); // the reply would not be delivered to anything
1227     huntAndEmit(connection, msg, obj, rootNode, isScriptable, isAdaptor);
1228     q_dbus_message_unref(msg);
1229 }
1230
1231 void QDBusConnectionPrivate::serviceOwnerChangedNoLock(const QString &name,
1232                                                        const QString &oldOwner, const QString &newOwner)
1233 {
1234     Q_UNUSED(oldOwner);
1235 //    QDBusWriteLocker locker(UpdateSignalHookOwnerAction, this);
1236     WatchedServicesHash::Iterator it = watchedServices.find(name);
1237     if (it == watchedServices.end())
1238         return;
1239     if (oldOwner != it->owner)
1240         qWarning("QDBusConnection: name '%s' had owner '%s' but we thought it was '%s'",
1241                  qPrintable(name), qPrintable(oldOwner), qPrintable(it->owner));
1242
1243     qDBusDebug() << this << "Updating name" << name << "from" << oldOwner << "to" << newOwner;
1244     it->owner = newOwner;
1245 }
1246
1247 int QDBusConnectionPrivate::findSlot(QObject* obj, const QByteArray &normalizedName,
1248                                      QVector<int> &params)
1249 {
1250     int midx = obj->metaObject()->indexOfMethod(normalizedName);
1251     if (midx == -1)
1252         return -1;
1253
1254     int inputCount = qDBusParametersForMethod(obj->metaObject()->method(midx), params);
1255     if ( inputCount == -1 || inputCount + 1 != params.count() )
1256         return -1;              // failed to parse or invalid arguments or output arguments
1257
1258     return midx;
1259 }
1260
1261 bool QDBusConnectionPrivate::prepareHook(QDBusConnectionPrivate::SignalHook &hook, QString &key,
1262                                          const QString &service,
1263                                          const QString &path, const QString &interface, const QString &name,
1264                                          const QStringList &argMatch,
1265                                          QObject *receiver, const char *signal, int minMIdx,
1266                                          bool buildSignature)
1267 {
1268     QByteArray normalizedName = signal + 1;
1269     hook.midx = findSlot(receiver, signal + 1, hook.params);
1270     if (hook.midx == -1) {
1271         normalizedName = QMetaObject::normalizedSignature(signal + 1);
1272         hook.midx = findSlot(receiver, normalizedName, hook.params);
1273     }
1274     if (hook.midx < minMIdx) {
1275         if (hook.midx == -1)
1276         {}
1277         return false;
1278     }
1279
1280     hook.service = service;
1281     hook.path = path;
1282     hook.obj = receiver;
1283     hook.argumentMatch = argMatch;
1284
1285     // build the D-Bus signal name and signature
1286     // This should not happen for QDBusConnection::connect, use buildSignature here, since
1287     // QDBusConnection::connect passes false and everything else uses true
1288     QString mname = name;
1289     if (buildSignature && mname.isNull()) {
1290         normalizedName.truncate(normalizedName.indexOf('('));
1291         mname = QString::fromUtf8(normalizedName);
1292     }
1293     key = mname;
1294     key.reserve(interface.length() + 1 + mname.length());
1295     key += QLatin1Char(':');
1296     key += interface;
1297
1298     if (buildSignature) {
1299         hook.signature.clear();
1300         for (int i = 1; i < hook.params.count(); ++i)
1301             if (hook.params.at(i) != QDBusMetaTypeId::message)
1302                 hook.signature += QLatin1String( QDBusMetaType::typeToSignature( hook.params.at(i) ) );
1303     }
1304
1305     hook.matchRule = buildMatchRule(service, path, interface, mname, argMatch, hook.signature);
1306     return true;                // connect to this signal
1307 }
1308
1309 void QDBusConnectionPrivate::sendError(const QDBusMessage &msg, QDBusError::ErrorType code)
1310 {
1311     if (code == QDBusError::UnknownMethod) {
1312         QString interfaceMsg;
1313         if (msg.interface().isEmpty())
1314             interfaceMsg = QLatin1String("any interface");
1315         else
1316             interfaceMsg = QString::fromLatin1("interface '%1'").arg(msg.interface());
1317
1318         send(msg.createErrorReply(code,
1319                                   QString::fromLatin1("No such method '%1' in %2 at object path '%3' "
1320                                                       "(signature '%4')")
1321                                   .arg(msg.member(), interfaceMsg, msg.path(), msg.signature())));
1322     } else if (code == QDBusError::UnknownInterface) {
1323         send(msg.createErrorReply(QDBusError::UnknownInterface,
1324                                   QString::fromLatin1("No such interface '%1' at object path '%2'")
1325                                   .arg(msg.interface(), msg.path())));
1326     } else if (code == QDBusError::UnknownObject) {
1327         send(msg.createErrorReply(QDBusError::UnknownObject,
1328                                   QString::fromLatin1("No such object path '%1'").arg(msg.path())));
1329     }
1330 }
1331
1332 bool QDBusConnectionPrivate::activateInternalFilters(const ObjectTreeNode &node,
1333                                                      const QDBusMessage &msg)
1334 {
1335     // object may be null
1336     const QString interface = msg.interface();
1337
1338     if (interface.isEmpty() || interface == QLatin1String(DBUS_INTERFACE_INTROSPECTABLE)) {
1339         if (msg.member() == QLatin1String("Introspect") && msg.signature().isEmpty()) {
1340             //qDebug() << "QDBusConnectionPrivate::activateInternalFilters introspect" << msg.d_ptr->msg;
1341             QDBusMessage reply = msg.createReply(qDBusIntrospectObject(node, msg.path()));
1342             send(reply);
1343             return true;
1344         }
1345
1346         if (!interface.isEmpty()) {
1347             sendError(msg, QDBusError::UnknownMethod);
1348             return true;
1349         }
1350     }
1351
1352     if (node.obj && (interface.isEmpty() ||
1353                      interface == QLatin1String(DBUS_INTERFACE_PROPERTIES))) {
1354         //qDebug() << "QDBusConnectionPrivate::activateInternalFilters properties" << msg.d_ptr->msg;
1355         if (msg.member() == QLatin1String("Get") && msg.signature() == QLatin1String("ss")) {
1356             QDBusMessage reply = qDBusPropertyGet(node, msg);
1357             send(reply);
1358             return true;
1359         } else if (msg.member() == QLatin1String("Set") && msg.signature() == QLatin1String("ssv")) {
1360             QDBusMessage reply = qDBusPropertySet(node, msg);
1361             send(reply);
1362             return true;
1363         } else if (msg.member() == QLatin1String("GetAll") && msg.signature() == QLatin1String("s")) {
1364             QDBusMessage reply = qDBusPropertyGetAll(node, msg);
1365             send(reply);
1366             return true;
1367         }
1368
1369         if (!interface.isEmpty()) {
1370             sendError(msg, QDBusError::UnknownMethod);
1371             return true;
1372         }
1373     }
1374
1375     return false;
1376 }
1377
1378 void QDBusConnectionPrivate::activateObject(ObjectTreeNode &node, const QDBusMessage &msg,
1379                                             int pathStartPos)
1380 {
1381     // This is called by QDBusConnectionPrivate::handleObjectCall to place a call to a slot
1382     // on the object.
1383     //
1384     // The call is routed through the adaptor sub-objects if we have any
1385
1386     // object may be null
1387
1388     if (node.flags & QDBusConnectionPrivate::VirtualObject) {
1389         if (node.treeNode->handleMessage(msg, q(this))) {
1390             return;
1391         } else {
1392             if (activateInternalFilters(node, msg))
1393                 return;
1394         }
1395     }
1396
1397     if (pathStartPos != msg.path().length()) {
1398         node.flags &= ~QDBusConnection::ExportAllSignals;
1399         node.obj = findChildObject(&node, msg.path(), pathStartPos);
1400         if (!node.obj) {
1401             sendError(msg, QDBusError::UnknownObject);
1402             return;
1403         }
1404     }
1405
1406     QDBusAdaptorConnector *connector;
1407     if (node.flags & QDBusConnection::ExportAdaptors &&
1408         (connector = qDBusFindAdaptorConnector(node.obj))) {
1409         int newflags = node.flags | QDBusConnection::ExportAllSlots;
1410
1411         if (msg.interface().isEmpty()) {
1412             // place the call in all interfaces
1413             // let the first one that handles it to work
1414             QDBusAdaptorConnector::AdaptorMap::ConstIterator it =
1415                 connector->adaptors.constBegin();
1416             QDBusAdaptorConnector::AdaptorMap::ConstIterator end =
1417                 connector->adaptors.constEnd();
1418
1419             for ( ; it != end; ++it)
1420                 if (activateCall(it->adaptor, newflags, msg))
1421                     return;
1422         } else {
1423             // check if we have an interface matching the name that was asked:
1424             QDBusAdaptorConnector::AdaptorMap::ConstIterator it;
1425             it = std::lower_bound(connector->adaptors.constBegin(), connector->adaptors.constEnd(),
1426                                   msg.interface());
1427             if (it != connector->adaptors.constEnd() && msg.interface() == QLatin1String(it->interface)) {
1428                 if (!activateCall(it->adaptor, newflags, msg))
1429                     sendError(msg, QDBusError::UnknownMethod);
1430                 return;
1431             }
1432         }
1433     }
1434
1435     // no adaptors matched or were exported
1436     // try our standard filters
1437     if (activateInternalFilters(node, msg))
1438         return;                 // internal filters have already run or an error has been sent
1439
1440     // try the object itself:
1441     if (node.flags & (QDBusConnection::ExportScriptableSlots|QDBusConnection::ExportNonScriptableSlots) ||
1442         node.flags & (QDBusConnection::ExportScriptableInvokables|QDBusConnection::ExportNonScriptableInvokables)) {
1443         bool interfaceFound = true;
1444         if (!msg.interface().isEmpty())
1445             interfaceFound = qDBusInterfaceInObject(node.obj, msg.interface());
1446
1447         if (interfaceFound) {
1448             if (!activateCall(node.obj, node.flags, msg))
1449                 sendError(msg, QDBusError::UnknownMethod);
1450             return;
1451         }
1452     }
1453
1454     // nothing matched, send an error code
1455     if (msg.interface().isEmpty())
1456         sendError(msg, QDBusError::UnknownMethod);
1457     else
1458         sendError(msg, QDBusError::UnknownInterface);
1459 }
1460
1461 void QDBusConnectionPrivate::handleObjectCall(const QDBusMessage &msg)
1462 {
1463     // if the msg is external, we were called from inside doDispatch
1464     // that means the dispatchLock mutex is locked
1465     // must not call out to user code in that case
1466     //
1467     // however, if the message is internal, handleMessage was called
1468     // directly and no lock is in place. We can therefore call out to
1469     // user code, if necessary
1470     ObjectTreeNode result;
1471     int usedLength;
1472     QThread *objThread = 0;
1473     QSemaphore sem;
1474     bool semWait;
1475
1476     {
1477         QDBusReadLocker locker(HandleObjectCallAction, this);
1478         if (!findObject(&rootNode, msg.path(), usedLength, result)) {
1479             // qDebug("Call failed: no object found at %s", qPrintable(msg.path()));
1480             sendError(msg, QDBusError::UnknownObject);
1481             return;
1482         }
1483
1484         if (!result.obj) {
1485             // no object -> no threading issues
1486             // it's either going to be an error, or an internal filter
1487             activateObject(result, msg, usedLength);
1488             return;
1489         }
1490
1491         objThread = result.obj->thread();
1492         if (!objThread) {
1493             send(msg.createErrorReply(QDBusError::InternalError,
1494                                       QString::fromLatin1("Object '%1' (at path '%2')"
1495                                                           " has no thread. Cannot deliver message.")
1496                                       .arg(result.obj->objectName(), msg.path())));
1497             return;
1498         }
1499
1500         if (!QDBusMessagePrivate::isLocal(msg)) {
1501             // external incoming message
1502             // post it and forget
1503             postEventToThread(HandleObjectCallPostEventAction, result.obj,
1504                               new QDBusActivateObjectEvent(QDBusConnection(this), this, result,
1505                                                            usedLength, msg));
1506             return;
1507         } else if (objThread != QThread::currentThread()) {
1508             // synchronize with other thread
1509             postEventToThread(HandleObjectCallPostEventAction, result.obj,
1510                               new QDBusActivateObjectEvent(QDBusConnection(this), this, result,
1511                                                            usedLength, msg, &sem));
1512             semWait = true;
1513         } else {
1514             semWait = false;
1515         }
1516     } // release the lock
1517
1518     if (semWait)
1519         SEM_ACQUIRE(HandleObjectCallSemaphoreAction, sem);
1520     else
1521         activateObject(result, msg, usedLength);
1522 }
1523
1524 QDBusActivateObjectEvent::~QDBusActivateObjectEvent()
1525 {
1526     if (!handled) {
1527         // we're being destroyed without delivering
1528         // it means the object was deleted between posting and delivering
1529         QDBusConnectionPrivate *that = QDBusConnectionPrivate::d(connection);
1530         that->sendError(message, QDBusError::UnknownObject);
1531     }
1532
1533     // semaphore releasing happens in ~QMetaCallEvent
1534 }
1535
1536 void QDBusActivateObjectEvent::placeMetaCall(QObject *)
1537 {
1538     QDBusConnectionPrivate *that = QDBusConnectionPrivate::d(connection);
1539
1540     QDBusLockerBase::reportThreadAction(HandleObjectCallPostEventAction,
1541                                         QDBusLockerBase::BeforeDeliver, that);
1542     that->activateObject(node, message, pathStartPos);
1543     QDBusLockerBase::reportThreadAction(HandleObjectCallPostEventAction,
1544                                         QDBusLockerBase::AfterDeliver, that);
1545
1546     handled = true;
1547 }
1548
1549 void QDBusConnectionPrivate::handleSignal(const QString &key, const QDBusMessage& msg)
1550 {
1551     SignalHookHash::const_iterator it = signalHooks.constFind(key);
1552     SignalHookHash::const_iterator end = signalHooks.constEnd();
1553     //qDebug("looking for: %s", path.toLocal8Bit().constData());
1554     //qDBusDebug() << signalHooks.keys();
1555     for ( ; it != end && it.key() == key; ++it) {
1556         const SignalHook &hook = it.value();
1557         if (!hook.service.isEmpty()) {
1558             const QString owner =
1559                     shouldWatchService(hook.service) ?
1560                     watchedServices.value(hook.service).owner :
1561                     hook.service;
1562             if (owner != msg.service())
1563                 continue;
1564         }
1565         if (!hook.path.isEmpty() && hook.path != msg.path())
1566             continue;
1567         if (!hook.signature.isEmpty() && hook.signature != msg.signature())
1568             continue;
1569         if (hook.signature.isEmpty() && !hook.signature.isNull() && !msg.signature().isEmpty())
1570             continue;
1571         if (!hook.argumentMatch.isEmpty()) {
1572             const QVariantList arguments = msg.arguments();
1573             if (hook.argumentMatch.size() > arguments.size())
1574                 continue;
1575
1576             bool matched = true;
1577             for (int i = 0; i < hook.argumentMatch.size(); ++i) {
1578                 const QString &param = hook.argumentMatch.at(i);
1579                 if (param.isNull())
1580                     continue;   // don't try to match against this
1581                 if (param == arguments.at(i).toString())
1582                     continue;   // matched
1583                 matched = false;
1584                 break;
1585             }
1586             if (!matched)
1587                 continue;
1588         }
1589
1590         activateSignal(hook, msg);
1591     }
1592 }
1593
1594 void QDBusConnectionPrivate::handleSignal(const QDBusMessage& msg)
1595 {
1596     // We call handlesignal(QString, QDBusMessage) three times:
1597     //  one with member:interface
1598     //  one with member:
1599     //  one with :interface
1600     // This allows us to match signals with wildcards on member or interface
1601     // (but not both)
1602
1603     QString key = msg.member();
1604     key.reserve(key.length() + 1 + msg.interface().length());
1605     key += QLatin1Char(':');
1606     key += msg.interface();
1607
1608     QDBusReadLocker locker(HandleSignalAction, this);
1609     handleSignal(key, msg);                  // one try
1610
1611     key.truncate(msg.member().length() + 1); // keep the ':'
1612     handleSignal(key, msg);                  // second try
1613
1614     key = QLatin1Char(':');
1615     key += msg.interface();
1616     handleSignal(key, msg);                  // third try
1617 }
1618
1619 static dbus_int32_t server_slot = -1;
1620
1621 void QDBusConnectionPrivate::setServer(DBusServer *s, const QDBusErrorInternal &error)
1622 {
1623     if (!s) {
1624         handleError(error);
1625         return;
1626     }
1627
1628     server = s;
1629     mode = ServerMode;
1630
1631     dbus_bool_t data_allocated = q_dbus_server_allocate_data_slot(&server_slot);
1632     if (data_allocated && server_slot < 0)
1633         return;
1634
1635     dbus_bool_t watch_functions_set = q_dbus_server_set_watch_functions(server,
1636                                                                       qDBusAddWatch,
1637                                                                       qDBusRemoveWatch,
1638                                                                       qDBusToggleWatch,
1639                                                                       this, 0);
1640     //qDebug() << "watch_functions_set" << watch_functions_set;
1641     Q_UNUSED(watch_functions_set);
1642
1643     dbus_bool_t time_functions_set = q_dbus_server_set_timeout_functions(server,
1644                                                                        qDBusAddTimeout,
1645                                                                        qDBusRemoveTimeout,
1646                                                                        qDBusToggleTimeout,
1647                                                                        this, 0);
1648     //qDebug() << "time_functions_set" << time_functions_set;
1649     Q_UNUSED(time_functions_set);
1650
1651     q_dbus_server_set_new_connection_function(server, qDBusNewConnection, this, 0);
1652
1653     dbus_bool_t data_set = q_dbus_server_set_data(server, server_slot, this, 0);
1654     //qDebug() << "data_set" << data_set;
1655     Q_UNUSED(data_set);
1656 }
1657
1658 void QDBusConnectionPrivate::setPeer(DBusConnection *c, const QDBusErrorInternal &error)
1659 {
1660     if (!c) {
1661         handleError(error);
1662         return;
1663     }
1664
1665     connection = c;
1666     mode = PeerMode;
1667
1668     q_dbus_connection_set_exit_on_disconnect(connection, false);
1669     q_dbus_connection_set_watch_functions(connection,
1670                                         qDBusAddWatch,
1671                                         qDBusRemoveWatch,
1672                                         qDBusToggleWatch,
1673                                         this, 0);
1674     q_dbus_connection_set_timeout_functions(connection,
1675                                           qDBusAddTimeout,
1676                                           qDBusRemoveTimeout,
1677                                           qDBusToggleTimeout,
1678                                           this, 0);
1679     q_dbus_connection_set_dispatch_status_function(connection, qDBusUpdateDispatchStatus, this, 0);
1680     q_dbus_connection_add_filter(connection,
1681                                qDBusSignalFilter,
1682                                this, 0);
1683
1684     QMetaObject::invokeMethod(this, "doDispatch", Qt::QueuedConnection);
1685 }
1686
1687 static QDBusConnection::ConnectionCapabilities connectionCapabilies(DBusConnection *connection)
1688 {
1689     QDBusConnection::ConnectionCapabilities result = 0;
1690
1691 #if defined(QT_LINKED_LIBDBUS) && DBUS_VERSION < 0x010400
1692     // no capabilities are possible
1693 #else
1694 # if !defined(QT_LINKED_LIBDBUS)
1695     // run-time check if the next functions are available
1696     int major, minor, micro;
1697     q_dbus_get_version(&major, &minor, &micro);
1698     if (major == 1 && minor < 4)
1699         return result;
1700 # endif
1701
1702 #ifndef DBUS_TYPE_UNIX_FD
1703 # define DBUS_TYPE_UNIX_FD int('h')
1704 #endif
1705     if (q_dbus_connection_can_send_type(connection, DBUS_TYPE_UNIX_FD))
1706         result |= QDBusConnection::UnixFileDescriptorPassing;
1707 #endif
1708
1709     return result;
1710 }
1711
1712 void QDBusConnectionPrivate::setConnection(DBusConnection *dbc, const QDBusErrorInternal &error)
1713 {
1714     if (!dbc) {
1715         handleError(error);
1716         return;
1717     }
1718
1719     connection = dbc;
1720     mode = ClientMode;
1721
1722     const char *service = q_dbus_bus_get_unique_name(connection);
1723     Q_ASSERT(service);
1724     baseService = QString::fromUtf8(service);
1725     capabilities = connectionCapabilies(connection);
1726
1727     q_dbus_connection_set_exit_on_disconnect(connection, false);
1728     q_dbus_connection_set_watch_functions(connection, qDBusAddWatch, qDBusRemoveWatch,
1729                                           qDBusToggleWatch, this, 0);
1730     q_dbus_connection_set_timeout_functions(connection, qDBusAddTimeout, qDBusRemoveTimeout,
1731                                             qDBusToggleTimeout, this, 0);
1732     q_dbus_connection_set_dispatch_status_function(connection, qDBusUpdateDispatchStatus, this, 0);
1733     q_dbus_connection_add_filter(connection, qDBusSignalFilter, this, 0);
1734
1735     // Initialize the hooks for the NameAcquired and NameLost signals
1736     // we don't use connectSignal here because we don't need the rules to be sent to the bus
1737     // the bus will always send us these two signals
1738     SignalHook hook;
1739     hook.service = dbusServiceString();
1740     hook.path.clear(); // no matching
1741     hook.obj = this;
1742     hook.params << QMetaType::Void << QVariant::String; // both functions take a QString as parameter and return void
1743
1744     hook.midx = staticMetaObject.indexOfSlot("registerServiceNoLock(QString)");
1745     Q_ASSERT(hook.midx != -1);
1746     signalHooks.insert(QLatin1String("NameAcquired:" DBUS_INTERFACE_DBUS), hook);
1747
1748     hook.midx = staticMetaObject.indexOfSlot("unregisterServiceNoLock(QString)");
1749     Q_ASSERT(hook.midx != -1);
1750     signalHooks.insert(QLatin1String("NameLost:" DBUS_INTERFACE_DBUS), hook);
1751
1752     qDBusDebug() << this << ": connected successfully";
1753
1754     // schedule a dispatch:
1755     QMetaObject::invokeMethod(this, "doDispatch", Qt::QueuedConnection);
1756 }
1757
1758 extern "C"{
1759 static void qDBusResultReceived(DBusPendingCall *pending, void *user_data)
1760 {
1761     QDBusPendingCallPrivate *call = reinterpret_cast<QDBusPendingCallPrivate *>(user_data);
1762     Q_ASSERT(call->pending == pending);
1763     Q_UNUSED(pending);
1764     QDBusConnectionPrivate::processFinishedCall(call);
1765 }
1766 }
1767
1768 void QDBusConnectionPrivate::waitForFinished(QDBusPendingCallPrivate *pcall)
1769 {
1770     Q_ASSERT(pcall->pending);
1771     Q_ASSERT(!pcall->autoDelete);
1772     //Q_ASSERT(pcall->mutex.isLocked()); // there's no such function
1773
1774     if (pcall->waitingForFinished) {
1775         // another thread is already waiting
1776         pcall->waitForFinishedCondition.wait(&pcall->mutex);
1777     } else {
1778         pcall->waitingForFinished = true;
1779         pcall->mutex.unlock();
1780
1781         {
1782             QDBusDispatchLocker locker(PendingCallBlockAction, this);
1783             q_dbus_pending_call_block(pcall->pending);
1784             // QDBusConnectionPrivate::processFinishedCall() is called automatically
1785         }
1786         pcall->mutex.lock();
1787         pcall->waitForFinishedCondition.wakeAll();
1788     }
1789 }
1790
1791 static inline bool waitingForFinishedIsSet(QDBusPendingCallPrivate *call)
1792 {
1793     const QMutexLocker locker(&call->mutex);
1794     return call->waitingForFinished;
1795 }
1796
1797 void QDBusConnectionPrivate::processFinishedCall(QDBusPendingCallPrivate *call)
1798 {
1799     QDBusConnectionPrivate *connection = const_cast<QDBusConnectionPrivate *>(call->connection);
1800
1801     QMutexLocker locker(&call->mutex);
1802
1803     QDBusMessage &msg = call->replyMessage;
1804     if (call->pending) {
1805         // decode the message
1806         DBusMessage *reply = q_dbus_pending_call_steal_reply(call->pending);
1807         msg = QDBusMessagePrivate::fromDBusMessage(reply, connection->capabilities);
1808         q_dbus_message_unref(reply);
1809     }
1810     qDBusDebug() << connection << "got message reply (async):" << msg;
1811
1812     // Check if the reply has the expected signature
1813     call->checkReceivedSignature();
1814
1815     if (!call->receiver.isNull() && call->methodIdx != -1 && msg.type() == QDBusMessage::ReplyMessage) {
1816         // Deliver the return values of a remote function call.
1817         //
1818         // There is only one connection and it is specified by idx
1819         // The slot must have the same parameter types that the message does
1820         // The slot may have less parameters than the message
1821         // The slot may optionally have one final parameter that is QDBusMessage
1822         // The slot receives read-only copies of the message (i.e., pass by value or by const-ref)
1823
1824         QDBusCallDeliveryEvent *e = prepareReply(connection, call->receiver, call->methodIdx,
1825                                                  call->metaTypes, msg);
1826         if (e)
1827             connection->postEventToThread(MessageResultReceivedAction, call->receiver, e);
1828         else
1829             qDBusDebug() << "Deliver failed!";
1830     }
1831
1832     if (call->pending)
1833         q_dbus_pending_call_unref(call->pending);
1834     call->pending = 0;
1835
1836     locker.unlock();
1837
1838     // Are there any watchers?
1839     if (call->watcherHelper)
1840         call->watcherHelper->emitSignals(msg, call->sentMessage);
1841
1842     if (msg.type() == QDBusMessage::ErrorMessage)
1843         emit connection->callWithCallbackFailed(QDBusError(msg), call->sentMessage);
1844
1845     if (call->autoDelete) {
1846         Q_ASSERT(!waitingForFinishedIsSet(call)); // can't wait on a call with autoDelete!
1847         delete call;
1848     }
1849 }
1850
1851 int QDBusConnectionPrivate::send(const QDBusMessage& message)
1852 {
1853     if (QDBusMessagePrivate::isLocal(message))
1854         return -1;              // don't send; the reply will be retrieved by the caller
1855                                 // through the d_ptr->localReply link
1856
1857     QDBusError error;
1858     DBusMessage *msg = QDBusMessagePrivate::toDBusMessage(message, capabilities, &error);
1859     if (!msg) {
1860         if (message.type() == QDBusMessage::MethodCallMessage)
1861             qWarning("QDBusConnection: error: could not send message to service \"%s\" path \"%s\" interface \"%s\" member \"%s\": %s",
1862                      qPrintable(message.service()), qPrintable(message.path()),
1863                      qPrintable(message.interface()), qPrintable(message.member()),
1864                      qPrintable(error.message()));
1865         else if (message.type() == QDBusMessage::SignalMessage)
1866             qWarning("QDBusConnection: error: could not send signal path \"%s\" interface \"%s\" member \"%s\": %s",
1867                      qPrintable(message.path()), qPrintable(message.interface()),
1868                      qPrintable(message.member()),
1869                      qPrintable(error.message()));
1870         else
1871             qWarning("QDBusConnection: error: could not send %s message to service \"%s\": %s",
1872                      message.type() == QDBusMessage::ReplyMessage ? "reply" :
1873                      message.type() == QDBusMessage::ErrorMessage ? "error" :
1874                      "invalid", qPrintable(message.service()),
1875                      qPrintable(error.message()));
1876         lastError = error;
1877         return 0;
1878     }
1879
1880     q_dbus_message_set_no_reply(msg, true); // the reply would not be delivered to anything
1881
1882     qDBusDebug() << this << "sending message (no reply):" << message;
1883     checkThread();
1884     bool isOk = q_dbus_connection_send(connection, msg, 0);
1885     int serial = 0;
1886     if (isOk)
1887         serial = q_dbus_message_get_serial(msg);
1888
1889     q_dbus_message_unref(msg);
1890     return serial;
1891 }
1892
1893 QDBusMessage QDBusConnectionPrivate::sendWithReply(const QDBusMessage &message,
1894                                                    int sendMode, int timeout)
1895 {
1896     checkThread();
1897     if ((sendMode == QDBus::BlockWithGui || sendMode == QDBus::Block)
1898          && isServiceRegisteredByThread(message.service()))
1899         // special case for synchronous local calls
1900         return sendWithReplyLocal(message);
1901
1902     if (!QCoreApplication::instance() || sendMode == QDBus::Block) {
1903         QDBusError err;
1904         DBusMessage *msg = QDBusMessagePrivate::toDBusMessage(message, capabilities, &err);
1905         if (!msg) {
1906             qWarning("QDBusConnection: error: could not send message to service \"%s\" path \"%s\" interface \"%s\" member \"%s\": %s",
1907                      qPrintable(message.service()), qPrintable(message.path()),
1908                      qPrintable(message.interface()), qPrintable(message.member()),
1909                      qPrintable(err.message()));
1910             lastError = err;
1911             return QDBusMessage::createError(err);
1912         }
1913
1914         qDBusDebug() << this << "sending message (blocking):" << message;
1915         QDBusErrorInternal error;
1916         DBusMessage *reply = q_dbus_connection_send_with_reply_and_block(connection, msg, timeout, error);
1917
1918         q_dbus_message_unref(msg);
1919
1920         if (!!error) {
1921             lastError = err = error;
1922             return QDBusMessage::createError(err);
1923         }
1924
1925         QDBusMessage amsg = QDBusMessagePrivate::fromDBusMessage(reply, capabilities);
1926         q_dbus_message_unref(reply);
1927         qDBusDebug() << this << "got message reply (blocking):" << amsg;
1928
1929         return amsg;
1930     } else { // use the event loop
1931         QDBusPendingCallPrivate *pcall = sendWithReplyAsync(message, timeout);
1932         Q_ASSERT(pcall);
1933
1934         if (pcall->replyMessage.type() == QDBusMessage::InvalidMessage) {
1935             pcall->watcherHelper = new QDBusPendingCallWatcherHelper;
1936             QEventLoop loop;
1937             loop.connect(pcall->watcherHelper, SIGNAL(reply(QDBusMessage)), SLOT(quit()));
1938             loop.connect(pcall->watcherHelper, SIGNAL(error(QDBusError,QDBusMessage)), SLOT(quit()));
1939
1940             // enter the event loop and wait for a reply
1941             loop.exec(QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents);
1942         }
1943
1944         QDBusMessage reply = pcall->replyMessage;
1945         lastError = QDBusError(reply);      // set or clear error
1946
1947         delete pcall;
1948         return reply;
1949     }
1950 }
1951
1952 QDBusMessage QDBusConnectionPrivate::sendWithReplyLocal(const QDBusMessage &message)
1953 {
1954     qDBusDebug() << this << "sending message via local-loop:" << message;
1955
1956     QDBusMessage localCallMsg = QDBusMessagePrivate::makeLocal(*this, message);
1957     bool handled = handleMessage(localCallMsg);
1958
1959     if (!handled) {
1960         QString interface = message.interface();
1961         if (interface.isEmpty())
1962             interface = QLatin1String("<no-interface>");
1963         return QDBusMessage::createError(QDBusError::InternalError,
1964                                          QString::fromLatin1("Internal error trying to call %1.%2 at %3 (signature '%4'")
1965                                          .arg(interface, message.member(),
1966                                               message.path(), message.signature()));
1967     }
1968
1969     // if the message was handled, there might be a reply
1970     QDBusMessage localReplyMsg = QDBusMessagePrivate::makeLocalReply(*this, localCallMsg);
1971     if (localReplyMsg.type() == QDBusMessage::InvalidMessage) {
1972         qWarning("QDBusConnection: cannot call local method '%s' at object %s (with signature '%s') "
1973                  "on blocking mode", qPrintable(message.member()), qPrintable(message.path()),
1974                  qPrintable(message.signature()));
1975         return QDBusMessage::createError(
1976             QDBusError(QDBusError::InternalError,
1977                        QLatin1String("local-loop message cannot have delayed replies")));
1978     }
1979
1980     // there is a reply
1981     qDBusDebug() << this << "got message via local-loop:" << localReplyMsg;
1982     return localReplyMsg;
1983 }
1984
1985 QDBusPendingCallPrivate *QDBusConnectionPrivate::sendWithReplyAsync(const QDBusMessage &message,
1986                                                                     int timeout)
1987 {
1988     if (isServiceRegisteredByThread(message.service())) {
1989         // special case for local calls
1990         QDBusPendingCallPrivate *pcall = new QDBusPendingCallPrivate(message, this);
1991         pcall->replyMessage = sendWithReplyLocal(message);
1992
1993         return pcall;
1994     }
1995
1996     checkThread();
1997     QDBusPendingCallPrivate *pcall = new QDBusPendingCallPrivate(message, this);
1998     pcall->ref.store(0);
1999
2000     QDBusError error;
2001     DBusMessage *msg = QDBusMessagePrivate::toDBusMessage(message, capabilities, &error);
2002     if (!msg) {
2003         qWarning("QDBusConnection: error: could not send message to service \"%s\" path \"%s\" interface \"%s\" member \"%s\": %s",
2004                  qPrintable(message.service()), qPrintable(message.path()),
2005                  qPrintable(message.interface()), qPrintable(message.member()),
2006                  qPrintable(error.message()));
2007         pcall->replyMessage = QDBusMessage::createError(error);
2008         lastError = error;
2009         return pcall;
2010     }
2011
2012     qDBusDebug() << this << "sending message (async):" << message;
2013     DBusPendingCall *pending = 0;
2014
2015     QDBusDispatchLocker locker(SendWithReplyAsyncAction, this);
2016     if (q_dbus_connection_send_with_reply(connection, msg, &pending, timeout)) {
2017         if (pending) {
2018             q_dbus_message_unref(msg);
2019
2020             pcall->pending = pending;
2021             q_dbus_pending_call_set_notify(pending, qDBusResultReceived, pcall, 0);
2022
2023             return pcall;
2024         } else {
2025             // we're probably disconnected at this point
2026             lastError = error = QDBusError(QDBusError::Disconnected, QLatin1String("Not connected to server"));
2027         }
2028     } else {
2029         lastError = error = QDBusError(QDBusError::NoMemory, QLatin1String("Out of memory"));
2030     }
2031
2032     q_dbus_message_unref(msg);
2033     pcall->replyMessage = QDBusMessage::createError(error);
2034     return pcall;
2035 }
2036
2037 int QDBusConnectionPrivate::sendWithReplyAsync(const QDBusMessage &message, QObject *receiver,
2038                                                const char *returnMethod, const char *errorMethod,
2039                                                int timeout)
2040 {
2041     QDBusPendingCallPrivate *pcall = sendWithReplyAsync(message, timeout);
2042     Q_ASSERT(pcall);
2043
2044     // has it already finished with success (dispatched locally)?
2045     if (pcall->replyMessage.type() == QDBusMessage::ReplyMessage) {
2046         pcall->setReplyCallback(receiver, returnMethod);
2047         processFinishedCall(pcall);
2048         delete pcall;
2049         return 1;
2050     }
2051
2052     // either it hasn't finished or it has finished with error
2053     if (errorMethod) {
2054         pcall->watcherHelper = new QDBusPendingCallWatcherHelper;
2055         connect(pcall->watcherHelper, SIGNAL(error(QDBusError,QDBusMessage)), receiver, errorMethod,
2056                 Qt::QueuedConnection);
2057         pcall->watcherHelper->moveToThread(thread());
2058     }
2059
2060     // has it already finished and is an error reply message?
2061     if (pcall->replyMessage.type() == QDBusMessage::ErrorMessage) {
2062         processFinishedCall(pcall);
2063         delete pcall;
2064         return 1;
2065     }
2066
2067     pcall->autoDelete = true;
2068     pcall->ref.ref();
2069     pcall->setReplyCallback(receiver, returnMethod);
2070
2071     return 1;
2072 }
2073
2074 bool QDBusConnectionPrivate::connectSignal(const QString &service,
2075                                            const QString &path, const QString &interface, const QString &name,
2076                                            const QStringList &argumentMatch, const QString &signature,
2077                                            QObject *receiver, const char *slot)
2078 {
2079     // check the slot
2080     QDBusConnectionPrivate::SignalHook hook;
2081     QString key;
2082     QString name2 = name;
2083     if (name2.isNull())
2084         name2.detach();
2085
2086     hook.signature = signature;
2087     if (!prepareHook(hook, key, service, path, interface, name, argumentMatch, receiver, slot, 0, false))
2088         return false;           // don't connect
2089
2090     // avoid duplicating:
2091     QDBusConnectionPrivate::SignalHookHash::ConstIterator it = signalHooks.constFind(key);
2092     QDBusConnectionPrivate::SignalHookHash::ConstIterator end = signalHooks.constEnd();
2093     for ( ; it != end && it.key() == key; ++it) {
2094         const QDBusConnectionPrivate::SignalHook &entry = it.value();
2095         if (entry.service == hook.service &&
2096             entry.path == hook.path &&
2097             entry.signature == hook.signature &&
2098             entry.obj == hook.obj &&
2099             entry.midx == hook.midx &&
2100             entry.argumentMatch == hook.argumentMatch) {
2101             // no need to compare the parameters if it's the same slot
2102             return true;        // already there
2103         }
2104     }
2105
2106     connectSignal(key, hook);
2107     return true;
2108 }
2109
2110 void QDBusConnectionPrivate::connectSignal(const QString &key, const SignalHook &hook)
2111 {
2112     signalHooks.insertMulti(key, hook);
2113     connect(hook.obj, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*)),
2114             Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection));
2115
2116     MatchRefCountHash::iterator it = matchRefCounts.find(hook.matchRule);
2117
2118     if (it != matchRefCounts.end()) { // Match already present
2119         it.value() = it.value() + 1;
2120         return;
2121     }
2122
2123     matchRefCounts.insert(hook.matchRule, 1);
2124
2125     if (connection) {
2126         if (mode != QDBusConnectionPrivate::PeerMode) {
2127             qDBusDebug("Adding rule: %s", hook.matchRule.constData());
2128             q_dbus_bus_add_match(connection, hook.matchRule, NULL);
2129
2130             // Successfully connected the signal
2131             // Do we need to watch for this name?
2132             if (shouldWatchService(hook.service)) {
2133                 WatchedServicesHash::mapped_type &data = watchedServices[hook.service];
2134                 if (++data.refcount == 1) {
2135                     // we need to watch for this service changing
2136                     connectSignal(dbusServiceString(), QString(), dbusInterfaceString(),
2137                                   QLatin1String("NameOwnerChanged"), QStringList() << hook.service, QString(),
2138                                   this, SLOT(serviceOwnerChangedNoLock(QString,QString,QString)));
2139                     data.owner = getNameOwnerNoCache(hook.service);
2140                     qDBusDebug() << this << "Watching service" << hook.service << "for owner changes (current owner:"
2141                                  << data.owner << ")";
2142                 }
2143             }
2144         }
2145     }
2146 }
2147
2148 bool QDBusConnectionPrivate::disconnectSignal(const QString &service,
2149                                               const QString &path, const QString &interface, const QString &name,
2150                                               const QStringList &argumentMatch, const QString &signature,
2151                                               QObject *receiver, const char *slot)
2152 {
2153     // check the slot
2154     QDBusConnectionPrivate::SignalHook hook;
2155     QString key;
2156     QString name2 = name;
2157     if (name2.isNull())
2158         name2.detach();
2159
2160     hook.signature = signature;
2161     if (!prepareHook(hook, key, service, path, interface, name, argumentMatch, receiver, slot, 0, false))
2162         return false;           // don't disconnect
2163
2164     // avoid duplicating:
2165     QDBusConnectionPrivate::SignalHookHash::Iterator it = signalHooks.find(key);
2166     QDBusConnectionPrivate::SignalHookHash::Iterator end = signalHooks.end();
2167     for ( ; it != end && it.key() == key; ++it) {
2168         const QDBusConnectionPrivate::SignalHook &entry = it.value();
2169         if (entry.service == hook.service &&
2170             entry.path == hook.path &&
2171             entry.signature == hook.signature &&
2172             entry.obj == hook.obj &&
2173             entry.midx == hook.midx &&
2174             entry.argumentMatch == hook.argumentMatch) {
2175             // no need to compare the parameters if it's the same slot
2176             disconnectSignal(it);
2177             return true;        // it was there
2178         }
2179     }
2180
2181     // the slot was not found
2182     return false;
2183 }
2184
2185 QDBusConnectionPrivate::SignalHookHash::Iterator
2186 QDBusConnectionPrivate::disconnectSignal(SignalHookHash::Iterator &it)
2187 {
2188     const SignalHook &hook = it.value();
2189
2190     bool erase = false;
2191     MatchRefCountHash::iterator i = matchRefCounts.find(hook.matchRule);
2192     if (i == matchRefCounts.end()) {
2193         qWarning("QDBusConnectionPrivate::disconnectSignal: MatchRule not found in matchRefCounts!!");
2194     } else {
2195         if (i.value() == 1) {
2196             erase = true;
2197             matchRefCounts.erase(i);
2198         }
2199         else {
2200             i.value() = i.value() - 1;
2201         }
2202     }
2203
2204     // we don't care about errors here
2205     if (connection && erase) {
2206         if (mode != QDBusConnectionPrivate::PeerMode) {
2207             qDBusDebug("Removing rule: %s", hook.matchRule.constData());
2208             q_dbus_bus_remove_match(connection, hook.matchRule, NULL);
2209
2210             // Successfully disconnected the signal
2211             // Were we watching for this name?
2212             WatchedServicesHash::Iterator sit = watchedServices.find(hook.service);
2213             if (sit != watchedServices.end()) {
2214                 if (--sit.value().refcount == 0) {
2215                     watchedServices.erase(sit);
2216                     disconnectSignal(dbusServiceString(), QString(), dbusInterfaceString(),
2217                                   QLatin1String("NameOwnerChanged"), QStringList() << hook.service, QString(),
2218                                   this, SLOT(_q_serviceOwnerChanged(QString,QString,QString)));
2219                 }
2220             }
2221         }
2222
2223     }
2224
2225     return signalHooks.erase(it);
2226 }
2227
2228 void QDBusConnectionPrivate::registerObject(const ObjectTreeNode *node)
2229 {
2230     connect(node->obj, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*)),
2231             Qt::DirectConnection);
2232
2233     if (node->flags & (QDBusConnection::ExportAdaptors
2234                        | QDBusConnection::ExportScriptableSignals
2235                        | QDBusConnection::ExportNonScriptableSignals)) {
2236         QDBusAdaptorConnector *connector = qDBusCreateAdaptorConnector(node->obj);
2237
2238         if (node->flags & (QDBusConnection::ExportScriptableSignals
2239                            | QDBusConnection::ExportNonScriptableSignals)) {
2240             connector->disconnectAllSignals(node->obj);
2241             connector->connectAllSignals(node->obj);
2242         }
2243
2244         // disconnect and reconnect to avoid duplicates
2245         connector->disconnect(SIGNAL(relaySignal(QObject*,const QMetaObject*,int,QVariantList)),
2246                               this, SLOT(relaySignal(QObject*,const QMetaObject*,int,QVariantList)));
2247         connect(connector, SIGNAL(relaySignal(QObject*,const QMetaObject*,int,QVariantList)),
2248                 this, SLOT(relaySignal(QObject*,const QMetaObject*,int,QVariantList)),
2249                 Qt::DirectConnection);
2250     }
2251 }
2252
2253 void QDBusConnectionPrivate::connectRelay(const QString &service,
2254                                           const QString &path, const QString &interface,
2255                                           QDBusAbstractInterface *receiver,
2256                                           const QMetaMethod &signal)
2257 {
2258     // this function is called by QDBusAbstractInterface when one of its signals is connected
2259     // we set up a relay from D-Bus into it
2260     SignalHook hook;
2261     QString key;
2262
2263     QByteArray sig;
2264     sig.append(QSIGNAL_CODE + '0');
2265     sig.append(signal.methodSignature());
2266     if (!prepareHook(hook, key, service, path, interface, QString(), QStringList(), receiver, sig,
2267                      QDBusAbstractInterface::staticMetaObject.methodCount(), true))
2268         return;                 // don't connect
2269
2270     // add it to our list:
2271     QDBusWriteLocker locker(ConnectRelayAction, this);
2272     SignalHookHash::ConstIterator it = signalHooks.constFind(key);
2273     SignalHookHash::ConstIterator end = signalHooks.constEnd();
2274     for ( ; it != end && it.key() == key; ++it) {
2275         const SignalHook &entry = it.value();
2276         if (entry.service == hook.service &&
2277             entry.path == hook.path &&
2278             entry.signature == hook.signature &&
2279             entry.obj == hook.obj &&
2280             entry.midx == hook.midx)
2281             return;             // already there, no need to re-add
2282     }
2283
2284     connectSignal(key, hook);
2285 }
2286
2287 void QDBusConnectionPrivate::disconnectRelay(const QString &service,
2288                                              const QString &path, const QString &interface,
2289                                              QDBusAbstractInterface *receiver,
2290                                              const QMetaMethod &signal)
2291 {
2292     // this function is called by QDBusAbstractInterface when one of its signals is disconnected
2293     // we remove relay from D-Bus into it
2294     SignalHook hook;
2295     QString key;
2296
2297     QByteArray sig;
2298     sig.append(QSIGNAL_CODE + '0');
2299     sig.append(signal.methodSignature());
2300     if (!prepareHook(hook, key, service, path, interface, QString(), QStringList(), receiver, sig,
2301                      QDBusAbstractInterface::staticMetaObject.methodCount(), true))
2302         return;                 // don't connect
2303
2304     // remove it from our list:
2305     QDBusWriteLocker locker(DisconnectRelayAction, this);
2306     SignalHookHash::Iterator it = signalHooks.find(key);
2307     SignalHookHash::Iterator end = signalHooks.end();
2308     for ( ; it != end && it.key() == key; ++it) {
2309         const SignalHook &entry = it.value();
2310         if (entry.service == hook.service &&
2311             entry.path == hook.path &&
2312             entry.signature == hook.signature &&
2313             entry.obj == hook.obj &&
2314             entry.midx == hook.midx) {
2315             // found it
2316             disconnectSignal(it);
2317             return;
2318         }
2319     }
2320
2321     qWarning("QDBusConnectionPrivate::disconnectRelay called for a signal that was not found");
2322 }
2323
2324 QString QDBusConnectionPrivate::getNameOwner(const QString& serviceName)
2325 {
2326     if (QDBusUtil::isValidUniqueConnectionName(serviceName))
2327         return serviceName;
2328     if (!connection)
2329         return QString();
2330
2331     {
2332         // acquire a read lock for the cache
2333         QReadLocker locker(&lock);
2334         WatchedServicesHash::ConstIterator it = watchedServices.constFind(serviceName);
2335         if (it != watchedServices.constEnd())
2336             return it->owner;
2337     }
2338
2339     // not cached
2340     return getNameOwnerNoCache(serviceName);
2341 }
2342
2343 QString QDBusConnectionPrivate::getNameOwnerNoCache(const QString &serviceName)
2344 {
2345     QDBusMessage msg = QDBusMessage::createMethodCall(dbusServiceString(),
2346             QLatin1String(DBUS_PATH_DBUS), dbusInterfaceString(),
2347             QLatin1String("GetNameOwner"));
2348     QDBusMessagePrivate::setParametersValidated(msg, true);
2349     msg << serviceName;
2350     QDBusMessage reply = sendWithReply(msg, QDBus::Block);
2351     if (reply.type() == QDBusMessage::ReplyMessage)
2352         return reply.arguments().at(0).toString();
2353     return QString();
2354 }
2355
2356 QDBusMetaObject *
2357 QDBusConnectionPrivate::findMetaObject(const QString &service, const QString &path,
2358                                        const QString &interface, QDBusError &error)
2359 {
2360     // service must be a unique connection name
2361     if (!interface.isEmpty()) {
2362         QDBusReadLocker locker(FindMetaObject1Action, this);
2363         QDBusMetaObject *mo = cachedMetaObjects.value(interface, 0);
2364         if (mo)
2365             return mo;
2366     }
2367
2368     // introspect the target object
2369     QDBusMessage msg = QDBusMessage::createMethodCall(service, path,
2370                                                 QLatin1String(DBUS_INTERFACE_INTROSPECTABLE),
2371                                                 QLatin1String("Introspect"));
2372     QDBusMessagePrivate::setParametersValidated(msg, true);
2373
2374     QDBusMessage reply = sendWithReply(msg, QDBus::Block);
2375
2376     // it doesn't exist yet, we have to create it
2377     QDBusWriteLocker locker(FindMetaObject2Action, this);
2378     QDBusMetaObject *mo = 0;
2379     if (!interface.isEmpty())
2380         mo = cachedMetaObjects.value(interface, 0);
2381     if (mo)
2382         // maybe it got created when we switched from read to write lock
2383         return mo;
2384
2385     QString xml;
2386     if (reply.type() == QDBusMessage::ReplyMessage) {
2387         if (reply.signature() == QLatin1String("s"))
2388             // fetch the XML description
2389             xml = reply.arguments().at(0).toString();
2390     } else {
2391         error = QDBusError(reply);
2392         lastError = error;
2393         if (reply.type() != QDBusMessage::ErrorMessage || error.type() != QDBusError::UnknownMethod)
2394             return 0; // error
2395     }
2396
2397     // release the lock and return
2398     QDBusMetaObject *result = QDBusMetaObject::createMetaObject(interface, xml,
2399                                                                 cachedMetaObjects, error);
2400     lastError = error;
2401     return result;
2402 }
2403
2404 void QDBusConnectionPrivate::registerService(const QString &serviceName)
2405 {
2406     QDBusWriteLocker locker(RegisterServiceAction, this);
2407     registerServiceNoLock(serviceName);
2408 }
2409
2410 void QDBusConnectionPrivate::registerServiceNoLock(const QString &serviceName)
2411 {
2412     serviceNames.append(serviceName);
2413 }
2414
2415 void QDBusConnectionPrivate::unregisterService(const QString &serviceName)
2416 {
2417     QDBusWriteLocker locker(UnregisterServiceAction, this);
2418     unregisterServiceNoLock(serviceName);
2419 }
2420
2421 void QDBusConnectionPrivate::unregisterServiceNoLock(const QString &serviceName)
2422 {
2423     serviceNames.removeAll(serviceName);
2424 }
2425
2426 bool QDBusConnectionPrivate::isServiceRegisteredByThread(const QString &serviceName) const
2427 {
2428     if (!serviceName.isEmpty() && serviceName == baseService)
2429         return true;
2430     QStringList copy = serviceNames;
2431     return copy.contains(serviceName);
2432 }
2433
2434 void QDBusConnectionPrivate::postEventToThread(int action, QObject *object, QEvent *ev)
2435 {
2436     QDBusLockerBase::reportThreadAction(action, QDBusLockerBase::BeforePost, this);
2437     QCoreApplication::postEvent(object, ev);
2438     QDBusLockerBase::reportThreadAction(action, QDBusLockerBase::AfterPost, this);
2439 }
2440
2441 QT_END_NAMESPACE
2442
2443 #endif // QT_NO_DBUS