/** * This file is part of TelepathyQt4 * * @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 #include #include "TelepathyQt4/connection-manager-internal.h" #include "TelepathyQt4/_gen/cli-connection-manager-body.hpp" #include "TelepathyQt4/_gen/cli-connection-manager.moc.hpp" #include "TelepathyQt4/_gen/connection-manager.moc.hpp" #include "TelepathyQt4/_gen/connection-manager-internal.moc.hpp" #include "TelepathyQt4/_gen/connection-manager-lowlevel.moc.hpp" #include "TelepathyQt4/debug-internal.h" #include #include #include #include #include #include #include #include #include #include #include namespace { bool checkValidProtocolName(const QString &protocolName) { if (!protocolName[0].isLetter()) { return false; } int length = protocolName.length(); if (length <= 1) { return true; } QChar ch; for (int i = 1; i < length; ++i) { ch = protocolName[i]; if (!ch.isLetterOrNumber() && ch != QLatin1Char('-')) { return false; } } return true; } } namespace Tp { ConnectionManager::Private::PendingNames::PendingNames(const QDBusConnection &bus) : PendingStringList(SharedPtr()), mBus(bus) { mMethodsQueue.enqueue(QLatin1String("ListNames")); mMethodsQueue.enqueue(QLatin1String("ListActivatableNames")); QTimer::singleShot(0, this, SLOT(continueProcessing())); } void ConnectionManager::Private::PendingNames::onCallFinished(QDBusPendingCallWatcher *watcher) { QDBusPendingReply reply = *watcher; if (!reply.isError()) { parseResult(reply.value()); continueProcessing(); } else { warning() << "Failure: error " << reply.error().name() << ": " << reply.error().message(); setFinishedWithError(reply.error()); } watcher->deleteLater(); } void ConnectionManager::Private::PendingNames::continueProcessing() { if (!mMethodsQueue.isEmpty()) { QLatin1String method = mMethodsQueue.dequeue(); invokeMethod(method); } else { debug() << "Success: list" << mResult; setResult(mResult.toList()); setFinished(); } } void ConnectionManager::Private::PendingNames::invokeMethod(const QLatin1String &method) { QDBusPendingCall call = mBus.interface()->asyncCallWithArgumentList( method, QList()); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this); connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), SLOT(onCallFinished(QDBusPendingCallWatcher*))); } void ConnectionManager::Private::PendingNames::parseResult(const QStringList &names) { foreach (const QString name, names) { if (name.startsWith(QLatin1String(TELEPATHY_INTERFACE_CONNECTION_MANAGER "."))) { mResult << name.right(name.length() - 44); } } } const Feature ConnectionManager::Private::ProtocolWrapper::FeatureCore = Feature(QLatin1String(ConnectionManager::Private::ProtocolWrapper::staticMetaObject.className()), 0, true); ConnectionManager::Private::ProtocolWrapper::ProtocolWrapper( const QDBusConnection &bus, const QString &busName, const QString &objectPath, const QString &cmName, const QString &name, const QVariantMap &props) : StatelessDBusProxy(bus, busName, objectPath, FeatureCore), OptionalInterfaceFactory(this), mReadinessHelper(readinessHelper()), mInfo(ProtocolInfo(cmName, name)), mImmutableProps(props) { fillRCCs(); ReadinessHelper::Introspectables introspectables; // As Protocol does not have predefined statuses let's simulate one (0) ReadinessHelper::Introspectable introspectableCore( QSet() << 0, // makesSenseForStatuses Features(), // dependsOnFeatures QStringList(), // dependsOnInterfaces (ReadinessHelper::IntrospectFunc) &ProtocolWrapper::introspectMain, this); introspectables[FeatureCore] = introspectableCore; mReadinessHelper->addIntrospectables(introspectables); } ConnectionManager::Private::ProtocolWrapper::~ProtocolWrapper() { } void ConnectionManager::Private::ProtocolWrapper::introspectMain( ConnectionManager::Private::ProtocolWrapper *self) { Client::DBus::PropertiesInterface *properties = self->propertiesInterface(); Q_ASSERT(properties != 0); if (self->receiveProperties(self->mImmutableProps)) { debug() << "Got everything we want from the immutable props for" << self->info().name(); if (self->hasInterface(TP_QT4_IFACE_PROTOCOL_INTERFACE_AVATARS)) { self->introspectQueue.enqueue(&ProtocolWrapper::introspectAvatars); } else { debug() << "Full functionality requires CM support for the Protocol.Avatars interface"; } if (self->hasInterface(TP_QT4_IFACE_PROTOCOL_INTERFACE_PRESENCE)) { self->introspectQueue.enqueue(&ProtocolWrapper::introspectPresence); } else { debug() << "Full functionality requires CM support for the Protocol.Presence interface"; } self->continueIntrospection(); return; } debug() << "Not enough immutable properties, calling Properties::GetAll(Protocol) for" << self->info().name(); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher( properties->GetAll(TP_QT4_IFACE_PROTOCOL), self); self->connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), SLOT(gotMainProperties(QDBusPendingCallWatcher*))); } void ConnectionManager::Private::ProtocolWrapper::introspectAvatars() { Client::DBus::PropertiesInterface *properties = propertiesInterface(); Q_ASSERT(properties != 0); debug() << "Calling Properties::GetAll(Protocol.Avatars)"; QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher( properties->GetAll(TP_QT4_IFACE_PROTOCOL_INTERFACE_AVATARS), this); connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), SLOT(gotAvatarsProperties(QDBusPendingCallWatcher*))); } void ConnectionManager::Private::ProtocolWrapper::introspectPresence() { Client::DBus::PropertiesInterface *properties = propertiesInterface(); Q_ASSERT(properties != 0); debug() << "Calling Properties::GetAll(Protocol.Presence)"; QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher( properties->GetAll(TP_QT4_IFACE_PROTOCOL_INTERFACE_PRESENCE), this); connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), SLOT(gotPresenceProperties(QDBusPendingCallWatcher*))); } void ConnectionManager::Private::ProtocolWrapper::continueIntrospection() { if (introspectQueue.isEmpty()) { mReadinessHelper->setIntrospectCompleted(FeatureCore, true); } else { (this->*(introspectQueue.dequeue()))(); } } void ConnectionManager::Private::ProtocolWrapper::gotMainProperties( QDBusPendingCallWatcher *watcher) { QDBusPendingReply reply = *watcher; QVariantMap props; if (!reply.isError()) { debug() << "Got reply to Properties.GetAll(Protocol)"; QVariantMap unqualifiedProps = reply.value(); QVariantMap qualifiedProps; foreach (QString unqualified, unqualifiedProps.keys()) { qualifiedProps.insert( QString(QLatin1String("%1.%2")). arg(TP_QT4_IFACE_PROTOCOL). arg(unqualified), unqualifiedProps.value(unqualified)); } receiveProperties(qualifiedProps); } else { warning().nospace() << "Properties.GetAll(Protocol) failed: " << reply.error().name() << ": " << reply.error().message(); warning() << " Full functionality requires CM support for the Protocol interface"; } if (hasInterface(TP_QT4_IFACE_PROTOCOL_INTERFACE_AVATARS)) { introspectQueue.enqueue(&ProtocolWrapper::introspectAvatars); } else { debug() << "Full functionality requires CM support for the Protocol.Avatars interface"; } if (hasInterface(TP_QT4_IFACE_PROTOCOL_INTERFACE_PRESENCE)) { introspectQueue.enqueue(&ProtocolWrapper::introspectPresence); } else { debug() << "Full functionality requires CM support for the Protocol.Presence interface"; } continueIntrospection(); watcher->deleteLater(); } void ConnectionManager::Private::ProtocolWrapper::gotAvatarsProperties( QDBusPendingCallWatcher *watcher) { QDBusPendingReply reply = *watcher; QVariantMap props; if (!reply.isError()) { debug() << "Got reply to Properties.GetAll(Protocol.Avatars)"; props = reply.value(); QStringList supportedMimeTypes = qdbus_cast( props[QLatin1String("SupportedAvatarMIMETypes")]); uint minHeight = qdbus_cast(props[QLatin1String("MinimumAvatarHeight")]); uint maxHeight = qdbus_cast(props[QLatin1String("MaximumAvatarHeight")]); uint recommendedHeight = qdbus_cast( props[QLatin1String("RecommendedAvatarHeight")]); uint minWidth = qdbus_cast(props[QLatin1String("MinimumAvatarWidth")]); uint maxWidth = qdbus_cast(props[QLatin1String("MaximumAvatarWidth")]); uint recommendedWidth = qdbus_cast( props[QLatin1String("RecommendedAvatarWidth")]); uint maxBytes = qdbus_cast(props[QLatin1String("MaximumAvatarBytes")]); mInfo.setAvatarRequirements(AvatarSpec(supportedMimeTypes, minHeight, maxHeight, recommendedHeight, minWidth, maxWidth, recommendedWidth, maxBytes)); } else { warning().nospace() << "Properties.GetAll(Protocol.Avatars) failed: " << reply.error().name() << ": " << reply.error().message(); warning() << " Full functionality requires CM support for the Protocol.Avatars interface"; } continueIntrospection(); watcher->deleteLater(); } void ConnectionManager::Private::ProtocolWrapper::gotPresenceProperties( QDBusPendingCallWatcher *watcher) { QDBusPendingReply reply = *watcher; QVariantMap props; if (!reply.isError()) { debug() << "Got reply to Properties.GetAll(Protocol.Presence)"; props = reply.value(); mInfo.setAllowedPresenceStatuses(PresenceSpecList(qdbus_cast( props[QLatin1String("Statuses")]))); } else { warning().nospace() << "Properties.GetAll(Protocol.Presence) failed: " << reply.error().name() << ": " << reply.error().message(); warning() << " Full functionality requires CM support for the Protocol.Presence interface"; } continueIntrospection(); watcher->deleteLater(); } void ConnectionManager::Private::ProtocolWrapper::fillRCCs() { RequestableChannelClassList classes; QVariantMap fixedProps; QStringList allowedProps; // Text chatrooms fixedProps.insert( QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType"), QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_TEXT)); fixedProps.insert( QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType"), static_cast(HandleTypeRoom)); RequestableChannelClass textChatroom = {fixedProps, allowedProps}; classes.append(textChatroom); // 1-1 text chats fixedProps[QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType")] = static_cast(HandleTypeContact); RequestableChannelClass text = {fixedProps, allowedProps}; classes.append(text); // Media calls fixedProps[QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType")] = QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA); RequestableChannelClass media = {fixedProps, allowedProps}; classes.append(media); // Initially audio-only calls allowedProps.push_back( QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialAudio")); RequestableChannelClass initialAudio = {fixedProps, allowedProps}; classes.append(initialAudio); // Initially AV calls allowedProps.push_back( QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialVideo")); RequestableChannelClass initialAV = {fixedProps, allowedProps}; classes.append(initialAV); // Initially video-only calls allowedProps.removeAll( QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialAudio")); RequestableChannelClass initialVideo = {fixedProps, allowedProps}; classes.append(initialVideo); // That also settles upgrading calls, because the media classes don't have ImmutableStreams mInfo.setRequestableChannelClasses(classes); } bool ConnectionManager::Private::ProtocolWrapper::receiveProperties(const QVariantMap &props) { bool gotEverything = props.contains(QLatin1String(TELEPATHY_INTERFACE_PROTOCOL ".Interfaces")) && props.contains(QLatin1String(TELEPATHY_INTERFACE_PROTOCOL ".Parameters")) && props.contains(QLatin1String(TELEPATHY_INTERFACE_PROTOCOL ".ConnectionInterfaces")) && props.contains(QLatin1String(TELEPATHY_INTERFACE_PROTOCOL ".RequestableChannelClasses")) && props.contains(QLatin1String(TELEPATHY_INTERFACE_PROTOCOL ".VCardField")) && props.contains(QLatin1String(TELEPATHY_INTERFACE_PROTOCOL ".EnglishName")) && props.contains(QLatin1String(TELEPATHY_INTERFACE_PROTOCOL ".Icon")); setInterfaces(qdbus_cast( props[QLatin1String(TELEPATHY_INTERFACE_PROTOCOL ".Interfaces")])); mReadinessHelper->setInterfaces(interfaces()); ParamSpecList parameters = qdbus_cast( props[QLatin1String(TELEPATHY_INTERFACE_PROTOCOL ".Parameters")]); foreach (const ParamSpec &spec, parameters) { mInfo.addParameter(spec); } mInfo.setVCardField(qdbus_cast( props[QLatin1String(TELEPATHY_INTERFACE_PROTOCOL ".VCardField")])); QString englishName = qdbus_cast( props[QLatin1String(TELEPATHY_INTERFACE_PROTOCOL ".EnglishName")]); if (englishName.isEmpty()) { QStringList words = mInfo.name().split(QLatin1Char('-')); for (int i = 0; i < words.size(); ++i) { words[i][0] = words[i].at(0).toUpper(); } englishName = words.join(QLatin1String(" ")); } mInfo.setEnglishName(englishName); QString iconName = qdbus_cast( props[QLatin1String(TELEPATHY_INTERFACE_PROTOCOL ".Icon")]); if (iconName.isEmpty()) { iconName = QString(QLatin1String("im-%1")).arg(mInfo.name()); } mInfo.setIconName(iconName); // Don't overwrite the everything-is-possible RCCs with an empty list if there is no RCCs key if (props.contains(QLatin1String(TELEPATHY_INTERFACE_PROTOCOL ".RequestableChannelClasses"))) { mInfo.setRequestableChannelClasses(qdbus_cast( props[QLatin1String(TELEPATHY_INTERFACE_PROTOCOL ".RequestableChannelClasses")])); } return gotEverything; } ConnectionManager::Private::Private(ConnectionManager *parent, const QString &name, const ConnectionFactoryConstPtr &connFactory, const ChannelFactoryConstPtr &chanFactory, const ContactFactoryConstPtr &contactFactory) : parent(parent), lowlevel(ConnectionManagerLowlevelPtr(new ConnectionManagerLowlevel(parent))), name(name), baseInterface(new Client::ConnectionManagerInterface(parent)), properties(parent->interface()), readinessHelper(parent->readinessHelper()), connFactory(connFactory), chanFactory(chanFactory), contactFactory(contactFactory) { debug() << "Creating new ConnectionManager:" << parent->busName(); if (connFactory->dbusConnection().name() != parent->dbusConnection().name()) { warning() << " The D-Bus connection in the connection factory is not the proxy connection"; } if (chanFactory->dbusConnection().name() != parent->dbusConnection().name()) { warning() << " The D-Bus connection in the channel factory is not the proxy connection"; } ReadinessHelper::Introspectables introspectables; // As ConnectionManager does not have predefined statuses let's simulate one (0) ReadinessHelper::Introspectable introspectableCore( QSet() << 0, // makesSenseForStatuses Features(), // dependsOnFeatures QStringList(), // dependsOnInterfaces (ReadinessHelper::IntrospectFunc) &Private::introspectMain, this); introspectables[FeatureCore] = introspectableCore; readinessHelper->addIntrospectables(introspectables); } ConnectionManager::Private::~Private() { delete baseInterface; } bool ConnectionManager::Private::parseConfigFile() { ManagerFile f(name); if (!f.isValid()) { return false; } foreach (const QString &protocol, f.protocols()) { ProtocolInfo info(name, protocol); foreach (const ParamSpec &spec, f.parameters(protocol)) { info.addParameter(spec); } info.setRequestableChannelClasses( f.requestableChannelClasses(protocol)); info.setVCardField(f.vcardField(protocol)); info.setEnglishName(f.englishName(protocol)); info.setIconName(f.iconName(protocol)); info.setAllowedPresenceStatuses(f.allowedPresenceStatuses(protocol)); info.setAvatarRequirements(f.avatarRequirements(protocol)); protocols.append(info); } return true; } void ConnectionManager::Private::introspectMain(ConnectionManager::Private *self) { if (self->parseConfigFile()) { self->readinessHelper->setIntrospectCompleted(FeatureCore, true); return; } warning() << "Error parsing config file for connection manager" << self->name << "- introspecting"; debug() << "Calling Properties::GetAll(ConnectionManager)"; QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher( self->properties->GetAll( QLatin1String(TELEPATHY_INTERFACE_CONNECTION_MANAGER)), self->parent); self->parent->connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), SLOT(gotMainProperties(QDBusPendingCallWatcher*))); } void ConnectionManager::Private::introspectProtocolsLegacy() { debug() << "Calling ConnectionManager::ListProtocols"; QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher( baseInterface->ListProtocols(), parent); parent->connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), SLOT(gotProtocolsLegacy(QDBusPendingCallWatcher*))); } void ConnectionManager::Private::introspectParametersLegacy() { foreach (const QString &protocolName, parametersQueue) { debug() << "Calling ConnectionManager::GetParameters(" << protocolName << ")"; QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher( baseInterface->GetParameters(protocolName), parent); parent->connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), SLOT(gotParametersLegacy(QDBusPendingCallWatcher*))); } } QString ConnectionManager::Private::makeBusName(const QString &name) { return QString(QLatin1String( TELEPATHY_CONNECTION_MANAGER_BUS_NAME_BASE)).append(name); } QString ConnectionManager::Private::makeObjectPath(const QString &name) { return QString(QLatin1String( TELEPATHY_CONNECTION_MANAGER_OBJECT_PATH_BASE)).append(name); } /** * \class ConnectionManagerLowlevel * \ingroup clientcm * \headerfile TelepathyQt4/connection-manager-lowlevel.h * * \brief The ConnectionManagerLowlevel class extends ConnectionManager with * support to low-level features. */ ConnectionManagerLowlevel::ConnectionManagerLowlevel(ConnectionManager *cm) : mPriv(new Private(cm)) { } ConnectionManagerLowlevel::~ConnectionManagerLowlevel() { delete mPriv; } bool ConnectionManagerLowlevel::isValid() const { return !mPriv->cm.isNull(); } ConnectionManagerPtr ConnectionManagerLowlevel::connectionManager() const { return ConnectionManagerPtr(mPriv->cm); } /** * \class ConnectionManager * \ingroup clientcm * \headerfile TelepathyQt4/connection-manager.h * * \brief The ConnectionManager class represents a Telepathy connection manager. * * Connection managers allow connections to be made on one or more protocols. * * Most client applications should use this functionality via the * AccountManager, to allow connections to be shared between client * applications. */ /** * Feature representing the core that needs to become ready to make the * ConnectionManager object usable. * * Note that this feature must be enabled in order to use most ConnectionManager * methods. * See specific methods documentation for more details. * * When calling isReady(), becomeReady(), this feature is implicitly added * to the requested features. */ const Feature ConnectionManager::FeatureCore = Feature(QLatin1String(ConnectionManager::staticMetaObject.className()), 0, true); /** * Create a new ConnectionManager object. * * The instance will use a connection factory creating Tp::Connection objects with no features * ready, and a channel factory creating stock Telepathy-Qt4 channel subclasses, as appropriate, * with no features ready. * * \param bus QDBusConnection to use. * \param name Name of the connection manager. * \return A ConnectionManagerPtr object pointing to the newly created * ConnectionManager object. */ ConnectionManagerPtr ConnectionManager::create(const QDBusConnection &bus, const QString &name) { return ConnectionManagerPtr(new ConnectionManager(QDBusConnection::sessionBus(), name, ConnectionFactory::create(bus), ChannelFactory::create(bus), ContactFactory::create())); } /** * Create a new ConnectionManager using QDBusConnection::sessionBus() and the given factories. * * The channel factory is passed to any Connection objects created by this manager * object. In fact, they're not used directly by ConnectionManager at all. * * A warning is printed if the factories are for a bus different from QDBusConnection::sessionBus(). * * \param name Name of the connection manager. * \param connectionFactory The connection factory to use. * \param channelFactory The channel factory to use. * \param contactFactory The contact factory to use. * \return A ConnectionManagerPtr object pointing to the newly created * ConnectionManager object. */ ConnectionManagerPtr ConnectionManager::create(const QString &name, const ConnectionFactoryConstPtr &connectionFactory, const ChannelFactoryConstPtr &channelFactory, const ContactFactoryConstPtr &contactFactory) { return ConnectionManagerPtr(new ConnectionManager(QDBusConnection::sessionBus(), name, connectionFactory, channelFactory, contactFactory)); } /** * Create a new ConnectionManager using the given \a bus and the given factories. * * The channel factory is passed to any Connection objects created by this manager * object. In fact, they're not used directly by ConnectionManager at all. * * A warning is printed if the factories are for a bus different from QDBusConnection::sessionBus(). * * \param bus QDBusConnection to use. * \param name Name of the connection manager. * \param connectionFactory The connection factory to use. * \param channelFactory The channel factory to use. * \param contactFactory The contact factory to use. * \return A ConnectionManagerPtr object pointing to the newly created * ConnectionManager object. */ ConnectionManagerPtr ConnectionManager::create(const QDBusConnection &bus, const QString &name, const ConnectionFactoryConstPtr &connectionFactory, const ChannelFactoryConstPtr &channelFactory, const ContactFactoryConstPtr &contactFactory) { return ConnectionManagerPtr(new ConnectionManager(bus, name, connectionFactory, channelFactory, contactFactory)); } /** * Construct a new ConnectionManager object using the given \a bus. * * \param bus QDBusConnection to use. * \param name Name of the connection manager. */ ConnectionManager::ConnectionManager(const QDBusConnection &bus, const QString &name, const ConnectionFactoryConstPtr &connectionFactory, const ChannelFactoryConstPtr &channelFactory, const ContactFactoryConstPtr &contactFactory) : StatelessDBusProxy(bus, Private::makeBusName(name), Private::makeObjectPath(name), FeatureCore), OptionalInterfaceFactory(this), mPriv(new Private(this, name, connectionFactory, channelFactory, contactFactory)) { } /** * Class destructor. */ ConnectionManager::~ConnectionManager() { delete mPriv; } /** * Return the short name of the connection manager (e.g. "gabble"). * * \return The name of the connection manager. */ QString ConnectionManager::name() const { return mPriv->name; } /** * Return the connection factory used by this manager. * * Only read access is provided. This allows constructing object instances and examining the object * construction settings, but not changing settings. Allowing changes would lead to tricky * situations where objects constructed at different times by the manager would have unpredictably * different construction settings (eg. subclass). * * \return A read-only pointer to the ConnectionFactory object. */ ConnectionFactoryConstPtr ConnectionManager::connectionFactory() const { return mPriv->connFactory; } /** * Return the channel factory used by this manager. * * Only read access is provided. This allows constructing object instances and examining the object * construction settings, but not changing settings. Allowing changes would lead to tricky * situations where objects constructed at different times by the manager would have unpredictably * different construction settings (eg. subclass). * * \return A read-only pointer to the ChannelFactory object. */ ChannelFactoryConstPtr ConnectionManager::channelFactory() const { return mPriv->chanFactory; } /** * Return the contact factory used by this manager. * * Only read access is provided. This allows constructing object instances and examining the object * construction settings, but not changing settings. Allowing changes would lead to tricky * situations where objects constructed at different times by the manager would have unpredictably * different construction settings (eg. subclass). * * \return A read-only pointer to the ContactFactory object. */ ContactFactoryConstPtr ConnectionManager::contactFactory() const { return mPriv->contactFactory; } /** * Return a list of strings identifying the protocols supported by this * connection manager, as described in the \telepathy_spec (e.g. "jabber"). * * These identifiers are not intended to be displayed to users directly; user * interfaces are responsible for mapping them to localized strings. * * This method requires ConnectionManager::FeatureCore to be ready. * * \return A list of supported protocols. */ QStringList ConnectionManager::supportedProtocols() const { QStringList protocols; foreach (const ProtocolInfo &info, mPriv->protocols) { protocols.append(info.name()); } return protocols; } /** * Return a list of protocols info for this connection manager. * * Note that the returned ProtocolInfoList contents should not be freed. * * This method requires ConnectionManager::FeatureCore to be ready. * * \return A list of á¹”rotocolInfo. */ const ProtocolInfoList &ConnectionManager::protocols() const { return mPriv->protocols; } /** * Return whether this connection manager implements the protocol specified by * \a protocolName. * * This method requires ConnectionManager::FeatureCore to be ready. * * \return \c true if the protocol is supported, \c false otherwise. * \sa protocol(), protocols() */ bool ConnectionManager::hasProtocol(const QString &protocolName) const { foreach (const ProtocolInfo &info, mPriv->protocols) { if (info.name() == protocolName) { return true; } } return false; } /** * Return the ProtocolInfo object for the protocol specified by * \a protocolName. * * This method requires ConnectionManager::FeatureCore to be ready. * * \param protocolName The name of the protocol. * \return A ProtocolInfo object which will return \c for ProtocolInfo::isValid() if the protocol * specified by \a protocolName is not supported. * \sa hasProtocol() */ ProtocolInfo ConnectionManager::protocol(const QString &protocolName) const { foreach (const ProtocolInfo &info, mPriv->protocols) { if (info.name() == protocolName) { return info; } } return ProtocolInfo(); } /** * Request a Connection object representing a given account on a given protocol * with the given parameters. * * Return a pending operation representing the Connection object which will * succeed when the connection has been created or fail if an error occurred. * * \param protocol Name of the protocol to create the account for. * \param parameters Account parameters. * \return A PendingOperation which will emit PendingConnection::finished when * the account has been created of failed its creation process. */ PendingConnection *ConnectionManagerLowlevel::requestConnection(const QString &protocol, const QVariantMap ¶meters) { if (!isValid()) { return new PendingConnection(TP_QT4_ERROR_NOT_AVAILABLE, QLatin1String("The connection manager has been destroyed already")); } return new PendingConnection(ConnectionManagerPtr(mPriv->cm), protocol, parameters); } /** * Return a pending operation from which a list of all installed connection * manager short names (such as "gabble" or "haze") can be retrieved if it * succeeds. * * \return A PendingStringList which will emit PendingStringList::finished * when this object has finished or failed getting the connection * manager names. */ PendingStringList *ConnectionManager::listNames(const QDBusConnection &bus) { return new ConnectionManager::Private::PendingNames(bus); } ConnectionManagerLowlevelPtr ConnectionManager::lowlevel() { return mPriv->lowlevel; } ConnectionManagerLowlevelConstPtr ConnectionManager::lowlevel() const { return mPriv->lowlevel; } /** * Return the Client::ConnectionManagerInterface for this ConnectionManager. * This method is protected since the convenience methods provided by this * class should generally be used instead of calling D-Bus methods * directly. * * \return A pointer to the existing Client::ConnectionManagerInterface for this * ConnectionManager object. */ Client::ConnectionManagerInterface *ConnectionManager::baseInterface() const { return mPriv->baseInterface; } /**** Private ****/ void ConnectionManager::gotMainProperties(QDBusPendingCallWatcher *watcher) { QDBusPendingReply reply = *watcher; QVariantMap props; if (!reply.isError()) { debug() << "Got reply to Properties.GetAll(ConnectionManager)"; props = reply.value(); // If Interfaces is not supported, the spec says to assume it's // empty, so keep the empty list mPriv was initialized with if (props.contains(QLatin1String("Interfaces"))) { setInterfaces(qdbus_cast(props[QLatin1String("Interfaces")])); mPriv->readinessHelper->setInterfaces(interfaces()); } } else { warning().nospace() << "Properties.GetAll(ConnectionManager) failed: " << reply.error().name() << ": " << reply.error().message(); mPriv->readinessHelper->setIntrospectCompleted(FeatureCore, false, reply.error()); watcher->deleteLater(); return; } ProtocolPropertiesMap protocolsMap = qdbus_cast(props[QLatin1String("Protocols")]); if (!protocolsMap.isEmpty()) { ProtocolPropertiesMap::const_iterator i = protocolsMap.constBegin(); ProtocolPropertiesMap::const_iterator end = protocolsMap.constEnd(); while (i != end) { QString protocolName = i.key(); if (!checkValidProtocolName(protocolName)) { warning() << "Protocol has an invalid name" << protocolName << "- ignoring"; continue; } QString escapedProtocolName = protocolName; escapedProtocolName.replace(QLatin1Char('-'), QLatin1Char('_')); QString protocolPath = QString( QLatin1String("%1/%2")).arg(objectPath()).arg(escapedProtocolName); SharedPtr wrapper = SharedPtr( new Private::ProtocolWrapper( dbusConnection(), busName(), protocolPath, mPriv->name, protocolName, i.value())); connect(wrapper->becomeReady(), SIGNAL(finished(Tp::PendingOperation*)), SLOT(onProtocolReady(Tp::PendingOperation*))); mPriv->wrappers.insert(wrapper); ++i; } } else { mPriv->introspectProtocolsLegacy(); } watcher->deleteLater(); } void ConnectionManager::gotProtocolsLegacy(QDBusPendingCallWatcher *watcher) { QDBusPendingReply reply = *watcher; QStringList protocolsNames; if (!reply.isError()) { debug() << "Got reply to ConnectionManager.ListProtocols"; protocolsNames = reply.value(); foreach (const QString &protocolName, protocolsNames) { mPriv->protocols.append(ProtocolInfo(mPriv->name, protocolName)); mPriv->parametersQueue.enqueue(protocolName); } mPriv->introspectParametersLegacy(); } else { mPriv->readinessHelper->setIntrospectCompleted(FeatureCore, false, reply.error()); warning().nospace() << "ConnectionManager.ListProtocols failed: " << reply.error().name() << ": " << reply.error().message(); // FIXME shouldn't this invalidate the CM? } watcher->deleteLater(); } void ConnectionManager::gotParametersLegacy(QDBusPendingCallWatcher *watcher) { QDBusPendingReply reply = *watcher; QString protocolName = mPriv->parametersQueue.dequeue(); bool found = false; int pos = 0; foreach (const ProtocolInfo &info, mPriv->protocols) { if (info.name() == protocolName) { found = true; break; } ++pos; } Q_ASSERT(found); if (!reply.isError()) { debug() << QString(QLatin1String("Got reply to ConnectionManager.GetParameters(%1)")).arg(protocolName); ParamSpecList parameters = reply.value(); ProtocolInfo &info = mPriv->protocols[pos]; foreach (const ParamSpec &spec, parameters) { debug() << "Parameter" << spec.name << "has flags" << spec.flags << "and signature" << spec.signature; info.addParameter(spec); } } else { // let's remove this protocol as we can't get the params mPriv->protocols.removeAt(pos); warning().nospace() << QString(QLatin1String("ConnectionManager.GetParameters(%1) failed: ")).arg(protocolName) << reply.error().name() << ": " << reply.error().message(); } if (mPriv->parametersQueue.isEmpty()) { if (!mPriv->protocols.isEmpty()) { mPriv->readinessHelper->setIntrospectCompleted(FeatureCore, true); } else { // we could not retrieve the params for any protocol, fail core. mPriv->readinessHelper->setIntrospectCompleted(FeatureCore, false, reply.error()); } } watcher->deleteLater(); } void ConnectionManager::onProtocolReady(Tp::PendingOperation *op) { PendingReady *pr = qobject_cast(op); SharedPtr wrapper = SharedPtr::qObjectCast(pr->proxy()); ProtocolInfo info = wrapper->info(); mPriv->wrappers.remove(wrapper); if (!op->isError()) { mPriv->protocols.append(info); } else { warning().nospace() << "Protocol(" << info.name() << ")::becomeReady " "failed: " << op->errorName() << ": " << op->errorMessage(); } if (mPriv->wrappers.isEmpty()) { if (!mPriv->protocols.isEmpty()) { mPriv->readinessHelper->setIntrospectCompleted(FeatureCore, true); } else { // we could not make any Protocol objects ready, fail core. mPriv->readinessHelper->setIntrospectCompleted(FeatureCore, false, op->errorName(), op->errorMessage()); } } } } // Tp