void Ut_AboutBusinessLogic::testBluetooth () { QSignalSpy spy ( m_Api, SIGNAL (requestFinished (AboutBusinessLogic::requestType, QVariant))); QMap <QString, QVariant> properties; properties["Address"] = QString ("fake-bluetooth-address"); /* * Let's initiate the Bluetooth query that will call the * QDBusAbstractInterface::callWithCallback() that is stubbed. */ m_Api->initiateBluetoothQueries (); QCOMPARE (lastCalledMethod, QString ("DefaultAdapter")); /* * Let's answer the previous step by calling the DBus callback manually. * This will initiate an other DBus call also stubbed. */ m_Api->defaultBluetoothAdapterReceived ( QDBusObjectPath("/fakeObjectPath")); QCOMPARE (lastCalledMethod, QString ("GetProperties")); /* * Answering the second DBus call and checking if the businesslogic * processed the data as it should. */ m_Api->defaultBluetoothAdapterAddressReceived (properties); QTest::qWait (100); QCOMPARE (spy.count (), 1); QList<QVariant> args = spy.first (); QCOMPARE (args.at (0).value<AboutBusinessLogic::requestType>(), AboutBusinessLogic::reqBtAddr); #if 0 /* for some strange reason it is not working :-S */ QCOMPARE (args.at (1).toString (), QString ("fake-bluetooth-address")); #endif /* * Let's test the failure socket. This does not do nothing... */ m_Api->DBusMessagingFailure (QDBusError()); delete m_Api; /* * Let's see what happens if we initate the data collection and instead of * producing answers to queries we just destroy the object. */ m_Api = new AboutBusinessLogic; m_Api->initiateBluetoothQueries (); //XXX: delete m_Api; (done in cleanup ()) }
/*! \fn QDBusError QDBusPendingReply::error() const Retrieves the error content of the reply message, if it has finished processing. If the reply message has not finished processing or if it contains a normal reply message (non-error), this function returns an invalid QDBusError. */ QDBusError QDBusPendingCall::error() const { if (d) { QMutexLocker locker(&d->mutex); return QDBusError(d->replyMessage); } // not connected, return an error QDBusError err = QDBusError(QDBusError::Disconnected, QDBusUtil::disconnectedErrorMessage()); return err; }
/*! \fn QDBusError QDBusPendingReply::error() const Retrieves the error content of the reply message, if it has finished processing. If the reply message has not finished processing or if it contains a normal reply message (non-error), this function returns an invalid QDBusError. */ QDBusError QDBusPendingCall::error() const { if (d) { QMutexLocker locker(&d->mutex); return QDBusError(d->replyMessage); } // not connected, return an error QDBusError err = QDBusError(QDBusError::Disconnected, QLatin1String("Not connected to D-Bus server")); return err; }
/*! Places a call to the remote method specified by \a method on this interface, using \a args as arguments. This function returns immediately after queueing the call. The reply from the remote function is delivered to the \a returnMethod on object \a receiver. If an error occurs, the \a errorMethod on object \a receiver is called instead. This function returns \c true if the queueing succeeds. It does not indicate that the executed call succeeded. If it fails, the \a errorMethod is called. If the queueing failed, this function returns \c false and no slot will be called. The \a returnMethod must have as its parameters the types returned by the function call. Optionally, it may have a QDBusMessage parameter as its last or only parameter. The \a errorMethod must have a QDBusError as its only parameter. \since 4.3 \sa QDBusError, QDBusMessage */ bool QDBusAbstractInterface::callWithCallback(const QString &method, const QList<QVariant> &args, QObject *receiver, const char *returnMethod, const char *errorMethod) { Q_D(QDBusAbstractInterface); if (!d->isValid || !d->canMakeCalls()) return false; QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(), interface(), method); QDBusMessagePrivate::setParametersValidated(msg, true); msg.setArguments(args); d->lastError = QDBusError(); return d->connection.callWithCallback(msg, receiver, returnMethod, errorMethod, d->timeout); }
int QDBusInterfacePrivate::metacall(QMetaObject::Call c, int id, void **argv) { Q_Q(QDBusInterface); if (c == QMetaObject::InvokeMetaMethod) { int offset = metaObject->methodOffset(); QMetaMethod mm = metaObject->method(id + offset); if (mm.methodType() == QMetaMethod::Signal) { // signal relay from D-Bus world to Qt world QMetaObject::activate(q, metaObject, id, argv); } else if (mm.methodType() == QMetaMethod::Slot || mm.methodType() == QMetaMethod::Method) { // method call relay from Qt world to D-Bus world // get D-Bus equivalent signature QString methodName = QString::fromLatin1(mm.name()); const int *inputTypes = metaObject->inputTypesForMethod(id); int inputTypesCount = *inputTypes; // we will assume that the input arguments were passed correctly QVariantList args; args.reserve(inputTypesCount); int i = 1; for ( ; i <= inputTypesCount; ++i) args << QVariant(inputTypes[i], argv[i]); // make the call QDBusMessage reply = q->callWithArgumentList(QDBus::Block, methodName, args); if (reply.type() == QDBusMessage::ReplyMessage) { // attempt to demarshall the return values args = reply.arguments(); QVariantList::ConstIterator it = args.constBegin(); const int *outputTypes = metaObject->outputTypesForMethod(id); int outputTypesCount = *outputTypes++; if (mm.returnType() != QMetaType::UnknownType && mm.returnType() != QMetaType::Void) { // this method has a return type if (argv[0] && it != args.constEnd()) copyArgument(argv[0], *outputTypes++, *it); // skip this argument even if we didn't copy it --outputTypesCount; ++it; } for (int j = 0; j < outputTypesCount && it != args.constEnd(); ++i, ++j, ++it) { copyArgument(argv[i], outputTypes[j], *it); } } // done lastError = QDBusError(reply); return -1; } } return id; }
QVariant QDBusAbstractInterfacePrivate::property(const QMetaProperty &mp) const { if (!connection.isConnected()) // not connected return QVariant(); // try to read this property QDBusMessage msg = QDBusMessage::createMethodCall(service, path, QLatin1String(DBUS_INTERFACE_PROPERTIES), QLatin1String("Get")); msg << interface << QString::fromUtf8(mp.name()); QDBusMessage reply = connection.call(msg, QDBus::Block); if (reply.type() == QDBusMessage::ReplyMessage && reply.arguments().count() == 1 && reply.signature() == QLatin1String("v")) { QVariant value = qvariant_cast<QDBusVariant>(reply.arguments().at(0)).variant(); // make sure the type is right if (qstrcmp(mp.typeName(), value.typeName()) == 0) { if (mp.type() == QVariant::LastType) // QVariant is special in this context return qvariant_cast<QDBusVariant>(reply.arguments().at(0)).variant(); return value; } } // there was an error... if (reply.type() == QDBusMessage::ErrorMessage) lastError = reply; else if (reply.signature() != QLatin1String("v")) { QString errmsg = QLatin1String("Invalid signature `%1' in return from call to " DBUS_INTERFACE_PROPERTIES); lastError = QDBusError(QDBusError::InvalidSignature, errmsg.arg(reply.signature())); } else { QString errmsg = QLatin1String("Unexpected type `%1' when retrieving property " "`%2 %3.%4'"); lastError = QDBusError(QDBusError::InvalidSignature, errmsg.arg(QLatin1String(reply.arguments().at(0).typeName()), QLatin1String(mp.typeName()), interface, QString::fromUtf8(mp.name()))); } return QVariant(); }
/*! \fn QDBusError QDBusPendingReply::error() const Retrieves the error content of the reply message, if it has finished processing. If the reply message has not finished processing or if it contains a normal reply message (non-error), this function returns an invalid QDBusError. */ QDBusError QDBusPendingCall::error() const { if (d) return d->replyMessage; // not connected, return an error QDBusError err = QDBusError(QDBusError::Disconnected, QLatin1String("Not connected to D-Bus server")); return err; }
/*! Sends the \a message over this connection, without waiting for a reply. This is suitable for errors, signals, and return values as well as calls whose return values are not necessary. Returns true if the message was queued successfully, false otherwise. */ bool QDBusConnection::send(const QDBusMessage &message) const { if (!d || !d->connection) { QDBusError err = QDBusError(QDBusError::Disconnected, QLatin1String("Not connected to D-BUS server")); if (d) d->lastError = err; return false; } return d->send(message) != 0; }
/*! Places a call to the remote method specified by \a method on this interface, using \a args as arguments. This function returns the message that was received as a reply, which can be a normal QDBusMessage::ReplyMessage (indicating success) or QDBusMessage::ErrorMessage (if the call failed). The \a mode parameter specifies how this call should be placed. If the call succeeds, lastError() will be cleared; otherwise, it will contain the error this call produced. Normally, you should place calls using call(). \warning If you use \c UseEventLoop, your code must be prepared to deal with any reentrancy: other method calls and signals may be delivered before this function returns, as well as other Qt queued signals and events. \threadsafe */ QDBusMessage QDBusAbstractInterface::callWithArgumentList(QDBus::CallMode mode, const QString& method, const QList<QVariant>& args) { Q_D(QDBusAbstractInterface); if (!d->isValid || !d->canMakeCalls()) return QDBusMessage::createError(d->lastError); QString m = method; // split out the signature from the method int pos = method.indexOf(QLatin1Char('.')); if (pos != -1) m.truncate(pos); if (mode == QDBus::AutoDetect) { // determine if this a sync or async call mode = QDBus::Block; const QMetaObject *mo = metaObject(); QByteArray match = m.toLatin1(); for (int i = staticMetaObject.methodCount(); i < mo->methodCount(); ++i) { QMetaMethod mm = mo->method(i); if (mm.name() == match) { // found a method with the same name as what we're looking for // hopefully, nobody is overloading asynchronous and synchronous methods with // the same name QList<QByteArray> tags = QByteArray(mm.tag()).split(' '); if (tags.contains("Q_NOREPLY")) mode = QDBus::NoBlock; break; } } } // qDebug() << "QDBusAbstractInterface" << "Service" << service() << "Path:" << path(); QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(), interface(), m); QDBusMessagePrivate::setParametersValidated(msg, true); msg.setArguments(args); QDBusMessage reply = d->connection.call(msg, mode, d->timeout); if (thread() == QThread::currentThread()) d->lastError = QDBusError(reply); // will clear if reply isn't an error // ensure that there is at least one element if (reply.arguments().isEmpty()) reply << QVariant(); return reply; }
/*! Sends the \a message over this connection and returns immediately. When the reply is received, the method \a returnMethod is called in the \a receiver object. If an error occurs, the method \a errorMethod will be called instead. If no reply is received within \a timeout milliseconds, an automatic error will be delivered indicating the expiration of the call. The default \a timeout is -1, which will be replaced with an implementation-defined value that is suitable for inter-process communications (generally, 25 seconds). This function is suitable for method calls only. It is guaranteed that the slot will be called exactly once with the reply, as long as the parameter types match and no error occurs. Returns true if the message was sent, or false if the message could not be sent. */ bool QDBusConnection::callWithCallback(const QDBusMessage &message, QObject *receiver, const char *returnMethod, const char *errorMethod, int timeout) const { if (!d || !d->connection) { QDBusError err = QDBusError(QDBusError::Disconnected, QLatin1String("Not connected to D-BUS server")); if (d) d->lastError = err; return false; } return d->sendWithReplyAsync(message, receiver, returnMethod, errorMethod, timeout) != 0; }
QT_BEGIN_NAMESPACE QDBusInterfacePrivate::QDBusInterfacePrivate(const QString &serv, const QString &p, const QString &iface, const QDBusConnection &con) : QDBusAbstractInterfacePrivate(serv, p, iface, con, true), metaObject(0) { // QDBusAbstractInterfacePrivate's constructor checked the parameters for us if (connection.isConnected()) { metaObject = connectionPrivate()->findMetaObject(service, path, interface, lastError); if (!metaObject) { // creation failed, somehow isValid = false; if (!lastError.isValid()) lastError = QDBusError(QDBusError::InternalError, QLatin1String("Unknown error")); } } }
QDBusInterfacePrivate::QDBusInterfacePrivate(const QString &serv, const QString &p, const QString &iface, const QDBusConnection &con) : QDBusAbstractInterfacePrivate(serv, p, iface, con, true), metaObject(0) { // QDBusAbstractInterfacePrivate's constructor checked the parameters for us if (connection.isConnected()) { metaObject = connectionPrivate()->findMetaObject(service, path, interface, lastError); if (!metaObject) { // creation failed, somehow // most common causes are that the service doesn't exist or doesn't support introspection // those are not fatal errors, so we continue working if (!lastError.isValid()) lastError = QDBusError(QDBusError::InternalError, QLatin1String("Unknown error")); } } }
bool QDBusAbstractInterfacePrivate::setProperty(const QMetaProperty &mp, const QVariant &value) { if (!isValid || !canMakeCalls()) // can't make calls return false; // send the value QDBusMessage msg = QDBusMessage::createMethodCall(service, path, QLatin1String(DBUS_INTERFACE_PROPERTIES), QLatin1String("Set")); QDBusMessagePrivate::setParametersValidated(msg, true); msg << interface << QString::fromUtf8(mp.name()) << QVariant::fromValue(QDBusVariant(value)); QDBusMessage reply = connection.call(msg, QDBus::Block, timeout); if (reply.type() != QDBusMessage::ReplyMessage) { lastError = QDBusError(reply); return false; } return true; }
/*! Sends the \a message over this connection and blocks, waiting for a reply, for at most \a timeout milliseconds. This function is suitable for method calls only. It returns the reply message as its return value, which will be either of type QDBusMessage::ReplyMessage or QDBusMessage::ErrorMessage. If no reply is received within \a timeout milliseconds, an automatic error will be delivered indicating the expiration of the call. The default \a timeout is -1, which will be replaced with an implementation-defined value that is suitable for inter-process communications (generally, 25 seconds). See the QDBusInterface::call() function for a more friendly way of placing calls. \warning If \a mode is QDBus::BlockWithGui, this function will reenter the Qt event loop in order to wait for the reply. During the wait, it may deliver signals and other method calls to your application. Therefore, it must be prepared to handle a reentrancy whenever a call is placed with call(). */ QDBusMessage QDBusConnection::call(const QDBusMessage &message, QDBus::CallMode mode, int timeout) const { if (!d || !d->connection) { QDBusError err = QDBusError(QDBusError::Disconnected, QLatin1String("Not connected to D-Bus server")); if (d) d->lastError = err; return QDBusMessage::createError(err); } if (mode != QDBus::NoBlock) return d->sendWithReply(message, mode, timeout); d->send(message); QDBusMessage retval; retval << QVariant(); // add one argument (to avoid .at(0) problems) return retval; }
/*! \internal Fills in the QDBusReply data \a error and \a data from the reply message \a reply. */ void qDBusReplyFill(const QDBusMessage &reply, QDBusError &error, QVariant &data) { error = reply; if (error.isValid()) { data = QVariant(); // clear it return; } if (reply.arguments().count() >= 1 && reply.arguments().at(0).userType() == data.userType()) { data = reply.arguments().at(0); return; } const char *expectedSignature = 0; QByteArray receivedSignature; if (reply.arguments().count() >= 1 && reply.arguments().at(0).userType() == QDBusMetaTypeId::argument) { // compare signatures instead QDBusArgument arg = qvariant_cast<QDBusArgument>(reply.arguments().at(0)); expectedSignature = QDBusMetaType::typeToSignature(data.userType()); receivedSignature = arg.currentSignature().toLatin1(); if (receivedSignature == expectedSignature) { // matched. Demarshall it QDBusMetaType::demarshall(arg, data.userType(), data.data()); return; } } // error QString errorMsg = QLatin1String("Unexpected reply signature: got \"%1\", " "expected \"%2\" (%3)"); if (receivedSignature.isEmpty()) receivedSignature = "no signature"; error = QDBusError(QDBusError::InvalidSignature, errorMsg.arg(QLatin1String(receivedSignature), QLatin1String(expectedSignature), QLatin1String(data.typeName()))); data = QVariant(); // clear it }
QDBusAbstractInterfacePrivate::QDBusAbstractInterfacePrivate(const QString &serv, const QString &p, const QString &iface, const QDBusConnection& con, bool isDynamic) : connection(con), service(serv), path(p), interface(iface), lastError(checkIfValid(serv, p, iface, isDynamic)), isValid(!lastError.isValid()) { if (!isValid) return; if (!connection.isConnected()) { lastError = QDBusError(QDBusError::Disconnected, QLatin1String("Not connected to D-Bus server")); } else if (!service.isEmpty()) { currentOwner = connectionPrivate()->getNameOwner(service); // verify the name owner if (currentOwner.isEmpty()) { lastError = connectionPrivate()->lastError; } } }
QT_BEGIN_NAMESPACE static QDBusError checkIfValid(const QString &service, const QString &path, const QString &interface, bool isDynamic) { // We should be throwing exceptions here... oh well QDBusError error; // dynamic interfaces (QDBusInterface) can have empty interfaces, but not service and object paths // non-dynamic is the opposite: service and object paths can be empty, but not the interface if (!isDynamic) { // use assertion here because this should never happen, at all Q_ASSERT_X(!interface.isEmpty(), "QDBusAbstractInterface", "Interface name cannot be empty"); } if (!QDBusUtil::checkBusName(service, isDynamic ? QDBusUtil::EmptyNotAllowed : QDBusUtil::EmptyAllowed, &error)) return error; if (!QDBusUtil::checkObjectPath(path, isDynamic ? QDBusUtil::EmptyNotAllowed : QDBusUtil::EmptyAllowed, &error)) return error; if (!QDBusUtil::checkInterfaceName(interface, QDBusUtil::EmptyAllowed, &error)) return error; // no error return QDBusError(); }
QDBusMessage QDBusMessage::fromDBusMessage(DBusMessage *dmsg) { QDBusMessage message; if (!dmsg) return message; message.d->type = dbus_message_get_type(dmsg); message.d->path = QString::fromUtf8(dbus_message_get_path(dmsg)); message.d->interface = QString::fromUtf8(dbus_message_get_interface(dmsg)); message.d->member = QString::fromUtf8(dbus_message_get_member(dmsg)); message.d->sender = QString::fromUtf8(dbus_message_get_sender(dmsg)); message.d->msg = dbus_message_ref(dmsg); DBusError dbusError; dbus_error_init(&dbusError); if (dbus_set_error_from_message(&dbusError, dmsg)) { message.d->error = QDBusError(&dbusError); } QDBusMarshall::messageToList(message, dmsg); return message; }
/*! \internal Constructs a DBusMessage object from \a message. The returned value must be de-referenced with q_dbus_message_unref. The \a capabilities flags indicates which capabilities to use. The \a error object is set to indicate the error if anything went wrong with the marshalling. Usually, this error message will be placed in the reply, as if the call failed. The \a error pointer must not be null. */ DBusMessage *QDBusMessagePrivate::toDBusMessage(const QDBusMessage &message, QDBusConnection::ConnectionCapabilities capabilities, QDBusError *error) { if (!qdbus_loadLibDBus()) { *error = QDBusError(QDBusError::Failed, QLatin1String("Could not open lidbus-1 library")); return 0; } DBusMessage *msg = 0; const QDBusMessagePrivate *d_ptr = message.d_ptr; switch (d_ptr->type) { case DBUS_MESSAGE_TYPE_INVALID: //qDebug() << "QDBusMessagePrivate::toDBusMessage" << "message is invalid"; break; case DBUS_MESSAGE_TYPE_METHOD_CALL: // only service and interface can be empty -> path and name must not be empty if (!d_ptr->parametersValidated) { if (!QDBusUtil::checkBusName(d_ptr->service, QDBusUtil::EmptyAllowed, error)) return 0; if (!QDBusUtil::checkObjectPath(d_ptr->path, QDBusUtil::EmptyNotAllowed, error)) return 0; if (!QDBusUtil::checkInterfaceName(d_ptr->interface, QDBusUtil::EmptyAllowed, error)) return 0; if (!QDBusUtil::checkMemberName(d_ptr->name, QDBusUtil::EmptyNotAllowed, error, "method")) return 0; } msg = q_dbus_message_new_method_call(data(d_ptr->service.toUtf8()), d_ptr->path.toUtf8(), data(d_ptr->interface.toUtf8()), d_ptr->name.toUtf8()); q_dbus_message_set_auto_start( msg, d_ptr->autoStartService ); break; case DBUS_MESSAGE_TYPE_METHOD_RETURN: msg = q_dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN); if (!d_ptr->localMessage) { q_dbus_message_set_destination(msg, q_dbus_message_get_sender(d_ptr->reply)); q_dbus_message_set_reply_serial(msg, q_dbus_message_get_serial(d_ptr->reply)); } break; case DBUS_MESSAGE_TYPE_ERROR: // error name can't be empty if (!d_ptr->parametersValidated && !QDBusUtil::checkErrorName(d_ptr->name, QDBusUtil::EmptyNotAllowed, error)) return 0; msg = q_dbus_message_new(DBUS_MESSAGE_TYPE_ERROR); q_dbus_message_set_error_name(msg, d_ptr->name.toUtf8()); if (!d_ptr->localMessage) { q_dbus_message_set_destination(msg, q_dbus_message_get_sender(d_ptr->reply)); q_dbus_message_set_reply_serial(msg, q_dbus_message_get_serial(d_ptr->reply)); } break; case DBUS_MESSAGE_TYPE_SIGNAL: // nothing can be empty here if (!d_ptr->parametersValidated) { if (!QDBusUtil::checkObjectPath(d_ptr->path, QDBusUtil::EmptyNotAllowed, error)) return 0; if (!QDBusUtil::checkInterfaceName(d_ptr->interface, QDBusUtil::EmptyAllowed, error)) return 0; if (!QDBusUtil::checkMemberName(d_ptr->name, QDBusUtil::EmptyNotAllowed, error, "method")) return 0; } msg = q_dbus_message_new_signal(d_ptr->path.toUtf8(), d_ptr->interface.toUtf8(), d_ptr->name.toUtf8()); break; default: Q_ASSERT(false); break; } // if we got here, the parameters validated // and since the message parameters cannot be changed once the message is created // we can record this fact d_ptr->parametersValidated = true; QDBusMarshaller marshaller(capabilities); QVariantList::ConstIterator it = d_ptr->arguments.constBegin(); QVariantList::ConstIterator cend = d_ptr->arguments.constEnd(); q_dbus_message_iter_init_append(msg, &marshaller.iterator); if (!d_ptr->message.isEmpty()) // prepend the error message marshaller.append(d_ptr->message); for ( ; it != cend; ++it) marshaller.appendVariantInternal(*it); // check if everything is ok if (marshaller.ok) return msg; // not ok; q_dbus_message_unref(msg); *error = QDBusError(QDBusError::Failed, QLatin1String("Marshalling failed: ") + marshaller.errorString); return 0; }
void QDBusAbstractInterfacePrivate::property(const QMetaProperty &mp, QVariant &where) const { if (!isValid || !canMakeCalls()) { // can't make calls where.clear(); return; } // is this metatype registered? const char *expectedSignature = ""; if (mp.type() != 0xff) { expectedSignature = QDBusMetaType::typeToSignature(where.userType()); if (expectedSignature == 0) { qWarning("QDBusAbstractInterface: type %s must be registered with QtDBus before it can be " "used to read property %s.%s", mp.typeName(), qPrintable(interface), mp.name()); lastError = QDBusError(QDBusError::Failed, QString::fromLatin1("Unregistered type %1 cannot be handled") .arg(QLatin1String(mp.typeName()))); where.clear(); return; } } // try to read this property QDBusMessage msg = QDBusMessage::createMethodCall(service, path, QLatin1String(DBUS_INTERFACE_PROPERTIES), QLatin1String("Get")); QDBusMessagePrivate::setParametersValidated(msg, true); msg << interface << QString::fromUtf8(mp.name()); QDBusMessage reply = connection.call(msg, QDBus::Block); if (reply.type() != QDBusMessage::ReplyMessage) { lastError = reply; where.clear(); return; } if (reply.signature() != QLatin1String("v")) { QString errmsg = QLatin1String("Invalid signature `%1' in return from call to " DBUS_INTERFACE_PROPERTIES); lastError = QDBusError(QDBusError::InvalidSignature, errmsg.arg(reply.signature())); where.clear(); return; } QByteArray foundSignature; const char *foundType = 0; QVariant value = qvariant_cast<QDBusVariant>(reply.arguments().at(0)).variant(); if (value.userType() == where.userType() || mp.type() == 0xff || (expectedSignature[0] == 'v' && expectedSignature[1] == '\0')) { // simple match where = value; return; } if (value.userType() == qMetaTypeId<QDBusArgument>()) { QDBusArgument arg = qvariant_cast<QDBusArgument>(value); foundType = "user type"; foundSignature = arg.currentSignature().toLatin1(); if (foundSignature == expectedSignature) { // signatures match, we can demarshall QDBusMetaType::demarshall(arg, where.userType(), where.data()); return; } } else { foundType = value.typeName(); foundSignature = QDBusMetaType::typeToSignature(value.userType()); } // there was an error... QString errmsg = QLatin1String("Unexpected `%1' (%2) when retrieving property `%3.%4' " "(expected type `%5' (%6))"); lastError = QDBusError(QDBusError::InvalidSignature, errmsg.arg(QString::fromLatin1(foundType), QString::fromLatin1(foundSignature), interface, QString::fromUtf8(mp.name()), QString::fromLatin1(mp.typeName()), QString::fromLatin1(expectedSignature))); where.clear(); return; }
QT_BEGIN_NAMESPACE QDBusAbstractInterfacePrivate::QDBusAbstractInterfacePrivate(const QString &serv, const QString &p, const QString &iface, const QDBusConnection& con, bool isDynamic) : connection(con), service(serv), path(p), interface(iface), isValid(true) { if (isDynamic) { // QDBusInterface: service and object path can't be empty, but interface can #if 0 Q_ASSERT_X(QDBusUtil::isValidBusName(service), "QDBusInterface::QDBusInterface", "Invalid service name"); Q_ASSERT_X(QDBusUtil::isValidObjectPath(path), "QDBusInterface::QDBusInterface", "Invalid object path given"); Q_ASSERT_X(interface.isEmpty() || QDBusUtil::isValidInterfaceName(interface), "QDBusInterface::QDBusInterface", "Invalid interface name"); #else if (!QDBusUtil::isValidBusName(service)) { lastError = QDBusError(QDBusError::Disconnected, QLatin1String("Invalid service name")); isValid = false; } else if (!QDBusUtil::isValidObjectPath(path)) { lastError = QDBusError(QDBusError::Disconnected, QLatin1String("Invalid object name given")); isValid = false; } else if (!interface.isEmpty() && !QDBusUtil::isValidInterfaceName(interface)) { lastError = QDBusError(QDBusError::Disconnected, QLatin1String("Invalid interface name")); isValid = false; } #endif } else { // all others: service and path can be empty here, but interface can't #if 0 Q_ASSERT_X(service.isEmpty() || QDBusUtil::isValidBusName(service), "QDBusAbstractInterface::QDBusAbstractInterface", "Invalid service name"); Q_ASSERT_X(path.isEmpty() || QDBusUtil::isValidObjectPath(path), "QDBusAbstractInterface::QDBusAbstractInterface", "Invalid object path given"); Q_ASSERT_X(QDBusUtil::isValidInterfaceName(interface), "QDBusAbstractInterface::QDBusAbstractInterface", "Invalid interface class!"); #else if (!service.isEmpty() && !QDBusUtil::isValidBusName(service)) { lastError = QDBusError(QDBusError::Disconnected, QLatin1String("Invalid service name")); isValid = false; } else if (!path.isEmpty() && !QDBusUtil::isValidObjectPath(path)) { lastError = QDBusError(QDBusError::Disconnected, QLatin1String("Invalid object path given")); isValid = false; } else if (!QDBusUtil::isValidInterfaceName(interface)) { lastError = QDBusError(QDBusError::Disconnected, QLatin1String("Invalid interface class")); isValid = false; } #endif } if (!isValid) return; if (!connection.isConnected()) { lastError = QDBusError(QDBusError::Disconnected, QLatin1String("Not connected to D-Bus server")); isValid = false; } else if (!service.isEmpty()) { currentOwner = connectionPrivate()->getNameOwner(service); // verify the name owner if (currentOwner.isEmpty()) { isValid = false; lastError = connectionPrivate()->lastError; } } }
QVariant QDBusAbstractInterfacePrivate::property(const QMetaProperty &mp) const { if (!connection.isConnected()) // not connected return QVariant(); // is this metatype registered? int mid; const char *expectedSignature; if (mp.type() == QVariant::LastType) { // We're asking to read a QVariant mid = qMetaTypeId<QDBusVariant>(); expectedSignature = "v"; } else { mid = QMetaType::type(mp.typeName()); expectedSignature = QDBusMetaType::typeToSignature(mid); if (expectedSignature == 0) { qWarning("QDBusAbstractInterface: type %s must be registered with QtDBus before it can be " "used to read property %s.%s", mp.typeName(), qPrintable(interface), mp.name()); return QVariant(); } } // try to read this property QDBusMessage msg = QDBusMessage::createMethodCall(service, path, QLatin1String(DBUS_INTERFACE_PROPERTIES), QLatin1String("Get")); msg << interface << QString::fromUtf8(mp.name()); QDBusMessage reply = connection.call(msg, QDBus::Block); if (reply.type() != QDBusMessage::ReplyMessage) { lastError = reply; return QVariant(); } if (reply.signature() != QLatin1String("v")) { QString errmsg = QLatin1String("Invalid signature `%1' in return from call to " DBUS_INTERFACE_PROPERTIES); lastError = QDBusError(QDBusError::InvalidSignature, errmsg.arg(reply.signature())); return QVariant(); } QByteArray foundSignature; const char *foundType = 0; QVariant value = qvariant_cast<QDBusVariant>(reply.arguments().at(0)).variant(); if (value.userType() == mid) return value; // simple match if (value.userType() == qMetaTypeId<QDBusArgument>()) { QDBusArgument arg = qvariant_cast<QDBusArgument>(value); foundType = "user type"; foundSignature = arg.currentSignature().toLatin1(); if (foundSignature == expectedSignature) { void *null = 0; QVariant result(mid, null); QDBusMetaType::demarshall(arg, mid, result.data()); if (mp.type() == QVariant::LastType) // special case: QVariant return qvariant_cast<QDBusVariant>(result).variant(); return result; } } else { foundType = value.typeName(); foundSignature = QDBusMetaType::typeToSignature(value.userType()); } // there was an error... QString errmsg = QLatin1String("Unexpected `%1' (%2) when retrieving property `%3.%4' " "(expected type `%5' (%6))"); lastError = QDBusError(QDBusError::InvalidSignature, errmsg.arg(QString::fromLatin1(foundType), QString::fromLatin1(foundSignature), interface, QString::fromUtf8(mp.name()), QString::fromLatin1(mp.typeName()), QString::fromLatin1(expectedSignature))); return QVariant(); }
QT_BEGIN_NAMESPACE /*! \class QDBusReply \inmodule QtDBus \since 4.2 \brief The QDBusReply class stores the reply for a method call to a remote object. A QDBusReply object is a subset of the QDBusMessage object that represents a method call's reply. It contains only the first output argument or the error code and is used by QDBusInterface-derived classes to allow returning the error code as the function's return argument. It can be used in the following manner: \snippet doc/src/snippets/code/src_qdbus_qdbusreply.cpp 0 If the remote method call cannot fail, you can skip the error checking: \snippet doc/src/snippets/code/src_qdbus_qdbusreply.cpp 1 However, if it does fail under those conditions, the value returned by QDBusReply::value() is a default-constructed value. It may be indistinguishable from a valid return value. QDBusReply objects are used for remote calls that have no output arguments or return values (i.e., they have a "void" return type). Use the isValid() function to test if the reply succeeded. \sa QDBusMessage, QDBusInterface */ /*! \fn QDBusReply::QDBusReply(const QDBusMessage &reply) Automatically construct a QDBusReply object from the reply message \a reply, extracting the first return value from it if it is a success reply. */ /*! \fn QDBusReply::QDBusReply(const QDBusPendingReply<T> &reply) Constructs a QDBusReply object from the pending reply message, \a reply. */ /*! \fn QDBusReply::QDBusReply(const QDBusPendingCall &pcall) Automatically construct a QDBusReply object from the asynchronous pending call \a pcall. If the call isn't finished yet, QDBusReply will call QDBusPendingCall::waitForFinished(), which is a blocking operation. If the return types patch, QDBusReply will extract the first return argument from the reply. */ /*! \fn QDBusReply::QDBusReply(const QDBusError &error) Constructs an error reply from the D-Bus error code given by \a error. */ /*! \fn QDBusReply::operator=(const QDBusReply &other) Makes this object be a copy of the object \a other. */ /*! \fn QDBusReply::operator=(const QDBusError &error) Sets this object to contain the error code given by \a error. You can later access it with error(). */ /*! \fn QDBusReply::operator=(const QDBusMessage &message) Makes this object contain the reply specified by message \a message. If \a message is an error message, this function will copy the error code and message into this object If \a message is a standard reply message and contains at least one parameter, it will be copied into this object, as long as it is of the correct type. If it's not of the same type as this QDBusError object, this function will instead set an error code indicating a type mismatch. */ /*! \fn QDBusReply::operator=(const QDBusPendingCall &pcall) Makes this object contain the reply specified by the pending asynchronous call \a pcall. If the call is not finished yet, this function will call QDBusPendingCall::waitForFinished() to block until the reply arrives. If \a pcall finishes with an error message, this function will copy the error code and message into this object If \a pcall finished with a standard reply message and contains at least one parameter, it will be copied into this object, as long as it is of the correct type. If it's not of the same type as this QDBusError object, this function will instead set an error code indicating a type mismatch. */ /*! \fn bool QDBusReply::isValid() const Returns true if no error occurred; otherwise, returns false. \sa error() */ /*! \fn QDBusReply::error() Returns the error code that was returned from the remote function call. If the remote call did not return an error (i.e., if it succeeded), then the QDBusError object that is returned will not be a valid error code (QDBusError::isValid() will return false). \sa isValid() */ /*! \fn QDBusReply::value() const Returns the remote function's calls return value. If the remote call returned with an error, the return value of this function is undefined and may be undistinguishable from a valid return value. This function is not available if the remote call returns \c void. */ /*! \fn QDBusReply::operator Type() const Returns the same as value(). This function is not available if the remote call returns \c void. */ /*! \internal Fills in the QDBusReply data \a error and \a data from the reply message \a reply. */ void qDBusReplyFill(const QDBusMessage &reply, QDBusError &error, QVariant &data) { error = reply; if (error.isValid()) { data = QVariant(); // clear it return; } if (reply.arguments().count() >= 1 && reply.arguments().at(0).userType() == data.userType()) { data = reply.arguments().at(0); return; } const char *expectedSignature = QDBusMetaType::typeToSignature(data.userType()); const char *receivedType = 0; QByteArray receivedSignature; if (reply.arguments().count() >= 1) { if (reply.arguments().at(0).userType() == QDBusMetaTypeId::argument) { // compare signatures instead QDBusArgument arg = qvariant_cast<QDBusArgument>(reply.arguments().at(0)); receivedSignature = arg.currentSignature().toLatin1(); if (receivedSignature == expectedSignature) { // matched. Demarshall it QDBusMetaType::demarshall(arg, data.userType(), data.data()); return; } } else { // not an argument and doesn't match? int type = reply.arguments().at(0).userType(); receivedType = QVariant::typeToName(QVariant::Type(type)); receivedSignature = QDBusMetaType::typeToSignature(type); } } // error if (receivedSignature.isEmpty()) receivedSignature = "no signature"; QString errorMsg; if (receivedType) { errorMsg = QString::fromLatin1("Unexpected reply signature: got \"%1\" (%4), " "expected \"%2\" (%3)") .arg(QLatin1String(receivedSignature), QLatin1String(expectedSignature), QLatin1String(data.typeName()), QLatin1String(receivedType)); } else { errorMsg = QString::fromLatin1("Unexpected reply signature: got \"%1\", " "expected \"%2\" (%3)") .arg(QLatin1String(receivedSignature), QLatin1String(expectedSignature), QLatin1String(data.typeName())); } error = QDBusError(QDBusError::InvalidSignature, errorMsg); data = QVariant(); // clear it }
/*! Returns the last error that happened in this connection. This function is provided for low-level code. If you're using QDBusInterface::call(), error codes are reported by its return value. \sa QDBusInterface, QDBusMessage */ QDBusError QDBusConnection::lastError() const { return d ? d->lastError : QDBusError(); }
/*! Returns the last error that happened in this server. This function is provided for low-level code. */ QDBusError QDBusServer::lastError() const { return d ? d->lastError : QDBusError(QDBusError::Disconnected, QDBusUtil::disconnectedErrorMessage()); }
QDBusMetaObject *QDBusMetaObject::createMetaObject(const QString &interface, const QString &xml, QHash<QString, QDBusMetaObject *> &cache, QDBusError &error) { error = QDBusError(); QDBusIntrospection::Interfaces parsed = QDBusIntrospection::parseInterfaces(xml); QDBusMetaObject *we = 0; QDBusIntrospection::Interfaces::ConstIterator it = parsed.constBegin(); QDBusIntrospection::Interfaces::ConstIterator end = parsed.constEnd(); for ( ; it != end; ++it) { // check if it's in the cache bool us = it.key() == interface; QDBusMetaObject *obj = cache.value(it.key(), 0); if ( !obj && ( us || !interface.startsWith( QLatin1String("local.") ) ) ) { // not in cache; create obj = new QDBusMetaObject; QDBusMetaObjectGenerator generator(it.key(), it.value().constData()); generator.write(obj); if ( (obj->cached = !it.key().startsWith( QLatin1String("local.") )) ) // cache it cache.insert(it.key(), obj); else if (!us) delete obj; } if (us) // it's us we = obj; } if (we) return we; // still nothing? if (parsed.isEmpty()) { // object didn't return introspection we = new QDBusMetaObject; QDBusMetaObjectGenerator generator(interface, 0); generator.write(we); we->cached = false; return we; } else if (interface.isEmpty()) { // merge all interfaces it = parsed.constBegin(); QDBusIntrospection::Interface merged = *it.value().constData(); for (++it; it != end; ++it) { merged.annotations.unite(it.value()->annotations); merged.methods.unite(it.value()->methods); merged.signals_.unite(it.value()->signals_); merged.properties.unite(it.value()->properties); } merged.name = QLatin1String("local.Merged"); merged.introspection.clear(); we = new QDBusMetaObject; QDBusMetaObjectGenerator generator(merged.name, &merged); generator.write(we); we->cached = false; return we; } // mark as an error error = QDBusError(QDBusError::UnknownInterface, QString( QLatin1String("Interface '%1' was not found") ) .arg(interface)); return 0; }
/*! Returns the last error that happened in this connection. This function is provided for low-level code. If you're using QDBusInterface::call(), error codes are reported by its return value. \sa QDBusInterface, QDBusMessage */ QDBusError QDBusConnection::lastError() const { return d ? d->lastError : QDBusError(QDBusError::Disconnected, QStringLiteral("Not connected.")); }