source: trunk/src/dbus/qdbusintegrator.cpp@ 219

Last change on this file since 219 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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