00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include <QtCore/qcoreapplication.h>
00024 #include <QtCore/qcoreevent.h>
00025 #include <QtCore/qdebug.h>
00026 #include <QtCore/qmetaobject.h>
00027 #include <QtCore/qsocketnotifier.h>
00028
00029 #include "qdbusconnection_p.h"
00030 #include "qdbusmessage.h"
00031
00032 int QDBusConnectionPrivate::messageMetaType = 0;
00033
00034 static dbus_bool_t qDBusAddTimeout(DBusTimeout *timeout, void *data)
00035 {
00036 Q_ASSERT(timeout);
00037 Q_ASSERT(data);
00038
00039
00040
00041 QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
00042
00043 if (!dbus_timeout_get_enabled(timeout))
00044 return true;
00045
00046 if (!QCoreApplication::instance()) {
00047 d->pendingTimeouts.append(timeout);
00048 return true;
00049 }
00050 int timerId = d->startTimer(dbus_timeout_get_interval(timeout));
00051 if (!timerId)
00052 return false;
00053
00054 d->timeouts[timerId] = timeout;
00055 return true;
00056 }
00057
00058 static void qDBusRemoveTimeout(DBusTimeout *timeout, void *data)
00059 {
00060 Q_ASSERT(timeout);
00061 Q_ASSERT(data);
00062
00063
00064
00065 QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
00066 d->pendingTimeouts.removeAll(timeout);
00067
00068 QDBusConnectionPrivate::TimeoutHash::iterator it = d->timeouts.begin();
00069 while (it != d->timeouts.end()) {
00070 if (it.value() == timeout) {
00071 d->killTimer(it.key());
00072 it = d->timeouts.erase(it);
00073 } else {
00074 ++it;
00075 }
00076 }
00077 }
00078
00079 static void qDBusToggleTimeout(DBusTimeout *timeout, void *data)
00080 {
00081 Q_ASSERT(timeout);
00082 Q_ASSERT(data);
00083
00084 qDebug("ToggleTimeout");
00085
00086 qDBusRemoveTimeout(timeout, data);
00087 qDBusAddTimeout(timeout, data);
00088 }
00089
00090 static dbus_bool_t qDBusAddWatch(DBusWatch *watch, void *data)
00091 {
00092 Q_ASSERT(watch);
00093 Q_ASSERT(data);
00094
00095 QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
00096
00097 int flags = dbus_watch_get_flags(watch);
00098 int fd = dbus_watch_get_fd(watch);
00099
00100 QDBusConnectionPrivate::Watcher watcher;
00101 if (flags & DBUS_WATCH_READABLE) {
00102 qDebug("addReadWatch %d", fd);
00103 watcher.watch = watch;
00104 if (QCoreApplication::instance()) {
00105 watcher.read = new QSocketNotifier(fd, QSocketNotifier::Read, d);
00106 d->connect(watcher.read, SIGNAL(activated(int)), SLOT(socketRead(int)));
00107 }
00108 }
00109 if (flags & DBUS_WATCH_WRITABLE) {
00110 qDebug("addWriteWatch %d", fd);
00111 watcher.watch = watch;
00112 if (QCoreApplication::instance()) {
00113 watcher.write = new QSocketNotifier(fd, QSocketNotifier::Write, d);
00114 d->connect(watcher.write, SIGNAL(activated(int)), SLOT(socketWrite(int)));
00115 }
00116 }
00117 d->watchers.insertMulti(fd, watcher);
00118
00119 return true;
00120 }
00121
00122 static void qDBusRemoveWatch(DBusWatch *watch, void *data)
00123 {
00124 Q_ASSERT(watch);
00125 Q_ASSERT(data);
00126
00127 qDebug("remove watch");
00128
00129 QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
00130 int fd = dbus_watch_get_fd(watch);
00131
00132 QDBusConnectionPrivate::WatcherHash::iterator i = d->watchers.find(fd);
00133 while (i != d->watchers.end() && i.key() == fd) {
00134 if (i.value().watch == watch) {
00135 delete i.value().read;
00136 delete i.value().write;
00137 d->watchers.erase(i);
00138 return;
00139 }
00140 ++i;
00141 }
00142 }
00143
00144 static void qDBusToggleWatch(DBusWatch *watch, void *data)
00145 {
00146 Q_ASSERT(watch);
00147 Q_ASSERT(data);
00148
00149 QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
00150 int fd = dbus_watch_get_fd(watch);
00151
00152 QDBusConnectionPrivate::WatcherHash::iterator i = d->watchers.find(fd);
00153 while (i != d->watchers.end() && i.key() == fd) {
00154 if (i.value().watch == watch) {
00155 bool enabled = dbus_watch_get_enabled(watch);
00156 int flags = dbus_watch_get_flags(watch);
00157
00158 qDebug("toggle watch %d to %d (write: %d, read: %d)", dbus_watch_get_fd(watch), enabled, flags & DBUS_WATCH_WRITABLE, flags & DBUS_WATCH_READABLE);
00159
00160 if (flags & DBUS_WATCH_READABLE && i.value().read)
00161 i.value().read->setEnabled(enabled);
00162 if (flags & DBUS_WATCH_WRITABLE && i.value().write)
00163 i.value().write->setEnabled(enabled);
00164 return;
00165 }
00166 ++i;
00167 }
00168 }
00169
00170 static void qDBusNewConnection(DBusServer *server, DBusConnection *c, void *data)
00171 {
00172 Q_ASSERT(data); Q_ASSERT(server); Q_ASSERT(c);
00173
00174 qDebug("SERVER: GOT A NEW CONNECTION");
00175 }
00176
00177 static DBusHandlerResult qDBusSignalFilter(DBusConnection *connection,
00178 DBusMessage *message, void *data)
00179 {
00180 Q_ASSERT(data);
00181 Q_UNUSED(connection);
00182
00183 QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
00184 if (d->mode == QDBusConnectionPrivate::InvalidMode)
00185 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00186
00187 int msgType = dbus_message_get_type(message);
00188 bool handled = false;
00189
00190 QDBusMessage amsg = QDBusMessage::fromDBusMessage(message);
00191 qDebug() << "got message: " << dbus_message_get_type(message) << amsg;
00192
00193 if (msgType == DBUS_MESSAGE_TYPE_SIGNAL) {
00194 handled = d->handleSignal(message);
00195 } else if (msgType == DBUS_MESSAGE_TYPE_METHOD_CALL) {
00196 handled = d->handleObjectCall(message);
00197 }
00198
00199 return handled ? DBUS_HANDLER_RESULT_HANDLED :
00200 DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00201 }
00202
00203 static bool qInvokeDBusSlot(const QDBusConnectionPrivate::SignalHook& hook, const QDBusMessage &msg)
00204 {
00205 int count = msg.count();
00206 if (!(count == hook.params.count()
00207 || (count + 1 == hook.params.count()
00208 && hook.params[count] == QDBusConnectionPrivate::messageMetaType)))
00209 return false;
00210
00211 QVarLengthArray<void *, 16> params;
00212 params.append(0);
00213 for (int i = 0; i < msg.count(); ++i) {
00214 const QVariant &v = msg.at(i);
00215 if (int(v.type()) != hook.params[i]) {
00216 return false;
00217 }
00218 params.append(const_cast<void *>(v.constData()));
00219 }
00220 if (count + 1 == hook.params.count())
00221 params.append(const_cast<QDBusMessage *>(&msg));
00222 return hook.obj->qt_metacall(QMetaObject::InvokeMetaMethod, hook.midx, params.data()) < 0;
00223 }
00224
00225 static bool qInvokeDBusSlot(QObject *object, int idx, const QDBusMessage &msg)
00226 {
00227 Q_ASSERT(object);
00228
00229 const QMetaMethod method = object->metaObject()->method(idx);
00230 if (!method.signature())
00231 return false;
00232
00233 QVarLengthArray<void *> params;
00234 params.append(0);
00235
00236 QList<QByteArray> parameterTypes = method.parameterTypes();
00237
00238
00239
00240 if ((parameterTypes.count() > msg.count())
00241 || (parameterTypes.count() + 1 != msg.count())
00242 && parameterTypes.last() != "QDBusMessage") {
00243 qWarning("Cannot deliver asynchronous reply to object named '%s' because of parameter "
00244 "mismatch. Please check your sendWithReplyAsync() statements.",
00245 object->objectName().toLocal8Bit().constData());
00246 return false;
00247 }
00248
00249 int i;
00250 for (i = 0; i < parameterTypes.count(); ++i) {
00251 const QByteArray param = parameterTypes.at(i);
00252 if (param == msg.at(i).typeName()) {
00253 params.append(const_cast<void *>(msg.at(i).constData()));
00254 } else if (i == parameterTypes.count() - 1 && param == "QDBusMessage") {
00255 params.append(const_cast<void *>(static_cast<const void *>(&msg)));
00256 } else {
00257 qWarning("Parameter mismatch while delivering message, expected '%s', got '%s'",
00258 msg.at(i).typeName(), param.constData());
00259 return false;
00260 }
00261 }
00262 return object->qt_metacall(QMetaObject::InvokeMetaMethod, idx, params.data()) < 0;
00263 }
00264
00265 static bool qInvokeDBusSlot(QObject *object, QDBusMessage *msg)
00266 {
00267 Q_ASSERT(object);
00268 Q_ASSERT(msg);
00269
00270 const QMetaObject *mo = object->metaObject();
00271 QVarLengthArray<void *> params;
00272 params.append(0);
00273
00274
00275 QByteArray slotName = msg->name().toUtf8();
00276 slotName.append("(");
00277 for (int i = 0; i < msg->count(); ++i) {
00278 slotName.append(msg->at(i).typeName()).append(",");
00279 params.append(const_cast<void *>(msg->at(i).constData()));
00280 }
00281 slotName.append("QDBusMessage)");
00282
00283 int idx = mo->indexOfSlot(slotName.constData());
00284 if (idx >= 0) {
00285 params.append(msg);
00286 return object->qt_metacall(QMetaObject::InvokeMetaMethod, idx, params.data()) < 0;
00287 }
00288
00289
00290 slotName.chop(13);
00291 slotName[slotName.count() - 1] = ')';
00292
00293 idx = mo->indexOfSlot(slotName.constData());
00294 if (idx >= 0 && (mo->method(idx).attributes() & QMetaMethod::Scriptable))
00295 return object->qt_metacall(QMetaObject::InvokeMetaMethod, idx, params.data()) < 0;
00296
00297
00298 slotName = msg->name().toUtf8();
00299 slotName.append("(QDBusMessage)");
00300
00301 idx = mo->indexOfSlot(slotName.constData());
00302 if (idx >= 0)
00303 return QMetaObject::invokeMethod(object, msg->name().toUtf8().constData(),
00304 Q_ARG(QDBusMessage, *msg));
00305
00306 return false;
00307 }
00308
00309 int QDBusConnectionPrivate::registerMessageMetaType()
00310 {
00311 int tp = messageMetaType = qRegisterMetaType<QDBusMessage>("QDBusMessage");
00312 return tp;
00313 }
00314
00315 bool QDBusConnectionPrivate::SignalHook::setSlot(const char *slotName)
00316 {
00317 Q_ASSERT(static_cast<QObject *>(obj)); Q_ASSERT(slotName);
00318
00319 QByteArray normalizedName = QMetaObject::normalizedSignature(slotName);
00320 const QMetaObject *mo = obj->metaObject();
00321 midx = mo->indexOfMethod(normalizedName.constData());
00322 if (midx < 0)
00323 return false;
00324
00325 const QList<QByteArray> ptypes = mo->method(midx).parameterTypes();
00326 for (int i = 0; i < ptypes.count(); ++i) {
00327 int t = QVariant::nameToType(ptypes.at(i).constData());
00328 if (t == QVariant::UserType)
00329 t = QMetaType::type(ptypes.at(i).constData());
00330 if (t == QVariant::Invalid)
00331 return false;
00332 params.append(t);
00333 }
00334
00335 return true;
00336 }
00337
00338 QDBusConnectionPrivate::QDBusConnectionPrivate(QObject *parent)
00339 : QObject(parent), ref(1), mode(InvalidMode), connection(0), server(0)
00340 {
00341 static const int msgType = registerMessageMetaType();
00342 Q_UNUSED(msgType);
00343
00344 dbus_error_init(&error);
00345 }
00346
00347 QDBusConnectionPrivate::~QDBusConnectionPrivate()
00348 {
00349 if (dbus_error_is_set(&error))
00350 dbus_error_free(&error);
00351
00352 closeConnection();
00353 }
00354
00355 void QDBusConnectionPrivate::closeConnection()
00356 {
00357 ConnectionMode oldMode = mode;
00358 mode = InvalidMode;
00359 if (oldMode == ServerMode) {
00360 if (server) {
00361 dbus_server_disconnect(server);
00362 dbus_server_unref(server);
00363 server = 0;
00364 }
00365 } else if (oldMode == ClientMode) {
00366 if (connection) {
00367 dbus_connection_close(connection);
00368
00369 while (dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS)
00370 ;
00371 dbus_connection_unref(connection);
00372 connection = 0;
00373 }
00374 }
00375 }
00376
00377 bool QDBusConnectionPrivate::handleError()
00378 {
00379 lastError = QDBusError(&error);
00380 if (dbus_error_is_set(&error))
00381 dbus_error_free(&error);
00382 return lastError.isValid();
00383 }
00384
00385 void QDBusConnectionPrivate::bindToApplication()
00386 {
00387
00388
00389 WatcherHash oldWatchers = watchers;
00390 watchers.clear();
00391 QHashIterator<int, QDBusConnectionPrivate::Watcher> it(oldWatchers);
00392 while (it.hasNext()) {
00393 it.next();
00394 if (!it.value().read && !it.value().write) {
00395 qDBusAddWatch(it.value().watch, this);
00396 }
00397 }
00398
00399
00400 while (!pendingTimeouts.isEmpty())
00401 qDBusAddTimeout(pendingTimeouts.takeFirst(), this);
00402 }
00403
00404 void QDBusConnectionPrivate::socketRead(int fd)
00405 {
00406 QHashIterator<int, QDBusConnectionPrivate::Watcher> it(watchers);
00407 while (it.hasNext()) {
00408 it.next();
00409 if (it.key() == fd && it.value().read && it.value().read->isEnabled()) {
00410 if (!dbus_watch_handle(it.value().watch, DBUS_WATCH_READABLE))
00411 qDebug("OUT OF MEM");
00412 }
00413 }
00414 if (mode == ClientMode)
00415 while (dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS);
00416
00417 }
00418
00419 void QDBusConnectionPrivate::socketWrite(int fd)
00420 {
00421 QHashIterator<int, QDBusConnectionPrivate::Watcher> it(watchers);
00422 while (it.hasNext()) {
00423 it.next();
00424 if (it.key() == fd && it.value().write && it.value().write->isEnabled()) {
00425 if (!dbus_watch_handle(it.value().watch, DBUS_WATCH_WRITABLE))
00426 qDebug("OUT OF MEM");
00427 }
00428 }
00429 }
00430
00431 void QDBusConnectionPrivate::objectDestroyed(QObject *obj)
00432 {
00433 ObjectHookHash::iterator it = objectHooks.begin();
00434 while (it != objectHooks.end()) {
00435 if (static_cast<QObject *>(it.value().obj) == obj)
00436 it = objectHooks.erase(it);
00437 else
00438 ++it;
00439 }
00440 SignalHookHash::iterator sit = signalHooks.begin();
00441 while (sit != signalHooks.end()) {
00442 if (static_cast<QObject *>(sit.value().obj) == obj)
00443 sit = signalHooks.erase(sit);
00444 else
00445 ++sit;
00446 }
00447 obj->disconnect(this);
00448 }
00449
00450 bool QDBusConnectionPrivate::handleObjectCall(DBusMessage *message) const
00451 {
00452 QDBusMessage msg = QDBusMessage::fromDBusMessage(message);
00453
00454 ObjectHook hook;
00455 ObjectHookHash::ConstIterator it = objectHooks.find(msg.path());
00456 while (it != objectHooks.constEnd() && it.key() == msg.path()) {
00457 if (it.value().interface == msg.interface()) {
00458 hook = it.value();
00459 break;
00460 } else if (it.value().interface.isEmpty()) {
00461 hook = it.value();
00462 }
00463 ++it;
00464 }
00465
00466 if (!hook.obj) {
00467 qDebug("NO OBJECT for %s", msg.path().toLocal8Bit().constData());
00468 return false;
00469 }
00470
00471 if (!qInvokeDBusSlot(hook.obj, &msg)) {
00472 qDebug("NO SUCH SLOT: %s(QDBusMessage)", msg.name().toLocal8Bit().constData());
00473 return false;
00474 }
00475
00476 return true;
00477 }
00478
00479 bool QDBusConnectionPrivate::handleSignal(const QString &path, const QDBusMessage &msg) const
00480 {
00481 SignalHookHash::const_iterator it = signalHooks.find(path);
00482 qDebug("looking for: %s", path.toLocal8Bit().constData());
00483 qDebug() << signalHooks.keys();
00484 while (it != signalHooks.constEnd() && it.key() == path) {
00485 const SignalHook &hook = it.value();
00486 if ((hook.name.isEmpty() || hook.name == msg.name())
00487 && (hook.interface.isEmpty() || hook.interface == msg.interface()))
00488 qInvokeDBusSlot(hook, msg);
00489 ++it;
00490 }
00491 return true;
00492 }
00493
00494 bool QDBusConnectionPrivate::handleSignal(DBusMessage *message) const
00495 {
00496 QDBusMessage msg = QDBusMessage::fromDBusMessage(message);
00497
00498
00499 return handleSignal(QString(), msg) | handleSignal(msg.path(), msg);
00500 }
00501
00502 static dbus_int32_t server_slot = -1;
00503
00504 void QDBusConnectionPrivate::setServer(DBusServer *s)
00505 {
00506 if (!server) {
00507 handleError();
00508 return;
00509 }
00510
00511 server = s;
00512 mode = ServerMode;
00513
00514 dbus_server_allocate_data_slot(&server_slot);
00515 if (server_slot < 0)
00516 return;
00517
00518 dbus_server_set_watch_functions(server, qDBusAddWatch, qDBusRemoveWatch,
00519 qDBusToggleWatch, this, 0);
00520 dbus_server_set_timeout_functions(server, qDBusAddTimeout, qDBusRemoveTimeout,
00521 qDBusToggleTimeout, this, 0);
00522 dbus_server_set_new_connection_function(server, qDBusNewConnection, this, 0);
00523
00524 dbus_server_set_data(server, server_slot, this, 0);
00525 }
00526
00527 void QDBusConnectionPrivate::setConnection(DBusConnection *dbc)
00528 {
00529 if (!dbc) {
00530 handleError();
00531 return;
00532 }
00533
00534 connection = dbc;
00535 mode = ClientMode;
00536
00537 dbus_connection_set_exit_on_disconnect(connection, false);
00538 dbus_connection_set_watch_functions(connection, qDBusAddWatch, qDBusRemoveWatch,
00539 qDBusToggleWatch, this, 0);
00540 dbus_connection_set_timeout_functions(connection, qDBusAddTimeout, qDBusRemoveTimeout,
00541 qDBusToggleTimeout, this, 0);
00542
00543
00544
00545 dbus_bus_add_match(connection, "type='signal'", &error);
00546 if (handleError()) {
00547 closeConnection();
00548 return;
00549 }
00550
00551 const char *service = dbus_bus_get_unique_name(connection);
00552 if (service) {
00553 QVarLengthArray<char, 56> filter;
00554 filter.append("destination='", 13);
00555 filter.append(service, qstrlen(service));
00556 filter.append("\'\0", 2);
00557
00558 dbus_bus_add_match(connection, filter.constData(), &error);
00559 if (handleError()) {
00560 closeConnection();
00561 return;
00562 }
00563 } else {
00564 qWarning("QDBusConnectionPrivate::SetConnection: Unable to get base service");
00565 }
00566
00567 dbus_connection_add_filter(connection, qDBusSignalFilter, this, 0);
00568
00569 qDebug("base service: %s", service);
00570 }
00571
00572 struct QDBusPendingCall
00573 {
00574 QPointer<QObject> receiver;
00575 int methodIdx;
00576 DBusPendingCall *pending;
00577 };
00578
00579 static void qDBusResultReceived(DBusPendingCall *pending, void *user_data)
00580 {
00581 QDBusPendingCall *call = reinterpret_cast<QDBusPendingCall *>(user_data);
00582 Q_ASSERT(call->pending == pending);
00583
00584 if (!call->receiver.isNull() && call->methodIdx != -1) {
00585 DBusMessage *reply = dbus_pending_call_steal_reply(pending);
00586 qInvokeDBusSlot(call->receiver, call->methodIdx, QDBusMessage::fromDBusMessage(reply));
00587 }
00588 dbus_pending_call_unref(pending);
00589 delete call;
00590 }
00591
00592 int QDBusConnectionPrivate::sendWithReplyAsync(const QDBusMessage &message, QObject *receiver,
00593 const char *method) const
00594 {
00595 DBusMessage *msg = message.toDBusMessage();
00596 if (!msg)
00597 return 0;
00598
00599 int slotIdx = -1;
00600 if (receiver && method && *method) {
00601 QByteArray normalized = QMetaObject::normalizedSignature(method + 1);
00602 slotIdx = receiver->metaObject()->indexOfMethod(normalized.constData());
00603 if (slotIdx == -1)
00604 qWarning("QDBusConnection::sendWithReplyAsync: no such method: '%s'",
00605 normalized.constData());
00606 }
00607
00608 DBusPendingCall *pending = 0;
00609 if (dbus_connection_send_with_reply(connection, msg, &pending, message.timeout())) {
00610 if (slotIdx != -1) {
00611 QDBusPendingCall *pcall = new QDBusPendingCall;
00612 pcall->receiver = receiver;
00613 pcall->methodIdx = slotIdx;
00614 pcall->pending = dbus_pending_call_ref(pending);
00615 dbus_pending_call_set_notify(pending, qDBusResultReceived, pcall, 0);
00616 }
00617 return dbus_message_get_serial(msg);
00618 }
00619
00620 return 0;
00621 }