qdbusintegrator.cpp

00001 /* qdbusintegrator.cpp QDBusConnection private implementation
00002  *
00003  * Copyright (C) 2005 Harald Fernengel <harry@kdevelop.org>
00004  *
00005  * Licensed under the Academic Free License version 2.1
00006  *
00007  * This program is free software; you can redistribute it and/or modify
00008  * it under the terms of the GNU General Public License as published by
00009  * the Free Software Foundation; either version 2 of the License, or
00010  * (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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   //  qDebug("addTimeout %d", dbus_timeout_get_interval(timeout));
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   //  qDebug("removeTimeout");
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"); // TODO
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); // return value
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); // ### return type
00235 
00236     QList<QByteArray> parameterTypes = method.parameterTypes();
00237 
00238     // check parameters, the slot should have <= parameters than the message
00239     // also allow the QDBusMessage itself as last parameter slot
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); // ### return type
00273 
00274     /* Try to find a slot with all args and the QDBusMessage */
00275     QByteArray slotName = msg->name().toUtf8(); // QVarLengthArray?
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     /* Try to find only args, without the QDBusMessage */
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     /* Try to find a slot with only QDBusMessage */
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; // prevent reentrancy
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             // send the "close" message
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     // Yay, now that we have an application we are in business
00388     // Re-add all watchers
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     // Re-add all timeouts
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         // ### break out of loop?
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     // yes, it is a single "|" below...
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); // ### check return type?
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 //    dbus_bus_add_match(connection, "type='signal',interface='com.trolltech.dbus.Signal'", &error);
00543 //    dbus_bus_add_match(connection, "type='signal'", &error);
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 }

Generated on Wed Feb 27 10:13:38 2008 for D-BUS by  doxygen 1.4.6