/** * This file is part of TelepathyQt * * @copyright Copyright (C) 2008-2010 Collabora Ltd. * @copyright Copyright (C) 2008-2010 Nokia Corporation * @license LGPL 2.1 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include "TelepathyQt/_gen/dbus-proxy.moc.hpp" #include "TelepathyQt/debug-internal.h" #include #include #include #include #include #include namespace Tp { // ==== DBusProxy ====================================================== // Features in TpProxy but not here: // * tracking which interfaces we have (in tpqt, subclasses do that) // * being Introspectable, a Peer and a Properties implementation // * disconnecting from signals when invalidated (probably has to be in the // generated code) // * making methods always raise an error when called after invalidated // (has to be in the generated code) struct TP_QT_NO_EXPORT DBusProxy::Private { Private(const QDBusConnection &dbusConnection, const QString &busName, const QString &objectPath); QDBusConnection dbusConnection; QString busName; QString objectPath; QString invalidationReason; QString invalidationMessage; }; DBusProxy::Private::Private(const QDBusConnection &dbusConnection, const QString &busName, const QString &objectPath) : dbusConnection(dbusConnection), busName(busName), objectPath(objectPath) { debug() << "Creating new DBusProxy"; } /** * \class DBusProxy * \ingroup clientproxies * \headerfile TelepathyQt/dbus-proxy.h * * \brief The DBusProxy class is a base class representing a remote object available over D-Bus. * * All Telepathy-Qt client convenience classes that wrap Telepathy interfaces * inherit from this class in order to provide basic D-Bus interface * information. */ /** * Construct a new DBusProxy object. * * \param dbusConnection QDBusConnection to use. * \param busName D-Bus bus name of the service that provides the remote object. * \param objectPath The object path. * \param featureCore The object core feature. */ DBusProxy::DBusProxy(const QDBusConnection &dbusConnection, const QString &busName, const QString &objectPath, const Feature &featureCore) : Object(), ReadyObject(this, featureCore), mPriv(new Private(dbusConnection, busName, objectPath)) { if (!dbusConnection.isConnected()) { invalidate(TP_QT_ERROR_DISCONNECTED, QLatin1String("DBus connection disconnected")); } } /** * Class destructor. */ DBusProxy::~DBusProxy() { delete mPriv; } /** * Return the D-Bus connection through which the remote object is * accessed. * * \return A QDBusConnection object. */ QDBusConnection DBusProxy::dbusConnection() const { return mPriv->dbusConnection; } /** * Return the D-Bus object path of the remote object within the service. * * \return The D-Bus object path. */ QString DBusProxy::objectPath() const { return mPriv->objectPath; } /** * Return the D-Bus bus name (either a unique name or a well-known * name) of the service that provides the remote object. * * \return The D-Bus bus name. */ QString DBusProxy::busName() const { return mPriv->busName; } /** * Sets the D-Bus bus name. This is used by subclasses after converting * well-known names to unique names. * * \param busName The D-Bus bus name to set. */ void DBusProxy::setBusName(const QString &busName) { mPriv->busName = busName; } /** * Return whether this proxy is still valid (has not emitted invalidated()). * * \return \c true if still valid, \c false otherwise. */ bool DBusProxy::isValid() const { return mPriv->invalidationReason.isEmpty(); } /** * Return the error name indicating the reason this proxy became invalid. * * \return A D-Bus error name, or QString() if this object is still valid. */ QString DBusProxy::invalidationReason() const { return mPriv->invalidationReason; } /** * Return a debugging message indicating the reason this proxy became invalid. * * \return A debugging message, or QString() if this object is still valid. */ QString DBusProxy::invalidationMessage() const { return mPriv->invalidationMessage; } /** * Called by subclasses when the DBusProxy should become invalid. * * This method takes care of setting the invalidationReason, * invalidationMessage, and emitting the invalidated signal. * * \param reason A D-Bus error name (a string in a subset of ASCII, * prefixed with a reversed domain name) * \param message A debugging message associated with the error */ void DBusProxy::invalidate(const QString &reason, const QString &message) { if (!isValid()) { debug().nospace() << "Already invalidated by " << mPriv->invalidationReason << ", not replacing with " << reason << " \"" << message << "\""; return; } Q_ASSERT(!reason.isEmpty()); debug().nospace() << "proxy invalidated: " << reason << ": " << message; mPriv->invalidationReason = reason; mPriv->invalidationMessage = message; Q_ASSERT(!isValid()); // Defer emitting the invalidated signal until we next // return to the mainloop. QTimer::singleShot(0, this, SLOT(emitInvalidated())); } void DBusProxy::invalidate(const QDBusError &error) { invalidate(error.name(), error.message()); } void DBusProxy::emitInvalidated() { Q_ASSERT(!isValid()); emit invalidated(this, mPriv->invalidationReason, mPriv->invalidationMessage); } /** * \fn void DBusProxy::invalidated(Tp::DBusProxy *proxy, * const QString &errorName, const QString &errorMessage) * * Emitted when this object is no longer usable. * * After this signal is emitted, any D-Bus method calls on the object * will fail, but it may be possible to retrieve information that has * already been retrieved and cached. * * \param proxy This proxy. * \param errorName The name of a D-Bus error describing the reason for the invalidation. * \param errorMessage A debugging message associated with the error. */ // ==== StatefulDBusProxy ============================================== struct TP_QT_NO_EXPORT StatefulDBusProxy::Private { Private(const QString &originalName) : originalName(originalName) {} QString originalName; }; /** * \class StatefulDBusProxy * \ingroup clientproxies * \headerfile TelepathyQt/dbus-proxy.h * * \brief The StatefulDBusProxy class is a base class representing a remote object whose API is * stateful. * * These objects do not remain useful if the service providing them exits or * crashes, so they emit invalidated() if this happens. * * Examples include the Connection and Channel classes. */ /** * Construct a new StatefulDBusProxy object. * * \param dbusConnection QDBusConnection to use. * \param busName D-Bus bus name of the service that provides the remote object. * \param objectPath The object path. * \param featureCore The object core feature. */ StatefulDBusProxy::StatefulDBusProxy(const QDBusConnection &dbusConnection, const QString &busName, const QString &objectPath, const Feature &featureCore) : DBusProxy(dbusConnection, busName, objectPath, featureCore), mPriv(new Private(busName)) { QDBusServiceWatcher *serviceWatcher = new QDBusServiceWatcher(busName, dbusConnection, QDBusServiceWatcher::WatchForUnregistration, this); connect(serviceWatcher, SIGNAL(serviceOwnerChanged(QString,QString,QString)), SLOT(onServiceOwnerChanged(QString,QString,QString))); QString error, message; QString uniqueName = uniqueNameFrom(dbusConnection, busName, error, message); if (uniqueName.isEmpty()) { invalidate(error, message); return; } setBusName(uniqueName); } /** * Class destructor. */ StatefulDBusProxy::~StatefulDBusProxy() { delete mPriv; } QString StatefulDBusProxy::uniqueNameFrom(const QDBusConnection &bus, const QString &name) { QString error, message; QString uniqueName = uniqueNameFrom(bus, name, error, message); if (uniqueName.isEmpty()) { warning() << "StatefulDBusProxy::uniqueNameFrom(): Failed to get unique name of" << name; warning() << " error:" << error << "message:" << message; } return uniqueName; } QString StatefulDBusProxy::uniqueNameFrom(const QDBusConnection &bus, const QString &name, QString &error, QString &message) { if (name.startsWith(QLatin1String(":"))) { return name; } // For a stateful interface, it makes no sense to follow name-owner // changes, so we want to bind to the unique name. QDBusReply reply = bus.interface()->serviceOwner(name); if (reply.isValid()) { return reply.value(); } else { error = reply.error().name(); message = reply.error().message(); return QString(); } } void StatefulDBusProxy::onServiceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner) { // We only want to invalidate this object if it is not already invalidated, // and its (not any other object's) name owner changed signal is emitted. if (isValid() && name == mPriv->originalName && newOwner.isEmpty()) { invalidate(TP_QT_DBUS_ERROR_NAME_HAS_NO_OWNER, QLatin1String("Name owner lost (service crashed?)")); } } // ==== StatelessDBusProxy ============================================= /** * \class StatelessDBusProxy * \ingroup clientproxies * \headerfile TelepathyQt/dbus-proxy.h * * \brief The StatelessDBusProxy class is a base class representing a remote object whose API is * basically stateless. * * These objects can remain valid even if the service providing them exits * and is restarted. * * Examples include the AccountManager, Account and ConnectionManager. */ /** * Construct a new StatelessDBusProxy object. * * \param dbusConnection QDBusConnection to use. * \param busName D-Bus bus name of the service that provides the remote object. * \param objectPath The object path. * \param featureCore The object core feature. */ StatelessDBusProxy::StatelessDBusProxy(const QDBusConnection &dbusConnection, const QString &busName, const QString &objectPath, const Feature &featureCore) : DBusProxy(dbusConnection, busName, objectPath, featureCore), mPriv(nullptr) { if (busName.startsWith(QLatin1String(":"))) { warning() << "Using StatelessDBusProxy for a unique name does not make sense"; } } /** * Class destructor. */ StatelessDBusProxy::~StatelessDBusProxy() { } } // Tp