tools/qdbus/src/qdbusintegrator.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 **
00003 ** Copyright (C) 1992-2006 Trolltech ASA. All rights reserved.
00004 **
00005 ** This file is part of the tools applications of the Qt Toolkit.
00006 **
00007 ** This file may be used under the terms of the GNU General Public
00008 ** License version 2.0 as published by the Free Software Foundation
00009 ** and appearing in the file LICENSE.GPL included in the packaging of
00010 ** this file.  Please review the following information to ensure GNU
00011 ** General Public Licensing requirements will be met:
00012 ** http://www.trolltech.com/products/qt/opensource.html
00013 **
00014 ** If you are unsure which license is appropriate for your use, please
00015 ** review the following information:
00016 ** http://www.trolltech.com/products/qt/licensing.html or contact the
00017 ** sales department at sales@trolltech.com.
00018 **
00019 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00020 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00021 **
00022 ****************************************************************************/
00023 
00024 #include <qcoreapplication.h>
00025 #include <qcoreevent.h>
00026 #include <qdebug.h>
00027 #include <qmetaobject.h>
00028 #include <qobject.h>
00029 #include <qsocketnotifier.h>
00030 #include <qstringlist.h>
00031 #include <qtimer.h>
00032 
00033 #include "qdbusargument.h"
00034 #include "qdbusconnection_p.h"
00035 #include "qdbusinterface_p.h"
00036 #include "qdbusmessage.h"
00037 #include "qdbusmetatype.h"
00038 #include "qdbusmetatype_p.h"
00039 #include "qdbusabstractadaptor.h"
00040 #include "qdbusabstractadaptor_p.h"
00041 #include "qdbusutil_p.h"
00042 #include "qdbusmessage_p.h"
00043 
00044 static bool isDebugging;
00045 #define qDBusDebug              if (!::isDebugging); else qDebug
00046 
00047 #ifndef USE_OUTSIDE_DISPATCH
00048 # define USE_OUTSIDE_DISPATCH    0
00049 #endif
00050 
00051 typedef void (*QDBusSpyHook)(const QDBusMessage&);
00052 typedef QVarLengthArray<QDBusSpyHook, 4> QDBusSpyHookList;
00053 Q_GLOBAL_STATIC(QDBusSpyHookList, qDBusSpyHookList)
00054 
00055 Q_GLOBAL_STATIC(QStringList, qDBusServicesRegisteredByThread)
00056 
00057 struct QDBusPendingCall
00058 {
00059     QPointer<QObject> receiver;
00060     QList<int> metaTypes;
00061     int methodIdx;
00062     DBusPendingCall *pending;
00063     const QDBusConnectionPrivate *connection;
00064 };
00065 
00066 class CallDeliveryEvent: public QEvent
00067 {
00068 public:
00069     CallDeliveryEvent()
00070         : QEvent(QEvent::User), object(0), flags(0), slotIdx(-1)
00071         { }
00072 
00073     const QDBusConnectionPrivate *conn;
00074     QPointer<QObject> object;
00075     QDBusMessage message;
00076     QList<int> metaTypes;
00077 
00078     int flags;
00079     int slotIdx;
00080 };
00081 
00082 static dbus_bool_t qDBusAddTimeout(DBusTimeout *timeout, void *data)
00083 {
00084     Q_ASSERT(timeout);
00085     Q_ASSERT(data);
00086 
00087   //  qDebug("addTimeout %d", dbus_timeout_get_interval(timeout));
00088 
00089     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
00090 
00091     if (!dbus_timeout_get_enabled(timeout))
00092         return true;
00093 
00094     if (!QCoreApplication::instance()) {
00095         d->pendingTimeouts.append(timeout);
00096         return true;
00097     }
00098     int timerId = d->startTimer(dbus_timeout_get_interval(timeout));
00099     if (!timerId)
00100         return false;
00101 
00102     d->timeouts[timerId] = timeout;
00103     return true;
00104 }
00105 
00106 static void qDBusRemoveTimeout(DBusTimeout *timeout, void *data)
00107 {
00108     Q_ASSERT(timeout);
00109     Q_ASSERT(data);
00110 
00111   //  qDebug("removeTimeout");
00112 
00113     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
00114     d->pendingTimeouts.removeAll(timeout);
00115 
00116     QDBusConnectionPrivate::TimeoutHash::iterator it = d->timeouts.begin();
00117     while (it != d->timeouts.end()) {
00118         if (it.value() == timeout) {
00119             d->killTimer(it.key());
00120             it = d->timeouts.erase(it);
00121         } else {
00122             ++it;
00123         }
00124     }
00125 }
00126 
00127 static void qDBusToggleTimeout(DBusTimeout *timeout, void *data)
00128 {
00129     Q_ASSERT(timeout);
00130     Q_ASSERT(data);
00131 
00132     //qDebug("ToggleTimeout");
00133 
00134     qDBusRemoveTimeout(timeout, data);
00135     qDBusAddTimeout(timeout, data);
00136 }
00137 
00138 static dbus_bool_t qDBusAddWatch(DBusWatch *watch, void *data)
00139 {
00140     Q_ASSERT(watch);
00141     Q_ASSERT(data);
00142 
00143     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
00144 
00145     int flags = dbus_watch_get_flags(watch);
00146     int fd = dbus_watch_get_fd(watch);
00147 
00148     QDBusConnectionPrivate::Watcher watcher;
00149     if (flags & DBUS_WATCH_READABLE) {
00150         //qDebug("addReadWatch %d", fd);
00151         watcher.watch = watch;
00152         if (QCoreApplication::instance()) {
00153             watcher.read = new QSocketNotifier(fd, QSocketNotifier::Read, d);
00154             watcher.read->setEnabled(dbus_watch_get_enabled(watch));
00155             d->connect(watcher.read, SIGNAL(activated(int)), SLOT(socketRead(int)));
00156         }
00157     }
00158     if (flags & DBUS_WATCH_WRITABLE) {
00159         //qDebug("addWriteWatch %d", fd);
00160         watcher.watch = watch;
00161         if (QCoreApplication::instance()) {
00162             watcher.write = new QSocketNotifier(fd, QSocketNotifier::Write, d);
00163             watcher.write->setEnabled(dbus_watch_get_enabled(watch));
00164             d->connect(watcher.write, SIGNAL(activated(int)), SLOT(socketWrite(int)));
00165         }
00166     }
00167     d->watchers.insertMulti(fd, watcher);
00168 
00169     return true;
00170 }
00171 
00172 static void qDBusRemoveWatch(DBusWatch *watch, void *data)
00173 {
00174     Q_ASSERT(watch);
00175     Q_ASSERT(data);
00176 
00177     //qDebug("remove watch");
00178 
00179     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
00180     int fd = dbus_watch_get_fd(watch);
00181 
00182     QDBusConnectionPrivate::WatcherHash::iterator i = d->watchers.find(fd);
00183     while (i != d->watchers.end() && i.key() == fd) {
00184         if (i.value().watch == watch) {
00185             delete i.value().read;
00186             delete i.value().write;
00187             d->watchers.erase(i);
00188             return;
00189         }
00190         ++i;
00191     }
00192 }
00193 
00194 static void qDBusToggleWatch(DBusWatch *watch, void *data)
00195 {
00196     Q_ASSERT(watch);
00197     Q_ASSERT(data);
00198 
00199     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
00200     int fd = dbus_watch_get_fd(watch);
00201 
00202     QDBusConnectionPrivate::WatcherHash::iterator i = d->watchers.find(fd);
00203     while (i != d->watchers.end() && i.key() == fd) {
00204         if (i.value().watch == watch) {
00205             bool enabled = dbus_watch_get_enabled(watch);
00206             int flags = dbus_watch_get_flags(watch);
00207 
00208             //qDebug("toggle watch %d to %d (write: %d, read: %d)", dbus_watch_get_fd(watch), enabled, flags & DBUS_WATCH_WRITABLE, flags & DBUS_WATCH_READABLE);
00209 
00210             if (flags & DBUS_WATCH_READABLE && i.value().read)
00211                 i.value().read->setEnabled(enabled);
00212             if (flags & DBUS_WATCH_WRITABLE && i.value().write)
00213                 i.value().write->setEnabled(enabled);
00214             return;
00215         }
00216         ++i;
00217     }
00218 }
00219 
00220 static void qDBusNewConnection(DBusServer *server, DBusConnection *c, void *data)
00221 {
00222     Q_ASSERT(data); Q_ASSERT(server); Q_ASSERT(c);
00223     Q_UNUSED(data); Q_UNUSED(server); Q_UNUSED(c);
00224 
00225     qDebug("SERVER: GOT A NEW CONNECTION"); // TODO
00226 }
00227 
00228 extern QDBUS_EXPORT void qDBusAddSpyHook(QDBusSpyHook);
00229 void qDBusAddSpyHook(QDBusSpyHook hook)
00230 {
00231     qDBusSpyHookList()->append(hook);
00232 }
00233 
00234 #if USE_OUTSIDE_DISPATCH
00235 # define HANDLED     DBUS_HANDLER_RESULT_HANDLED_OUTSIDE_DISPATCH
00236 static DBusHandlerResult qDBusSignalFilterOutside(DBusConnection *connection,
00237                                                   DBusMessage *message, void *data)
00238 {
00239     Q_ASSERT(data);
00240     Q_UNUSED(connection);
00241     Q_UNUSED(message);
00242 
00243     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
00244     if (d->mode == QDBusConnectionPrivate::InvalidMode)
00245         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; // internal error, actually
00246 
00247     CallDeliveryEvent *e = d->postedCallDeliveryEvent();
00248 
00249     d->deliverCall(*e);
00250     delete e;
00251 
00252     return DBUS_HANDLER_RESULT_HANDLED;
00253 }
00254 #else
00255 # define HANDLED     DBUS_HANDLER_RESULT_HANDLED
00256 #endif
00257 
00258 extern "C" {
00259 static DBusHandlerResult
00260 qDBusSignalFilter(DBusConnection *connection, DBusMessage *message, void *data)
00261 {
00262     return QDBusConnectionPrivate::messageFilter(connection, message, data);
00263 }
00264 }
00265 
00266 DBusHandlerResult QDBusConnectionPrivate::messageFilter(DBusConnection *connection,
00267                                                         DBusMessage *message, void *data)
00268 {
00269     Q_ASSERT(data);
00270     Q_UNUSED(connection);
00271 
00272     QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data);
00273     if (d->mode == QDBusConnectionPrivate::InvalidMode)
00274         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00275 
00276     QDBusMessage amsg = QDBusMessagePrivate::fromDBusMessage(message);
00277     qDBusDebug() << "got message:" << amsg;
00278 
00279     const QDBusSpyHookList *list = qDBusSpyHookList();
00280     for (int i = 0; i < list->size(); ++i) {
00281         qDBusDebug() << "calling the message spy hook";
00282         (*(*list)[i])(amsg);
00283     }
00284 
00285     bool handled = false;
00286     int msgType = dbus_message_get_type(message);
00287     if (msgType == DBUS_MESSAGE_TYPE_SIGNAL) {
00288         handled = d->handleSignal(amsg);
00289     } else if (msgType == DBUS_MESSAGE_TYPE_METHOD_CALL) {
00290         handled = d->handleObjectCall(amsg);
00291     }
00292 
00293     return handled ? HANDLED :
00294         DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00295 }
00296 
00297 static void huntAndDestroy(QObject *needle, QDBusConnectionPrivate::ObjectTreeNode *haystack)
00298 {
00299     QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator it = haystack->children.constBegin();
00300     QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator end = haystack->children.constEnd();
00301     for ( ; it != end; ++it)
00302         huntAndDestroy(needle, it->node);
00303 
00304     if (needle == haystack->obj) {
00305         haystack->obj = 0;
00306         haystack->flags = 0;
00307     }
00308 }
00309 
00310 static void huntAndEmit(DBusConnection *connection, DBusMessage *msg,
00311                         QObject *needle, QDBusConnectionPrivate::ObjectTreeNode *haystack,
00312                         bool isScriptable, bool isAdaptor, const QString &path = QString())
00313 {
00314     QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator it = haystack->children.constBegin();
00315     QDBusConnectionPrivate::ObjectTreeNode::DataList::ConstIterator end = haystack->children.constEnd();
00316     for ( ; it != end; ++it)
00317         huntAndEmit(connection, msg, needle, it->node, isScriptable, isAdaptor, path + QLatin1String("/") + it->name);
00318 
00319     if (needle == haystack->obj) {
00320         // is this a signal we should relay?
00321         if (isAdaptor && (haystack->flags & QDBusConnection::ExportAdaptors) == 0)
00322             return;             // no: it comes from an adaptor and we're not exporting adaptors
00323         else if (!isAdaptor) {
00324             int mask = isScriptable
00325                        ? QDBusConnection::ExportScriptableSignals
00326                        : QDBusConnection::ExportNonScriptableSignals;
00327             if ((haystack->flags & mask) == 0)
00328                 return;         // signal was not exported
00329         }
00330 
00331         QByteArray p = path.toLatin1();
00332         if (p.isEmpty())
00333             p = "/";
00334         qDBusDebug() << "Emitting signal at " << p;
00335         DBusMessage *msg2 = dbus_message_copy(msg);
00336         dbus_message_set_path(msg2, p);
00337         dbus_connection_send(connection, msg2, 0);
00338         dbus_message_unref(msg2);
00339     }
00340 }
00341 
00342 static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags,
00343                     const QString &signature_, QList<int>& metaTypes)
00344 {
00345     QByteArray msgSignature = signature_.toLatin1();
00346 
00347     for (int idx = mo->methodCount() - 1 ; idx >= QObject::staticMetaObject.methodCount(); --idx) {
00348         QMetaMethod mm = mo->method(idx);
00349 
00350         // check access:
00351         if (mm.access() != QMetaMethod::Public)
00352             continue;
00353 
00354         // check type:
00355         // unnecessary, since signals are never public:
00356         //if (mm.methodType() != QMetaMethod::Slot)
00357         //    continue;
00358 
00359         // check name:
00360         QByteArray slotname = mm.signature();
00361         int paren = slotname.indexOf('(');
00362         if (paren != name.length() || !slotname.startsWith(name))
00363             continue;
00364 
00365         int returnType = qDBusNameToTypeId(mm.typeName());
00366         bool isAsync = qDBusCheckAsyncTag(mm.tag());
00367         bool isScriptable = mm.attributes() & QMetaMethod::Scriptable;
00368 
00369         // consistency check:
00370         if (isAsync && returnType != QMetaType::Void)
00371             continue;
00372 
00373         int inputCount = qDBusParametersForMethod(mm, metaTypes);
00374         if (inputCount == -1)
00375             continue;           // problem parsing
00376 
00377         metaTypes[0] = returnType;
00378         bool hasMessage = false;
00379         if (inputCount > 0 &&
00380             metaTypes.at(inputCount) == QDBusMetaTypeId::message) {
00381             // "no input parameters" is allowed as long as the message meta type is there
00382             hasMessage = true;
00383             --inputCount;
00384         }
00385 
00386         // try to match the parameters
00387         int i;
00388         QByteArray reconstructedSignature;
00389         for (i = 1; i <= inputCount; ++i) {
00390             const char *typeSignature = QDBusMetaType::typeToSignature( metaTypes.at(i) );
00391             if (!typeSignature)
00392                 break;          // invalid
00393 
00394             reconstructedSignature += typeSignature;
00395             if (!msgSignature.startsWith(reconstructedSignature))
00396                 break;
00397         }
00398 
00399         if (reconstructedSignature != msgSignature)
00400             continue;           // we didn't match them all
00401 
00402         if (hasMessage)
00403             ++i;
00404 
00405         // make sure that the output parameters have signatures too
00406         if (returnType != 0 && QDBusMetaType::typeToSignature(returnType) == 0)
00407             continue;
00408 
00409         bool ok = true;
00410         for (int j = i; ok && j < metaTypes.count(); ++j)
00411             if (QDBusMetaType::typeToSignature(metaTypes.at(i)) == 0)
00412                 ok = false;
00413         if (!ok)
00414             continue;
00415 
00416         // consistency check:
00417         if (isAsync && metaTypes.count() > i + 1)
00418             continue;
00419 
00420         if (isScriptable && (flags & QDBusConnection::ExportScriptableSlots) == 0)
00421             continue;           // not exported
00422         if (!isScriptable && (flags & QDBusConnection::ExportNonScriptableSlots) == 0)
00423             continue;           // not exported
00424 
00425         // if we got here, this slot matched
00426         return idx;
00427     }
00428 
00429     // no slot matched
00430     return -1;
00431 }
00432 
00433 static CallDeliveryEvent* prepareReply(QObject *object, int idx, const QList<int> &metaTypes,
00434                                        const QDBusMessage &msg)
00435 {
00436     Q_ASSERT(object);
00437 
00438     int n = metaTypes.count() - 1;
00439     if (metaTypes[n] == QDBusMetaTypeId::message)
00440         --n;
00441 
00442     // check that types match
00443     for (int i = 0; i < n; ++i)
00444         if (metaTypes.at(i + 1) != msg.arguments().at(i).userType() &&
00445             msg.arguments().at(i).userType() != qMetaTypeId<QDBusArgument>())
00446             return 0;           // no match
00447 
00448     // we can deliver
00449     // prepare for the call
00450     CallDeliveryEvent *data = new CallDeliveryEvent;
00451     data->object = object;
00452     data->flags = 0;
00453     data->message = msg;
00454     data->metaTypes = metaTypes;
00455     data->slotIdx = idx;
00456 
00457     return data;
00458 }
00459 
00460 bool QDBusConnectionPrivate::activateSignal(const QDBusConnectionPrivate::SignalHook& hook,
00461                                             const QDBusMessage &msg)
00462 {
00463     // This is called by QDBusConnectionPrivate::handleSignal to deliver a signal
00464     // that was received from D-Bus
00465     //
00466     // Signals are delivered to slots if the parameters match
00467     // Slots can have less parameters than there are on the message
00468     // Slots can optionally have one final parameter that is a QDBusMessage
00469     // Slots receive read-only copies of the message (i.e., pass by value or by const-ref)
00470     CallDeliveryEvent *call = prepareReply(hook.obj, hook.midx, hook.params, msg);
00471     if (call) {
00472         postCallDeliveryEvent(call);
00473         return true;
00474     }
00475     return false;
00476 }
00477 
00478 bool QDBusConnectionPrivate::activateCall(QObject* object, int flags,
00479                                           const QDBusMessage &msg)
00480 {
00481     // This is called by QDBusConnectionPrivate::handleObjectCall to place a call
00482     // to a slot on the object.
00483     //
00484     // The call is delivered to the first slot that matches the following conditions:
00485     //  - has the same name as the message's target member
00486     //  - ALL of the message's types are found in slot's parameter list
00487     //  - optionally has one more parameter of type QDBusMessage
00488     // If none match, then the slot of the same name as the message target and with
00489     // the first type of QDBusMessage is delivered.
00490     //
00491     // The D-Bus specification requires that all MethodCall messages be replied to, unless the
00492     // caller specifically waived this requirement. This means that we inspect if the user slot
00493     // generated a reply and, if it didn't, we will. Obviously, if the user slot doesn't take a
00494     // QDBusMessage parameter, it cannot generate a reply.
00495     //
00496     // When a return message is generated, the slot's return type, if any, will be placed
00497     // in the message's first position. If there are non-const reference parameters to the
00498     // slot, they must appear at the end and will be placed in the subsequent message
00499     // positions.
00500 
00501     if (!object)
00502         return false;
00503 
00504     QList<int> metaTypes;
00505     int idx;
00506 
00507     {
00508         const QMetaObject *mo = object->metaObject();
00509         QByteArray memberName = msg.member().toUtf8();
00510 
00511         // find a slot that matches according to the rules above
00512         idx = ::findSlot(mo, memberName, flags, msg.signature(), metaTypes);
00513         if (idx == -1) {
00514             // ### this is where we want to add the connection as an arg too
00515             // try with no parameters, but with a QDBusMessage
00516             idx = ::findSlot(mo, memberName, flags, QString(), metaTypes);
00517             if (metaTypes.count() != 2 || metaTypes.at(1) != QDBusMetaTypeId::message) {
00518                 return false;
00519     }
00520         }
00521     }
00522 
00523 
00524     // found the slot to be called
00525     // prepare for the call:
00526     CallDeliveryEvent *call = new CallDeliveryEvent;
00527 
00528     // parameters:
00529     call->object = object;
00530     call->flags = flags;
00531     call->message = msg;
00532 
00533     // save our state:
00534     call->metaTypes = metaTypes;
00535     call->slotIdx = idx;
00536 
00537     if (QDBusMessagePrivate::isLocal(msg)) {
00538         //qDebug() << "QDBusConnectionPrivate::activateCall" << msg.d_ptr->msg;
00539         sendCallDeliveryEvent(call);
00540     } else {
00541         postCallDeliveryEvent(call);
00542     }
00543 
00544     // ready
00545     return true;
00546 }
00547 
00548 void QDBusConnectionPrivate::sendCallDeliveryEvent(CallDeliveryEvent *data)
00549 {
00550     Q_ASSERT(data);
00551     data->conn = this;
00552 #if USE_OUTSIDE_DISPATCH
00553     callDeliveryMutex.lock();
00554     callDeliveryState = data;
00555 #else
00556     QCoreApplication::sendEvent(this, data);
00557 #endif
00558 }
00559 
00560 void QDBusConnectionPrivate::postCallDeliveryEvent(CallDeliveryEvent *data)
00561 {
00562     Q_ASSERT(data);
00563     data->conn = this;
00564 #if USE_OUTSIDE_DISPATCH
00565     callDeliveryMutex.lock();
00566     callDeliveryState = data;
00567 #else
00568     QCoreApplication::postEvent(this, data);
00569 #endif
00570 }
00571 
00572 CallDeliveryEvent *QDBusConnectionPrivate::postedCallDeliveryEvent()
00573 {
00574     CallDeliveryEvent *e = callDeliveryState;
00575     Q_ASSERT(e && e->conn == this);
00576 
00577     // release it:
00578     callDeliveryState = 0;
00579     callDeliveryMutex.unlock();
00580 
00581     return e;
00582 }
00583 
00584 void QDBusConnectionPrivate::deliverCall(const CallDeliveryEvent& data) const
00585 {
00586     // resume state:
00587     const QList<int>& metaTypes = data.metaTypes;
00588     const QDBusMessage& msg = data.message;
00589 
00590     QVarLengthArray<void *, 10> params;
00591     params.reserve(metaTypes.count());
00592 
00593     QVariantList auxParameters;
00594     // let's create the parameter list
00595 
00596     // first one is the return type -- add it below
00597     params.append(0);
00598 
00599     // add the input parameters
00600     int i;
00601     for (i = 1; i <= qMin(msg.arguments().count(), metaTypes.count() - 1); ++i) {
00602         int id = metaTypes[i];
00603         if (id == QDBusMetaTypeId::message)
00604             break;
00605 
00606         if (id == int(msg.arguments().at(i - 1).userType()))
00607             // no conversion needed
00608             params.append(const_cast<void *>(msg.arguments().at(i - 1).constData() ));
00609         else if (msg.arguments().at(i - 1).userType() == qMetaTypeId<QDBusArgument>()) {
00610             // convert to what the function expects
00611             void *null = 0;
00612             auxParameters.append(QVariant(id, null));
00613 
00614             const QDBusArgument &in =
00615                 *reinterpret_cast<const QDBusArgument *>(msg.arguments().at(i - 1).constData());
00616             QVariant &out = auxParameters[auxParameters.count() - 1];
00617 
00618             if (!QDBusMetaType::demarshall(in, out.userType(), out.data()))
00619                 qFatal("Internal error: demarshalling function for type '%s' (%d) failed!",
00620                        out.typeName(), out.userType());
00621 
00622             params.append(const_cast<void *>(out.constData()) );
00623         } else {
00624             qFatal("Internal error: got invalid meta type %d when trying to convert to meta type %d",
00625                    msg.arguments().at(i - 1).userType(), id);
00626         }
00627     }
00628 
00629     bool takesMessage = false;
00630     if (metaTypes.count() > i && metaTypes[i] == QDBusMetaTypeId::message) {
00631         params.append(const_cast<void*>(static_cast<const void*>(&msg)));
00632         takesMessage = true;
00633         ++i;
00634     }
00635 
00636     // output arguments
00637     QVariantList outputArgs;
00638     void *null = 0;
00639     if (metaTypes[0] != QMetaType::Void) {
00640         QVariant arg(metaTypes[0], null);
00641         outputArgs.append( arg );
00642         params[0] = const_cast<void*>(outputArgs.at( outputArgs.count() - 1 ).constData());
00643     }
00644     for ( ; i < metaTypes.count(); ++i) {
00645         QVariant arg(metaTypes[i], null);
00646         outputArgs.append( arg );
00647         params.append(const_cast<void*>(outputArgs.at( outputArgs.count() - 1 ).constData()));
00648     }
00649 
00650     // make call:
00651     bool fail;
00652     if (data.object.isNull()) {
00653         fail = true;
00654     } else {
00655         QDBusConnectionPrivate::setSender(this);
00656         fail = data.object->qt_metacall(QMetaObject::InvokeMetaMethod,
00657                                         data.slotIdx, params.data()) >= 0;
00658         QDBusConnectionPrivate::setSender(0);
00659     }
00660 
00661     // do we create a reply? Only if the caller is waiting for a reply and one hasn't been sent
00662     // yet.
00663     if (QDBusMessagePrivate::isLocal(msg) && !fail) {
00664         // a little hack to enable local calls to return values
00665         //qDebug() << "QDBusConnectionPrivate::deliverCall" << outputArgs;
00666         QDBusMessagePrivate::setArguments(&msg, outputArgs);
00667         QDBusMessagePrivate::setType(&msg, QDBusMessage::ReplyMessage);
00668         return;
00669     }
00670 
00671     if (!msg.isReplyRequired() && !msg.isDelayedReply()) {
00672         if (!fail) {
00673             // normal reply
00674             qDBusDebug() << "Automatically sending reply:" << outputArgs;
00675             send(msg.createReply(outputArgs));
00676         } else {
00677             // generate internal error
00678             qWarning("Internal error: Failed to deliver message");
00679             send(QDBusMessage::createError(QDBusError(QDBusError::InternalError,
00680                                            QLatin1String("Failed to deliver message"))));
00681         }
00682     }
00683 
00684     return;
00685 }
00686 
00687 void QDBusConnectionPrivate::customEvent(QEvent *e)
00688 {
00689     // nothing else should be sending custom events at us
00690     CallDeliveryEvent* call = static_cast<CallDeliveryEvent *>(e);
00691 
00692     // self check:
00693     Q_ASSERT(call->conn == this);
00694 
00695     deliverCall(*call);
00696 }
00697 
00698 QDBusConnectionPrivate::QDBusConnectionPrivate(QObject *p)
00699     : QObject(p), ref(1), mode(InvalidMode), connection(0), server(0), busService(0)
00700 {
00701     extern bool qDBusInitThreads();
00702     static const bool threads = qDBusInitThreads();
00703     static const bool debugging = !qgetenv("QDBUS_DEBUG").isEmpty();
00704 
00705     Q_UNUSED(threads);
00706     ::isDebugging = debugging;
00707 
00708     QDBusMetaTypeId::init();
00709     dbus_error_init(&error);
00710 
00711     rootNode.flags = 0;
00712 
00713     connect(this, SIGNAL(serviceOwnerChanged(QString,QString,QString)),
00714             this, SLOT(_q_serviceOwnerChanged(QString,QString,QString)));
00715 }
00716 
00717 QDBusConnectionPrivate::~QDBusConnectionPrivate()
00718 {
00719     if (dbus_error_is_set(&error))
00720         dbus_error_free(&error);
00721 
00722     closeConnection();
00723     rootNode.clear();        // free resources
00724     qDeleteAll(cachedMetaObjects);
00725 }
00726 
00727 void QDBusConnectionPrivate::closeConnection()
00728 {
00729     QWriteLocker locker(&lock);
00730     ConnectionMode oldMode = mode;
00731     mode = InvalidMode; // prevent reentrancy
00732     if (oldMode == ServerMode) {
00733         if (server) {
00734             dbus_server_disconnect(server);
00735             dbus_server_unref(server);
00736             server = 0;
00737         }
00738     } else if (oldMode == ClientMode) {
00739         if (connection) {
00740             dbus_connection_close(connection);
00741             // send the "close" message
00742             while (dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS)
00743                 ;
00744             dbus_connection_unref(connection);
00745             connection = 0;
00746         }
00747     }
00748 }
00749 
00750 bool QDBusConnectionPrivate::handleError()
00751 {
00752     lastError = QDBusError(&error);
00753     if (dbus_error_is_set(&error))
00754         dbus_error_free(&error);
00755     return lastError.isValid();
00756 }
00757 
00758 void QDBusConnectionPrivate::bindToApplication()
00759 {
00760     // Yay, now that we have an application we are in business
00761     Q_ASSERT_X(QCoreApplication::instance(), "QDBusConnection",
00762                "qDBusBindToApplication called without an application");
00763     moveToThread(QCoreApplication::instance()->thread());
00764 
00765     // Re-add all watchers
00766     WatcherHash oldWatchers = watchers;
00767     watchers.clear();
00768     QHashIterator<int, QDBusConnectionPrivate::Watcher> it(oldWatchers);
00769     while (it.hasNext()) {
00770         it.next();
00771         if (!it.value().read && !it.value().write) {
00772             qDBusAddWatch(it.value().watch, this);
00773         } else {
00774             watchers.insertMulti(it.key(), it.value());
00775         }
00776     }
00777 
00778     // Re-add all timeouts
00779     while (!pendingTimeouts.isEmpty())
00780        qDBusAddTimeout(pendingTimeouts.takeFirst(), this);
00781 }
00782 
00783 void QDBusConnectionPrivate::timerEvent(QTimerEvent *e)
00784 {
00785     DBusTimeout *timeout = timeouts.value(e->timerId(), 0);
00786     dbus_timeout_handle(timeout);
00787 }
00788 
00789 void QDBusConnectionPrivate::doDispatch()
00790 {
00791     if (mode == ClientMode)
00792         while (dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS);
00793 }
00794 
00795 void QDBusConnectionPrivate::socketRead(int fd)
00796 {
00797     QHashIterator<int, QDBusConnectionPrivate::Watcher> it(watchers);
00798     while (it.hasNext()) {
00799         it.next();
00800         if (it.key() == fd && it.value().read && it.value().read->isEnabled()) {
00801             if (!dbus_watch_handle(it.value().watch, DBUS_WATCH_READABLE))
00802                 qDebug("OUT OF MEM");
00803         }
00804     }
00805 
00806     doDispatch();
00807 }
00808 
00809 void QDBusConnectionPrivate::socketWrite(int fd)
00810 {
00811     QHashIterator<int, QDBusConnectionPrivate::Watcher> it(watchers);
00812     while (it.hasNext()) {
00813         it.next();
00814         if (it.key() == fd && it.value().write && it.value().write->isEnabled()) {
00815             if (!dbus_watch_handle(it.value().watch, DBUS_WATCH_WRITABLE))
00816                 qDebug("OUT OF MEM");
00817         }
00818     }
00819 }
00820 
00821 void QDBusConnectionPrivate::objectDestroyed(QObject *obj)
00822 {
00823     QWriteLocker locker(&lock);
00824     huntAndDestroy(obj, &rootNode);
00825 
00826     SignalHookHash::iterator sit = signalHooks.begin();
00827     while (sit != signalHooks.end()) {
00828         if (static_cast<QObject *>(sit.value().obj) == obj)
00829             sit = signalHooks.erase(sit);
00830         else
00831             ++sit;
00832     }
00833 
00834     obj->disconnect(this);
00835 }
00836 
00837 void QDBusConnectionPrivate::relaySignal(QObject *obj, const QMetaObject *mo, int signalId,
00838                                          const QVariantList &args)
00839 {
00840     int mciid = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTERFACE);
00841     Q_ASSERT(mciid != -1);
00842 
00843     QMetaClassInfo mci = mo->classInfo(mciid);
00844     Q_ASSERT(mci.value());
00845     const char *interface = mci.value();
00846 
00847     QMetaMethod mm = mo->method(signalId);
00848     QByteArray memberName = mm.signature();
00849     memberName.truncate(memberName.indexOf('('));
00850 
00851     // check if it's scriptable
00852     bool isScriptable = mm.attributes() & QMetaMethod::Scriptable;
00853     bool isAdaptor = false;
00854     for ( ; mo; mo = mo->superClass())
00855         if (mo == &QDBusAbstractAdaptor::staticMetaObject) {
00856             isAdaptor = true;
00857             break;
00858         }
00859 
00860     QReadLocker locker(&lock);
00861     QDBusMessage message = QDBusMessage::createSignal(QLatin1String("/"), QLatin1String(interface),
00862                                                       QLatin1String(memberName));
00863     message.setArguments(args);
00864     DBusMessage *msg = QDBusMessagePrivate::toDBusMessage(message);
00865     if (!msg) {
00866         qWarning("QDBusConnection: Could not emit signal %s.%s", interface, memberName.constData());
00867         return;
00868     }
00869 
00870     //qDBusDebug() << "Emitting signal" << message;
00871     //qDBusDebug() << "for paths:";
00872     dbus_message_set_no_reply(msg, true); // the reply would not be delivered to anything
00873     huntAndEmit(connection, msg, obj, &rootNode, isScriptable, isAdaptor);
00874     dbus_message_unref(msg);
00875 }
00876 
00877 void QDBusConnectionPrivate::_q_serviceOwnerChanged(const QString &name,
00878                                                     const QString &oldOwner, const QString &newOwner)
00879 {
00880     Q_UNUSED(oldOwner);
00881 
00882     if (isServiceRegisteredByThread(oldOwner))
00883         unregisterService(name);
00884     if (isServiceRegisteredByThread(newOwner))
00885         registerService(name);
00886 
00887     QMutableHashIterator<QString, SignalHook> it(signalHooks);
00888     it.toFront();
00889     while (it.hasNext())
00890         if (it.next().value().service == name)
00891             it.value().owner = newOwner;
00892 }
00893 
00894 int QDBusConnectionPrivate::findSlot(QObject* obj, const QByteArray &normalizedName,
00895                                      QList<int> &params)
00896 {
00897     int midx = obj->metaObject()->indexOfMethod(normalizedName);
00898     if (midx == -1)
00899         return -1;
00900 
00901     int inputCount = qDBusParametersForMethod(obj->metaObject()->method(midx), params);
00902     if ( inputCount == -1 || inputCount + 1 != params.count() )
00903         return -1;              // failed to parse or invalid arguments or output arguments
00904 
00905     return midx;
00906 }
00907 
00908 bool QDBusConnectionPrivate::prepareHook(QDBusConnectionPrivate::SignalHook &hook, QString &key,
00909                                          const QString &service, const QString &owner,
00910                                          const QString &path, const QString &interface, const QString &name,
00911                                          QObject *receiver, const char *signal, int minMIdx,
00912                                          bool buildSignature)
00913 {
00914     QByteArray normalizedName = signal + 1;
00915     hook.midx = findSlot(receiver, signal + 1, hook.params);
00916     if (hook.midx == -1) {
00917         normalizedName = QMetaObject::normalizedSignature(signal + 1);
00918         hook.midx = findSlot(receiver, normalizedName, hook.params);
00919     }
00920     if (hook.midx < minMIdx) {
00921         if (hook.midx == -1)
00922             ;//qWarning("No such slot '%s' while connecting D-Bus", normalizedName.constData());
00923         return false;
00924     }
00925 
00926     hook.service = service;
00927     hook.owner = owner; // we don't care if the service has an owner yet
00928     hook.path = path;
00929     hook.obj = receiver;
00930 
00931     // build the D-Bus signal name and signature
00932     // This should not happen for QDBusConnection::connect, use buildSignature here, since
00933     // QDBusConnection::connect passes false and everything else uses true
00934     QString mname = name;
00935     if (buildSignature && mname.isNull()) {
00936         normalizedName.truncate(normalizedName.indexOf('('));
00937         mname = QString::fromUtf8(normalizedName);
00938     }
00939     key = mname;
00940     key.reserve(interface.length() + 1 + mname.length());
00941     key += QLatin1Char(':');
00942     key += interface;
00943 
00944     if (buildSignature) {
00945         hook.signature.clear();
00946         for (int i = 1; i < hook.params.count(); ++i)
00947             if (hook.params.at(i) != QDBusMetaTypeId::message)
00948                 hook.signature += QLatin1String( QDBusMetaType::typeToSignature( hook.params.at(i) ) );
00949     }
00950 
00951     return true;                // connect to this signal
00952 }
00953 
00954 bool QDBusConnectionPrivate::activateInternalFilters(const ObjectTreeNode *node, const QDBusMessage &msg)
00955 {
00956     // object may be null
00957 
00958     if (msg.interface().isEmpty() || msg.interface() == QLatin1String(DBUS_INTERFACE_INTROSPECTABLE)) {
00959         if (msg.member() == QLatin1String("Introspect") && msg.signature().isEmpty()) {
00960             //qDebug() << "QDBusConnectionPrivate::activateInternalFilters introspect" << msg.d_ptr->msg;
00961             QDBusMessage reply = msg.createReply(qDBusIntrospectObject(node));
00962             if (QDBusMessagePrivate::isLocal(msg)) {
00963                 QDBusMessagePrivate::setType(&msg, reply.type());
00964                 QDBusMessagePrivate::setArguments(&msg, QVariantList() << qDBusIntrospectObject(node));
00965             } else {
00966                 send(reply);
00967             }
00968         }
00969         if (msg.interface() == QLatin1String(DBUS_INTERFACE_INTROSPECTABLE))
00970             return true;
00971     }
00972 
00973     if (node->obj && (msg.interface().isEmpty() ||
00974                       msg.interface() == QLatin1String(DBUS_INTERFACE_PROPERTIES))) {
00975         //qDebug() << "QDBusConnectionPrivate::activateInternalFilters properties" << msg.d_ptr->msg;
00976         if (msg.member() == QLatin1String("Get") && msg.signature() == QLatin1String("ss")) {
00977             QDBusMessage reply = qDBusPropertyGet(node, msg);
00978             if (QDBusMessagePrivate::isLocal(msg)) {
00979                 QDBusMessagePrivate::setType(&msg, reply.type());
00980                 QDBusMessagePrivate::setArguments(&msg, reply.arguments());
00981             } else {
00982                 send(reply);
00983             }
00984         } else if (msg.member() == QLatin1String("Set") && msg.signature() == QLatin1String("ssv")) {
00985             QDBusMessage reply = qDBusPropertySet(node, msg);
00986             if (QDBusMessagePrivate::isLocal(msg)) {
00987                 QDBusMessagePrivate::setType(&msg, reply.type());
00988                 QDBusMessagePrivate::setArguments(&msg, reply.arguments());
00989             } else {
00990                 send(reply);
00991             }
00992         }
00993 
00994         if (msg.interface() == QLatin1String(DBUS_INTERFACE_PROPERTIES))
00995             return true;
00996     }
00997 
00998     return false;
00999 }
01000 
01001 bool QDBusConnectionPrivate::activateObject(const ObjectTreeNode *node, const QDBusMessage &msg)
01002 {
01003     // This is called by QDBusConnectionPrivate::handleObjectCall to place a call to a slot
01004     // on the object.
01005     //
01006     // The call is routed through the adaptor sub-objects if we have any
01007 
01008     // object may be null
01009 
01010     QDBusAdaptorConnector *connector;
01011     if (node->flags & QDBusConnection::ExportAdaptors &&
01012         (connector = qDBusFindAdaptorConnector(node->obj))) {
01013         int newflags = node->flags | QDBusConnection::ExportNonScriptableSlots;
01014 
01015         if (msg.interface().isEmpty()) {
01016             // place the call in all interfaces
01017             // let the first one that handles it to work
01018             QDBusAdaptorConnector::AdaptorMap::ConstIterator it =
01019                 connector->adaptors.constBegin();
01020             QDBusAdaptorConnector::AdaptorMap::ConstIterator end =
01021                 connector->adaptors.constEnd();
01022 
01023             for ( ; it != end; ++it)
01024                 if (activateCall(it->adaptor, newflags, msg))
01025                     return true;
01026         } else {
01027             // check if we have an interface matching the name that was asked:
01028             QDBusAdaptorConnector::AdaptorMap::ConstIterator it;
01029             it = qLowerBound(connector->adaptors.constBegin(), connector->adaptors.constEnd(),
01030                              msg.interface());
01031             if (it != connector->adaptors.constEnd() && msg.interface() == QLatin1String(it->interface))
01032                 if (activateCall(it->adaptor, newflags, msg))
01033                 return true;
01034         }
01035     }
01036 
01037     // no adaptors matched or were exported
01038     // try our standard filters
01039     if (activateInternalFilters(node, msg))
01040         return true;
01041 
01042     // try the object itself:
01043     if (node->flags & (QDBusConnection::ExportScriptableSlots|QDBusConnection::ExportNonScriptableSlots) &&
01044         activateCall(node->obj, node->flags, msg)) {
01045         return true;
01046     }
01047 #if 0
01048     // nothing matched
01049     qDebug("Call failed: no match for %s%s%s at %s",
01050            qPrintable(msg.interface()), msg.interface().isEmpty() ? "" : ".",
01051            qPrintable(msg.signature()),
01052            qPrintable(msg.path()));
01053 #endif
01054     return false;
01055 }
01056 
01057 template<typename Func>
01058 static bool applyForObject(QDBusConnectionPrivate::ObjectTreeNode *root, const QString &fullpath,
01059                            Func& functor)
01060 {
01061     // walk the object tree
01062     QStringList path = fullpath.split(QLatin1Char('/'));
01063     if (path.last().isEmpty())
01064         path.removeLast();      // happens if path is "/"
01065     int i = 1;
01066     QDBusConnectionPrivate::ObjectTreeNode *node = root;
01067 
01068     // try our own tree first
01069     while (node && !(node->flags & QDBusConnection::ExportChildObjects) ) {
01070         if (i == path.count()) {
01071             // found our object
01072             functor(node);
01073             return true;
01074         }
01075 
01076         QVector<QDBusConnectionPrivate::ObjectTreeNode::Data>::ConstIterator it =
01077             qLowerBound(node->children.constBegin(), node->children.constEnd(), path.at(i));
01078         if (it != node->children.constEnd() && it->name == path.at(i))
01079             // match
01080             node = it->node;
01081         else
01082             node = 0;
01083 
01084         ++i;
01085     }
01086 
01087     // any object in the tree can tell us to switch to its own object tree:
01088     if (node && node->flags & QDBusConnection::ExportChildObjects) {
01089         QObject *obj = node->obj;
01090 
01091         while (obj) {
01092             if (i == path.count()) {
01093                 // we're at the correct level
01094                 QDBusConnectionPrivate::ObjectTreeNode fakenode(*node);
01095                 fakenode.obj = obj;
01096                 functor(&fakenode);
01097                 return true;
01098             }
01099 
01100             const QObjectList children = obj->children();
01101 
01102             // find a child with the proper name
01103             QObject *next = 0;
01104             QObjectList::ConstIterator it = children.constBegin();
01105             QObjectList::ConstIterator end = children.constEnd();
01106             for ( ; it != end; ++it)
01107                 if ((*it)->objectName() == path.at(i)) {
01108                     next = *it;
01109                     break;
01110                 }
01111 
01112             if (!next)
01113                 break;
01114 
01115             ++i;
01116             obj = next;
01117         }
01118     }
01119 
01120     // object not found
01121     return false;
01122 }
01123 
01124 #if 0
01125 
01126 struct qdbus_callObject
01127 {
01128     QDBusConnectionPrivate *self;
01129     const QDBusMessage &msg;
01130     bool returnVal;
01131     inline qdbus_callObject(QDBusConnectionPrivate *s, const QDBusMessage &m)
01132         : self(s), msg(m), returnVal(false)
01133     { }
01134 
01135     inline void operator()(QDBusConnectionPrivate::ObjectTreeNode *node)
01136     { }
01137 };
01138 
01139 bool QDBusConnectionPrivate::doDirectObjectCall(const QDBusMessage &msg)
01140 {
01141     QReadLocker locker(&lock);
01142 
01143     qdbus_callObject call(this, msg);
01144     if (applyForObject(&rootNode, msg.path(), call))
01145         return call.returnVal;
01146 
01147     // qDebug("Call failed: no object found at %s", qPrintable(msg.path()));
01148     return false;
01149 }
01150 
01151 #endif
01152 
01153 struct qdbus_activateObject
01154 {
01155     QDBusConnectionPrivate *self;
01156     const QDBusMessage &msg;
01157     bool returnVal;
01158     inline qdbus_activateObject(QDBusConnectionPrivate *s, const QDBusMessage &m)
01159         : self(s), msg(m), returnVal(false)
01160     { }
01161 
01162     inline void operator()(QDBusConnectionPrivate::ObjectTreeNode *node)
01163     { returnVal = self->activateObject(node, msg); }
01164 };
01165 
01166 bool QDBusConnectionPrivate::handleObjectCall(const QDBusMessage &msg)
01167 {
01168     QReadLocker locker(&lock);
01169 
01170     qdbus_activateObject apply(this, msg);
01171     if (applyForObject(&rootNode, msg.path(), apply))
01172         return apply.returnVal;
01173 
01174     // qDebug("Call failed: no object found at %s", qPrintable(msg.path()));
01175     return false;
01176 }
01177 
01178 bool QDBusConnectionPrivate::handleSignal(const QString &key, const QDBusMessage& msg)
01179 {
01180     bool result = false;
01181     SignalHookHash::const_iterator it = signalHooks.find(key);
01182     SignalHookHash::const_iterator end = signalHooks.constEnd();
01183     //qDebug("looking for: %s", path.toLocal8Bit().constData());
01184     //qDBusDebug() << signalHooks.keys();
01185     for ( ; it != end && it.key() == key; ++it) {
01186         const SignalHook &hook = it.value();
01187         if (!hook.owner.isEmpty() && hook.owner != msg.service())
01188             continue;
01189         if (!hook.path.isEmpty() && hook.path != msg.path())
01190             continue;
01191         if (!hook.signature.isEmpty() && hook.signature != msg.signature())
01192             continue;
01193         if (hook.signature.isEmpty() && !hook.signature.isNull() && !msg.signature().isEmpty())
01194             continue;
01195 
01196         // yes, |=
01197         result |= activateSignal(hook, msg);
01198     }
01199     return result;
01200 }
01201 
01202 bool QDBusConnectionPrivate::handleSignal(const QDBusMessage& msg)
01203 {
01204     // We call handlesignal(QString, QDBusMessage) three times:
01205     //  one with member:interface
01206     //  one with member:
01207     //  one with :interface
01208     // This allows us to match signals with wildcards on member or interface
01209     // (but not both)
01210 
01211     QString key = msg.member();
01212     key.reserve(key.length() + 1 + msg.interface().length());
01213     key += QLatin1Char(':');
01214     key += msg.interface();
01215 
01216     QReadLocker locker(&lock);
01217     bool result = handleSignal(key, msg);    // one try
01218 
01219     key.truncate(msg.member().length() + 1); // keep the ':'
01220     result |= handleSignal(key, msg);        // second try
01221 
01222     key = QLatin1Char(':');
01223     key += msg.interface();
01224     result |= handleSignal(key, msg);        // third try
01225     return result;
01226 }
01227 
01228 static dbus_int32_t server_slot = -1;
01229 
01230 void QDBusConnectionPrivate::setServer(DBusServer *s)
01231 {
01232     if (!server) {
01233         handleError();
01234         return;
01235     }
01236 
01237     server = s;
01238     mode = ServerMode;
01239 
01240     dbus_server_allocate_data_slot(&server_slot);
01241     if (server_slot < 0)
01242         return;
01243 
01244     dbus_server_set_watch_functions(server, qDBusAddWatch, qDBusRemoveWatch,
01245                                     qDBusToggleWatch, this, 0); // ### check return type?
01246     dbus_server_set_timeout_functions(server, qDBusAddTimeout, qDBusRemoveTimeout,
01247                                       qDBusToggleTimeout, this, 0);
01248     dbus_server_set_new_connection_function(server, qDBusNewConnection, this, 0);
01249 
01250     dbus_server_set_data(server, server_slot, this, 0);
01251 }
01252 
01253 void QDBusConnectionPrivate::setConnection(DBusConnection *dbc)
01254 {
01255     if (!dbc) {
01256         handleError();
01257         return;
01258     }
01259 
01260     connection = dbc;
01261     mode = ClientMode;
01262 
01263     dbus_connection_set_exit_on_disconnect(connection, false);
01264     dbus_connection_set_watch_functions(connection, qDBusAddWatch, qDBusRemoveWatch,
01265                                         qDBusToggleWatch, this, 0);
01266     dbus_connection_set_timeout_functions(connection, qDBusAddTimeout, qDBusRemoveTimeout,
01267                                           qDBusToggleTimeout, this, 0);
01268 //    dbus_bus_add_match(connection, "type='signal',interface='com.trolltech.dbus.Signal'", &error);
01269 //    dbus_bus_add_match(connection, "type='signal'", &error);
01270 
01271     dbus_bus_add_match(connection, "type='signal'", &error);
01272     if (handleError()) {
01273         closeConnection();
01274         return;
01275     }
01276 
01277     const char *service = dbus_bus_get_unique_name(connection);
01278     if (service) {
01279         QVarLengthArray<char, 56> filter;
01280         filter.append("destination='", 13);
01281         filter.append(service, qstrlen(service));
01282         filter.append("\'\0", 2);
01283 
01284         dbus_bus_add_match(connection, filter.constData(), &error);
01285         if (handleError()) {
01286             closeConnection();
01287             return;
01288         }
01289     } else {
01290         qWarning("QDBusConnectionPrivate::SetConnection: Unable to get base service");
01291     }
01292 
01293 #if USE_OUTSIDE_DISPATCH
01294     dbus_connection_add_filter_outside(connection, qDBusSignalFilter, qDBusSignalFilterOutside, this, 0);
01295 #else
01296     dbus_connection_add_filter(connection, qDBusSignalFilter, this, 0);
01297 #endif
01298 
01299     //qDebug("base service: %s", service);
01300 
01301     // schedule a dispatch:
01302     QMetaObject::invokeMethod(this, "doDispatch", Qt::QueuedConnection);
01303 }
01304 
01305 extern "C"{
01306 static void qDBusResultReceived(DBusPendingCall *pending, void *user_data)
01307 {
01308     QDBusConnectionPrivate::messageResultReceived(pending, user_data);
01309 }
01310 }
01311 
01312 void QDBusConnectionPrivate::messageResultReceived(DBusPendingCall *pending, void *user_data)
01313 {
01314     QDBusPendingCall *call = reinterpret_cast<QDBusPendingCall *>(user_data);
01315     QDBusConnectionPrivate *connection = const_cast<QDBusConnectionPrivate *>(call->connection);
01316     Q_ASSERT(call->pending == pending);
01317 
01318     if (!call->receiver.isNull() && call->methodIdx != -1) {
01319         DBusMessage *reply = dbus_pending_call_steal_reply(pending);
01320 
01321         // Deliver the return values of a remote function call.
01322         //
01323         // There is only one connection and it is specified by idx
01324         // The slot must have the same parameter types that the message does
01325         // The slot may have less parameters than the message
01326         // The slot may optionally have one final parameter that is QDBusMessage
01327         // The slot receives read-only copies of the message (i.e., pass by value or by const-ref)
01328 
01329         QDBusMessage msg = QDBusMessagePrivate::fromDBusMessage(reply);
01330         qDBusDebug() << "got message: " << msg;
01331         CallDeliveryEvent *e = prepareReply(call->receiver, call->methodIdx, call->metaTypes, msg);
01332         if (e)
01333             connection->postCallDeliveryEvent(e);
01334         else
01335             qDBusDebug() << "Deliver failed!";
01336     }
01337     dbus_pending_call_unref(pending);
01338     delete call;
01339 }
01340 
01341 int QDBusConnectionPrivate::send(const QDBusMessage& message) const
01342 {
01343     DBusMessage *msg = QDBusMessagePrivate::toDBusMessage(message);
01344     if (!msg) {
01345         if (message.type() == QDBusMessage::MethodCallMessage)
01346             qWarning("QDBusConnection: error: could not send message to service \"%s\" path \"%s\" interface \"%s\" member \"%s\"",
01347                      qPrintable(message.service()), qPrintable(message.path()),
01348                      qPrintable(message.interface()), qPrintable(message.member()));
01349         else if (message.type() == QDBusMessage::SignalMessage)
01350             qWarning("QDBusConnection: error: could not send signal path \"%s\" interface \"%s\" member \"%s\"",
01351                      qPrintable(message.path()), qPrintable(message.interface()),
01352                      qPrintable(message.member()));
01353         else
01354             qWarning("QDBusConnection: error: could not send %s message to service \"%s\"",
01355                      message.type() == QDBusMessage::ReplyMessage ? "reply" :
01356                      message.type() == QDBusMessage::ErrorMessage ? "error" :
01357                      "invalid", qPrintable(message.service()));
01358         return 0;
01359     }
01360 
01361     dbus_message_set_no_reply(msg, true); // the reply would not be delivered to anything
01362 
01363     qDBusDebug() << "sending message:" << message;
01364     bool isOk = dbus_connection_send(connection, msg, 0);
01365     int serial = 0;
01366     if (isOk)
01367         serial = dbus_message_get_serial(msg);
01368 
01369     dbus_message_unref(msg);
01370     return serial;
01371 }
01372 
01373 QDBusMessage QDBusConnectionPrivate::sendWithReply(const QDBusMessage &message,
01374                                                    int sendMode, int timeout)
01375 {
01376     // special case for synchronous local calls
01377     const bool localBlockingCall = ((sendMode == QDBus::BlockWithGui || sendMode == QDBus::Block)
01378                                     && isServiceRegisteredByThread(message.service()));
01379 
01380     if (!QCoreApplication::instance() || sendMode == QDBus::Block || localBlockingCall) {
01381         DBusMessage *msg = QDBusMessagePrivate::toDBusMessage(message);
01382         if (!msg) {
01383             qWarning("QDBusConnection: error: could not send message to service \"%s\" path \"%s\" interface \"%s\" member \"%s\"",
01384                      qPrintable(message.service()), qPrintable(message.path()),
01385                      qPrintable(message.interface()), qPrintable(message.member()));
01386             return QDBusMessage();
01387         }
01388 
01389         if (localBlockingCall) {
01390     QDBusMessage messageWithSignature = QDBusMessagePrivate::updateSignature(message, msg);
01391     QDBusMessagePrivate::setLocal(&messageWithSignature, true);
01392 
01393     bool handled = false;
01394             int type = dbus_message_get_type(msg);
01395     if (type == DBUS_MESSAGE_TYPE_SIGNAL)
01396         handled = handleSignal(messageWithSignature);
01397     else if (type == DBUS_MESSAGE_TYPE_METHOD_CALL)
01398         handled = handleObjectCall(messageWithSignature);
01399 
01400     if (!handled)
01401                 handled = activateInternalFilters(&rootNode, message);
01402             if (handled) {
01403                 return messageWithSignature;
01404             } else {
01405                 return QDBusMessage::createError(
01406                         QString::fromLocal8Bit("InternalError"),
01407                         QString::fromLocal8Bit("the sender and receiver are in the same thread"));
01408             }
01409         } else {
01410     QDBusMessage amsg;
01411     qDBusDebug() << "sending message:" << message;
01412             DBusMessage *reply = dbus_connection_send_with_reply_and_block(connection, msg, timeout, &error);
01413 
01414             handleError();
01415             dbus_message_unref(msg);
01416 
01417             if (lastError.isValid())
01418                 return QDBusMessagePrivate::fromError(lastError);
01419 
01420             amsg = QDBusMessagePrivate::fromDBusMessage(reply);
01421             dbus_message_unref(reply);
01422             qDBusDebug() << "got message:" << amsg;
01423 
01424             if (dbus_connection_get_dispatch_status(connection) == DBUS_DISPATCH_DATA_REMAINS)
01425                 QMetaObject::invokeMethod(this, "doDispatch", Qt::QueuedConnection);
01426 
01427             return amsg;
01428         }
01429     } else { // use the event loop
01430         QDBusReplyWaiter waiter;
01431         if (sendWithReplyAsync(message, &waiter, SLOT(reply(QDBusMessage)), timeout) > 0) {
01432             // enter the event loop and wait for a reply
01433             waiter.exec(QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents);
01434 
01435             lastError = waiter.replyMsg; // set or clear error
01436             return waiter.replyMsg;
01437         }
01438 
01439         return QDBusMessage();
01440     }
01441 }
01442 
01443 int QDBusConnectionPrivate::sendWithReplyAsync(const QDBusMessage &message, QObject *receiver,
01444                                                const char *method, int timeout)
01445 {
01446     if (!receiver || !method || !*method) {
01447         // would not be able to deliver a reply
01448         qWarning("QDBusConnection::sendWithReplyAsync: error: cannot deliver a reply to %s::%s (%s)",
01449                  receiver ? receiver->metaObject()->className() : "(null)",
01450                  method ? method + 1 : "(null)",
01451                  receiver ? qPrintable(receiver->objectName()) : "no name");
01452         return send(message);
01453     }
01454 
01455     int slotIdx = -1;
01456     QList<int> metaTypes;
01457     slotIdx = findSlot(receiver, method + 1, metaTypes);
01458     if (slotIdx == -1) {
01459         QByteArray normalizedName = QMetaObject::normalizedSignature(method + 1);
01460         slotIdx = findSlot(receiver, normalizedName, metaTypes);
01461     }
01462     if (slotIdx == -1) {
01463         // would not be able to deliver a reply
01464         qWarning("QDBusConnection::sendWithReplyAsync: error: cannot deliver a reply to %s::%s (%s)",
01465                  receiver->metaObject()->className(),
01466                  method + 1, qPrintable(receiver->objectName()));
01467         return send(message);
01468     }
01469 
01470     DBusMessage *msg = QDBusMessagePrivate::toDBusMessage(message);
01471     if (!msg) {
01472         qWarning("QDBusConnection: error: could not send message to service \"%s\" path \"%s\" interface \"%s\" member \"%s\"",
01473                  qPrintable(message.service()), qPrintable(message.path()),
01474                  qPrintable(message.interface()), qPrintable(message.member()));
01475         return 0;
01476     }
01477 
01478     qDBusDebug() << "sending message:" << message;
01479     DBusPendingCall *pending = 0;
01480     if (dbus_connection_send_with_reply(connection, msg, &pending, timeout)) {
01481         int serial = dbus_message_get_serial(msg);
01482         dbus_message_unref(msg);
01483 
01484         QDBusPendingCall *pcall = new QDBusPendingCall;
01485         pcall->receiver = receiver;
01486         pcall->metaTypes = metaTypes;
01487         pcall->methodIdx = slotIdx;
01488         pcall->connection = this;
01489         pcall->pending = pending;
01490         dbus_pending_call_set_notify(pending, qDBusResultReceived, pcall, 0);
01491 
01492         return serial;
01493     }
01494 
01495     dbus_message_unref(msg);
01496     return 0;
01497 }
01498 
01499 void QDBusConnectionPrivate::connectSignal(const QString &key, const SignalHook &hook)
01500 {
01501     signalHooks.insertMulti(key, hook);
01502     connect(hook.obj, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*)));
01503 }
01504 
01505 void QDBusConnectionPrivate::disconnectSignal(const QString &key, const SignalHook &hook)
01506 {
01507     hook.obj->disconnect(SIGNAL(destroyed(QObject*)), this, SLOT(objectDestroyed(QObject*)));
01508     signalHooks.remove(key);
01509 }
01510 
01511 void QDBusConnectionPrivate::registerObject(const ObjectTreeNode *node)
01512 {
01513     connect(node->obj, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*)));
01514 
01515     if (node->flags & (QDBusConnection::ExportAdaptors
01516                        | QDBusConnection::ExportScriptableSignals
01517                        | QDBusConnection::ExportNonScriptableSignals)) {
01518         QDBusAdaptorConnector *connector = qDBusCreateAdaptorConnector(node->obj);
01519 
01520         if (node->flags & (QDBusConnection::ExportScriptableSignals
01521                            | QDBusConnection::ExportNonScriptableSignals)) {
01522             connector->disconnectAllSignals(node->obj);
01523             connector->connectAllSignals(node->obj);
01524         }
01525 
01526         // disconnect and reconnect to avoid duplicates
01527         connector->disconnect(SIGNAL(relaySignal(QObject*,const QMetaObject*,int,QVariantList)),
01528                               this, SLOT(relaySignal(QObject*,const QMetaObject*,int,QVariantList)));
01529         connect(connector, SIGNAL(relaySignal(QObject*,const QMetaObject*,int,QVariantList)),
01530                 this, SLOT(relaySignal(QObject*,const QMetaObject*,int,QVariantList)));
01531     }
01532 }
01533 
01534 void QDBusConnectionPrivate::connectRelay(const QString &service, const QString &path,
01535                                           const QString &interface,
01536                                           QDBusAbstractInterface *receiver,
01537                                           const char *signal)
01538 {
01539     // this function is called by QDBusAbstractInterface when one of its signals is connected
01540     // we set up a relay from D-Bus into it
01541     SignalHook hook;
01542     QString key;
01543     QString owner = getNameOwner(service);
01544 
01545     if (!prepareHook(hook, key, service, owner, path, interface, QString(), receiver, signal,
01546                      QDBusAbstractInterface::staticMetaObject.methodCount(), true))
01547         return;                 // don't connect
01548 
01549     // add it to our list:
01550     QWriteLocker locker(&lock);
01551     SignalHookHash::ConstIterator it = signalHooks.find(key);
01552     SignalHookHash::ConstIterator end = signalHooks.constEnd();
01553     for ( ; it != end && it.key() == key; ++it) {
01554         const SignalHook &entry = it.value();
01555         if (entry.service == hook.service &&
01556             entry.owner == hook.owner &&
01557             entry.path == hook.path &&
01558             entry.signature == hook.signature &&
01559             entry.obj == hook.obj &&
01560             entry.midx == hook.midx)
01561             return;             // already there, no need to re-add
01562     }
01563 
01564     connectSignal(key, hook);
01565 }
01566 
01567 void QDBusConnectionPrivate::disconnectRelay(const QString &service, const QString &path,
01568                                              const QString &interface,
01569                                              QDBusAbstractInterface *receiver,
01570                                              const char *signal)
01571 {
01572     // this function is called by QDBusAbstractInterface when one of its signals is disconnected
01573     // we remove relay from D-Bus into it
01574     SignalHook hook;
01575     QString key;
01576 
01577     QString owner = getNameOwner(service);
01578 
01579     if (!prepareHook(hook, key, service, owner, path, interface, QString(), receiver, signal,
01580                      QDBusAbstractInterface::staticMetaObject.methodCount(), true))
01581         return;                 // don't connect
01582 
01583     // remove it from our list:
01584     QWriteLocker locker(&lock);
01585     SignalHookHash::Iterator it = signalHooks.find(key);
01586     SignalHookHash::Iterator end = signalHooks.end();
01587     for ( ; it != end && it.key() == key; ++it) {
01588         const SignalHook &entry = it.value();
01589         if (entry.service == hook.service &&
01590             entry.owner == hook.owner &&
01591             entry.path == hook.path &&
01592             entry.signature == hook.signature &&
01593             entry.obj == hook.obj &&
01594             entry.midx == hook.midx) {
01595             // found it
01596             signalHooks.erase(it);
01597             return;
01598         }
01599     }
01600 
01601     qWarning("QDBusConnectionPrivate::disconnectRelay called for a signal that was not found");
01602 }
01603 
01604 QString QDBusConnectionPrivate::getNameOwner(const QString& serviceName)
01605 {
01606     if (QDBusUtil::isValidUniqueConnectionName(serviceName))
01607         return serviceName;
01608     if (!connection || !QDBusUtil::isValidBusName(serviceName))
01609         return QString();
01610 
01611     QDBusMessage msg = QDBusMessage::createMethodCall(QLatin1String(DBUS_SERVICE_DBUS),
01612             QLatin1String(DBUS_PATH_DBUS), QLatin1String(DBUS_INTERFACE_DBUS),
01613             QLatin1String("GetNameOwner"));
01614     msg << serviceName;
01615     QDBusMessage reply = sendWithReply(msg, QDBus::Block);
01616     if (!lastError.isValid() && reply.type() == QDBusMessage::ReplyMessage)
01617         return reply.arguments().at(0).toString();
01618     return QString();
01619 }
01620 
01621 struct qdbus_Introspect
01622 {
01623     QString xml;
01624     inline void operator()(QDBusConnectionPrivate::ObjectTreeNode *node)
01625   { xml = qDBusIntrospectObject(node); }
01626 };
01627 
01628 QDBusMetaObject *
01629 QDBusConnectionPrivate::findMetaObject(const QString &service, const QString &path,
01630                                        const QString &interface)
01631 {
01632     // service must be a unique connection name
01633     if (!interface.isEmpty()) {
01634         QReadLocker locker(&lock);
01635         QDBusMetaObject *mo = cachedMetaObjects.value(interface, 0);
01636         if (mo)
01637             return mo;
01638     }
01639     if (service == QLatin1String(dbus_bus_get_unique_name(connection))) {
01640         // it's one of our own
01641         QWriteLocker locker(&lock);
01642         QDBusMetaObject *mo = 0;
01643         if (!interface.isEmpty())
01644             mo = cachedMetaObjects.value(interface, 0);
01645         if (mo)
01646             // maybe it got created when we switched from read to write lock
01647             return mo;
01648 
01649         qdbus_Introspect apply;
01650         if (!applyForObject(&rootNode, path, apply)) {
01651             lastError = QDBusError(QDBusError::InvalidArgs,
01652                                    QString(QLatin1String("No object at %1")).arg(path));
01653             return 0;           // no object at path
01654         }
01655 
01656         // release the lock and return
01657         return QDBusMetaObject::createMetaObject(interface, apply.xml, cachedMetaObjects, lastError);
01658     }
01659 
01660     // not local: introspect the target object:
01661     QDBusMessage msg = QDBusMessage::createMethodCall(service, path,
01662                                                 QLatin1String(DBUS_INTERFACE_INTROSPECTABLE),
01663                                                 QLatin1String("Introspect"));
01664 
01665     QDBusMessage reply = sendWithReply(msg, QDBus::Block);
01666 
01667     // it doesn't exist yet, we have to create it
01668     QWriteLocker locker(&lock);
01669     QDBusMetaObject *mo = 0;
01670     if (!interface.isEmpty())
01671         mo = cachedMetaObjects.value(interface, 0);
01672     if (mo)
01673         // maybe it got created when we switched from read to write lock
01674         return mo;
01675 
01676     QString xml;
01677     if (reply.type() == QDBusMessage::ReplyMessage)
01678         // fetch the XML description
01679         xml = reply.arguments().at(0).toString();
01680     else {
01681         lastError = reply;
01682         if (reply.type() != QDBusMessage::ErrorMessage || lastError.type() != QDBusError::UnknownMethod)
01683             return 0; // error
01684     }
01685 
01686     // release the lock and return
01687     return QDBusMetaObject::createMetaObject(interface, xml, cachedMetaObjects, lastError);
01688 }
01689 
01690 void QDBusConnectionPrivate::registerService(const QString &serviceName)
01691 {
01692     qDBusServicesRegisteredByThread()->append(serviceName);
01693 }
01694 
01695 void QDBusConnectionPrivate::unregisterService(const QString &serviceName)
01696 {
01697     qDBusServicesRegisteredByThread()->removeAll(serviceName);
01698 }
01699 
01700 bool QDBusConnectionPrivate::isServiceRegisteredByThread(const QString &serviceName) const
01701 {
01702     return (serviceName == baseService() || qDBusServicesRegisteredByThread()->contains(serviceName));
01703 }
01704 
01705 QString QDBusConnectionPrivate::baseService() const
01706 {
01707     return connection ?
01708            QString::fromUtf8(dbus_bus_get_unique_name(connection))
01709            : QString();
01710 }
01711 
01712 void QDBusReplyWaiter::reply(const QDBusMessage &msg)
01713 {
01714     replyMsg = msg;
01715     quit();
01716 }

Generated on Thu Mar 15 12:03:06 2007 for Qt 4.2 User's Guide by  doxygen 1.5.1