/** * 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 #include "TelepathyQt/_gen/account-manager.moc.hpp" #include "TelepathyQt/_gen/cli-account-manager.moc.hpp" #include "TelepathyQt/_gen/cli-account-manager-body.hpp" #include "TelepathyQt/debug-internal.h" #include #include #include #include #include #include #include #include #include #include #include namespace Tp { struct TP_QT_NO_EXPORT AccountManager::Private { Private(AccountManager *parent, const AccountFactoryConstPtr &accFactory, const ConnectionFactoryConstPtr &connFactory, const ChannelFactoryConstPtr &chanFactory, const ContactFactoryConstPtr &contactFactory); ~Private(); void init(); static void introspectMain(Private *self); void checkIntrospectionCompleted(); QSet getAccountPathsFromProp(const QVariant &prop); QSet getAccountPathsFromProps(const QVariantMap &props); void addAccountForPath(const QString &accountObjectPath); // Public object AccountManager *parent; // Instance of generated interface class Client::AccountManagerInterface *baseInterface; // Mandatory properties interface proxy Client::DBus::PropertiesInterface *properties; ReadinessHelper *readinessHelper; AccountFactoryConstPtr accFactory; ConnectionFactoryConstPtr connFactory; ChannelFactoryConstPtr chanFactory; ContactFactoryConstPtr contactFactory; // Introspection int reintrospectionRetries; bool gotInitialAccounts; QHash incompleteAccounts; QHash accounts; QStringList supportedAccountProperties; }; static const int maxReintrospectionRetries = 5; static const int reintrospectionRetryInterval = 3; AccountManager::Private::Private(AccountManager *parent, const AccountFactoryConstPtr &accFactory, const ConnectionFactoryConstPtr &connFactory, const ChannelFactoryConstPtr &chanFactory, const ContactFactoryConstPtr &contactFactory) : parent(parent), baseInterface(new Client::AccountManagerInterface(parent)), properties(parent->interface()), readinessHelper(parent->readinessHelper()), accFactory(accFactory), connFactory(connFactory), chanFactory(chanFactory), contactFactory(contactFactory), reintrospectionRetries(0), gotInitialAccounts(false) { debug() << "Creating new AccountManager:" << parent->busName(); if (accFactory->dbusConnection().name() != parent->dbusConnection().name()) { warning() << " The D-Bus connection in the account factory is not the proxy connection"; } 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 AccountManager 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); readinessHelper->becomeReady(Features() << FeatureCore); init(); } AccountManager::Private::~Private() { delete baseInterface; } void AccountManager::Private::init() { if (!parent->isValid()) { return; } parent->connect(baseInterface, SIGNAL(AccountValidityChanged(QDBusObjectPath,bool)), SLOT(onAccountValidityChanged(QDBusObjectPath,bool))); parent->connect(baseInterface, SIGNAL(AccountRemoved(QDBusObjectPath)), SLOT(onAccountRemoved(QDBusObjectPath))); } void AccountManager::Private::introspectMain(AccountManager::Private *self) { debug() << "Calling Properties::GetAll(AccountManager)"; QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher( self->properties->GetAll( TP_QT_IFACE_ACCOUNT_MANAGER), self->parent); self->parent->connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), SLOT(gotMainProperties(QDBusPendingCallWatcher*))); } void AccountManager::Private::checkIntrospectionCompleted() { if (!parent->isReady(FeatureCore) && incompleteAccounts.size() == 0) { readinessHelper->setIntrospectCompleted(FeatureCore, true); } } QSet AccountManager::Private::getAccountPathsFromProp( const QVariant &prop) { QSet set; ObjectPathList paths = qdbus_cast(prop); if (paths.size() == 0) { /* maybe the AccountManager is buggy, like Mission Control * 5.0.beta45, and returns an array of strings rather than * an array of object paths? */ QStringList wronglyTypedPaths = qdbus_cast(prop); if (wronglyTypedPaths.size() > 0) { warning() << "AccountManager returned wrong type for" "Valid/InvalidAccounts (expected 'ao', got 'as'); " "working around it"; foreach (QString path, wronglyTypedPaths) { set << path; } } } else { foreach (const QDBusObjectPath &path, paths) { set << path.path(); } } return set; } QSet AccountManager::Private::getAccountPathsFromProps( const QVariantMap &props) { return getAccountPathsFromProp(props[QLatin1String("ValidAccounts")]).unite( getAccountPathsFromProp(props[QLatin1String("InvalidAccounts")])); } void AccountManager::Private::addAccountForPath(const QString &path) { // Also check incompleteAccounts, because otherwise we end up introspecting an account twice // when getting an AccountValidityChanged signal for a new account before we get the initial // introspection accounts list from the GetAll return (the GetAll return function // unconditionally calls addAccountForPath if (accounts.contains(path) || incompleteAccounts.contains(path)) { return; } PendingReady *readyOp = accFactory->proxy(parent->busName(), path, connFactory, chanFactory, contactFactory); AccountPtr account(AccountPtr::qObjectCast(readyOp->proxy())); Q_ASSERT(!account.isNull()); parent->connect(readyOp, SIGNAL(finished(Tp::PendingOperation*)), SLOT(onAccountReady(Tp::PendingOperation*))); incompleteAccounts.insert(path, account); } /** * \class AccountManager * \ingroup clientam * \headerfile TelepathyQt/account-manager.h * * \brief The AccountManager class represents a Telepathy account manager. * * The remote object accessor functions on this object (allAccounts(), * validAccounts(), and so on) don't make any D-Bus calls; instead, they return/use * values cached from a previous introspection run. The introspection process * populates their values in the most efficient way possible based on what the * service implements. * * To avoid unnecessary D-Bus traffic, some accessors only return valid * information after AccountManager::FeatureCore has been enabled. * See the individual methods descriptions for more details. * * AccountManager features can be enabled by calling becomeReady() * with the desired set of features as an argument (currently only AccountManager::FeatureCore is * supported), and waiting for the resulting PendingOperation to finish. * * All accounts returned by AccountManager are guaranteed to have the features set in the * AccountFactory used by it ready. * * A signal is emitted to indicate that accounts are added. See newCreated() for more details. * * \section am_usage_sec Usage * * \subsection am_create_sec Creating an AccountManager object * * One way to create an AccountManager object is to just call the create method. * For example: * * \code AccountManagerPtr am = AccountManager::create(); \endcode * * An AccountManagerPtr object is returned, which will automatically keep * track of object lifetime. * * You can also provide a D-Bus connection as a QDBusConnection: * * \code AccountManagerPtr am = AccountManager::create(QDBusConnection::sessionBus()); \endcode * * \subsection am_ready_sec Making AccountManager ready to use * * An AccountManager object needs to become ready before usage, meaning that the * introspection process finished and the object accessors can be used. * * To make the object ready, use becomeReady() and wait for the * PendingOperation::finished() signal to be emitted. * * \code * * class MyClass : public QObject * { * QOBJECT * * public: * MyClass(QObject *parent = 0); * ~MyClass() { } * * private Q_SLOTS: * void onAccountManagerReady(Tp::PendingOperation*); * * private: * AccountManagerPtr mAM; * }; * * MyClass::MyClass(QObject *parent) * : QObject(parent) * mAM(AccountManager::create()) * { * connect(mAM->becomeReady(), * SIGNAL(finished(Tp::PendingOperation*)), * SLOT(onAccountManagerReady(Tp::PendingOperation*))); * } * * void MyClass::onAccountManagerReady(Tp::PendingOperation *op) * { * if (op->isError()) { * qWarning() << "Account manager cannot become ready:" << * op->errorName() << "-" << op->errorMessage(); * return; * } * * // AccountManager is now ready * qDebug() << "All accounts:"; * foreach (const Tp::AccountPtr &acc, mAM->allAccounts()) { * qDebug() << " path:" << acc->objectPath(); * } * } * * \endcode * * See \ref async_model, \ref shared_ptr */ /** * Feature representing the core that needs to become ready to make the * AccountManager object usable. * * Note that this feature must be enabled in order to use most AccountManager * methods. * * When calling isReady(), becomeReady(), this feature is implicitly added * to the requested features. */ const Feature AccountManager::FeatureCore = Feature(QLatin1String(AccountManager::staticMetaObject.className()), 0, true); /** * Create a new AccountManager object using the given \a bus. * * The instance will use an account factory creating Tp::Account objects with Account::FeatureCore * ready, a connection factory creating Tp::Connection objects with no features ready, a channel * factory creating stock Tp::Channel subclasses, as appropriate, with no features ready, and a * contact factory creating Tp::Contact objects with no features ready. * * \param bus QDBusConnection to use. * \return An AccountManagerPtr object pointing to the newly created * AccountManager object. */ AccountManagerPtr AccountManager::create(const QDBusConnection &bus) { return AccountManagerPtr(new AccountManager(bus, AccountFactory::create(bus, Account::FeatureCore), ConnectionFactory::create(bus), ChannelFactory::create(bus), ContactFactory::create(), AccountManager::FeatureCore)); } /** * Create a new AccountManager using QDBusConnection::sessionBus() and the given factories. * * The connection, channel and contact factories are passed to any Account objects created by this * account manager object. In fact, they're not used directly by AccountManager at all. * * A warning is printed if the factories are for a bus different from QDBusConnection::sessionBus(). * * \param accountFactory The account factory to use. * \param connectionFactory The connection factory to use. * \param channelFactory The channel factory to use. * \param contactFactory The contact factory to use. * \return An AccountManagerPtr object pointing to the newly created * AccountManager object. */ AccountManagerPtr AccountManager::create(const AccountFactoryConstPtr &accountFactory, const ConnectionFactoryConstPtr &connectionFactory, const ChannelFactoryConstPtr &channelFactory, const ContactFactoryConstPtr &contactFactory) { return AccountManagerPtr(new AccountManager(QDBusConnection::sessionBus(), accountFactory, connectionFactory, channelFactory, contactFactory, AccountManager::FeatureCore)); } /** * Create a new AccountManager using the given \a bus and the given factories. * * The connection, channel and contact factories are passed to any Account objects created by this * account manager object. In fact, they're not used directly by AccountManager at all. * * A warning is printed if the factories are not for \a bus. * * \param bus QDBusConnection to use. * \param accountFactory The account factory to use. * \param connectionFactory The connection factory to use. * \param channelFactory The channel factory to use. * \param contactFactory The contact factory to use. * \return An AccountManagerPtr object pointing to the newly created * AccountManager object. */ AccountManagerPtr AccountManager::create(const QDBusConnection &bus, const AccountFactoryConstPtr &accountFactory, const ConnectionFactoryConstPtr &connectionFactory, const ChannelFactoryConstPtr &channelFactory, const ContactFactoryConstPtr &contactFactory) { return AccountManagerPtr(new AccountManager(bus, accountFactory, connectionFactory, channelFactory, contactFactory, AccountManager::FeatureCore)); } /** * Construct a new AccountManager object using the given \a bus and the given factories. * * The connection, channel and contact factories are passed to any Account objects created by this * account manager object. In fact, they're not used directly by AccountManager at all. * * A warning is printed if the factories are not for \a bus. * * \param bus QDBusConnection to use. * \param accountFactory The account factory to use. * \param connectionFactory The connection factory to use. * \param channelFactory The channel factory to use. * \param contactFactory The contact factory to use. * \param coreFeature The core feature of the Account subclass. The corresponding introspectable * should depend on AccountManager::FeatureCore. */ AccountManager::AccountManager(const QDBusConnection &bus, const AccountFactoryConstPtr &accountFactory, const ConnectionFactoryConstPtr &connectionFactory, const ChannelFactoryConstPtr &channelFactory, const ContactFactoryConstPtr &contactFactory, const Feature &coreFeature) : StatelessDBusProxy(bus, TP_QT_ACCOUNT_MANAGER_BUS_NAME, TP_QT_ACCOUNT_MANAGER_OBJECT_PATH, coreFeature), OptionalInterfaceFactory(this), mPriv(new Private(this, accountFactory, connectionFactory, channelFactory, contactFactory)) { } /** * Class destructor. */ AccountManager::~AccountManager() { delete mPriv; } /** * Return the account factory used by this account 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 AccountFactory object. */ AccountFactoryConstPtr AccountManager::accountFactory() const { return mPriv->accFactory; } /** * Return the connection factory used by this account 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 AccountManager::connectionFactory() const { return mPriv->connFactory; } /** * Return the channel factory used by this account 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 AccountManager::channelFactory() const { return mPriv->chanFactory; } /** * Return the contact factory used by this account 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 AccountManager::contactFactory() const { return mPriv->contactFactory; } /** * Return a list containing all accounts. * * Newly accounts added and/or discovered are signaled via newAccount(). * * This method requires AccountManager::FeatureCore to be ready. * * \return A list of pointers to Account objects. */ QList AccountManager::allAccounts() const { QList ret; foreach (const AccountPtr &account, mPriv->accounts) { ret << account; } return ret; } /** * Return a set of accounts containing all valid accounts. * * This method requires AccountManager::FeatureCore to be ready. * * \return A pointer to an AccountSet object containing the matching accounts. */ AccountSetPtr AccountManager::validAccounts() const { QVariantMap filter; filter.insert(QLatin1String("valid"), true); return filterAccounts(filter); } /** * Return a set of accounts containing all invalid accounts. * * This method requires AccountManager::FeatureCore to be ready. * * \return A pointer to an AccountSet object containing the matching accounts. */ AccountSetPtr AccountManager::invalidAccounts() const { QVariantMap filter; filter.insert(QLatin1String("valid"), false); return filterAccounts(filter); } /** * Return a set of accounts containing all enabled accounts. * * This method requires AccountManager::FeatureCore to be ready. * * \return A pointer to an AccountSet object containing the matching accounts. */ AccountSetPtr AccountManager::enabledAccounts() const { QVariantMap filter; filter.insert(QLatin1String("enabled"), true); return filterAccounts(filter); } /** * Return a set of accounts containing all disabled accounts. * * This method requires AccountManager::FeatureCore to be ready. * * \return A pointer to an AccountSet object containing the matching accounts. */ AccountSetPtr AccountManager::disabledAccounts() const { QVariantMap filter; filter.insert(QLatin1String("enabled"), false); return filterAccounts(filter); } /** * Return a set of accounts containing all online accounts. * * This method requires AccountManager::FeatureCore to be ready. * * \return A pointer to an AccountSet object containing the matching accounts. */ AccountSetPtr AccountManager::onlineAccounts() const { QVariantMap filter; filter.insert(QLatin1String("online"), true); return filterAccounts(filter); } /** * Return a set of accounts containing all offline accounts. * * This method requires AccountManager::FeatureCore to be ready. * * \return A pointer to an AccountSet object containing the matching accounts. */ AccountSetPtr AccountManager::offlineAccounts() const { QVariantMap filter; filter.insert(QLatin1String("online"), false); return filterAccounts(filter); } /** * Return a set of accounts containing all accounts that support text chats by * providing a contact identifier. * * For this method to work, you must use an AccountFactory which makes Account::FeatureCapabilities * ready. * * This method requires AccountManager::FeatureCore to be ready. * * \return A pointer to an AccountSet object containing the matching accounts. */ AccountSetPtr AccountManager::textChatAccounts() const { if (!accountFactory()->features().contains(Account::FeatureCapabilities)) { warning() << "Account filtering by capabilities can only be used with an AccountFactory" << "which makes Account::FeatureCapabilities ready"; return filterAccounts(AccountFilterConstPtr()); } AccountCapabilityFilterPtr filter = AccountCapabilityFilter::create(); filter->addRequestableChannelClassSubset(RequestableChannelClassSpec::textChat()); return filterAccounts(filter); } /** * Return a set of accounts containing all accounts that support text chat * rooms. * * For this method to work, you must use an AccountFactory which makes Account::FeatureCapabilities * ready. * * This method requires AccountManager::FeatureCore to be ready. * * \return A pointer to an AccountSet object containing the matching accounts. */ AccountSetPtr AccountManager::textChatroomAccounts() const { if (!accountFactory()->features().contains(Account::FeatureCapabilities)) { warning() << "Account filtering by capabilities can only be used with an AccountFactory" << "which makes Account::FeatureCapabilities ready"; return filterAccounts(AccountFilterConstPtr()); } AccountCapabilityFilterPtr filter = AccountCapabilityFilter::create(); filter->addRequestableChannelClassSubset(RequestableChannelClassSpec::textChatroom()); return filterAccounts(filter); } /** * Return a set of accounts containing all accounts that support audio calls (using the * Call interface) by providing a contact identifier. * * For this method to work, you must use an AccountFactory which makes Account::FeatureCapabilities * ready. * * This method requires AccountManager::FeatureCore to be ready. * * \return A pointer to an AccountSet object containing the matching accounts. */ AccountSetPtr AccountManager::audioCallAccounts() const { if (!accountFactory()->features().contains(Account::FeatureCapabilities)) { warning() << "Account filtering by capabilities can only be used with an AccountFactory" << "which makes Account::FeatureCapabilities ready"; return filterAccounts(AccountFilterConstPtr()); } AccountCapabilityFilterPtr filter = AccountCapabilityFilter::create(); filter->addRequestableChannelClassSubset(RequestableChannelClassSpec::audioCall()); return filterAccounts(filter); } /** * Return a set of accounts containing all accounts that support video calls (using the * Call interface) by providing a contact identifier. * * For this method to work, you must use an AccountFactory which makes Account::FeatureCapabilities * ready. * * This method requires AccountManager::FeatureCore to be ready. * * \return A pointer to an AccountSet object containing the matching accounts. */ AccountSetPtr AccountManager::videoCallAccounts() const { if (!accountFactory()->features().contains(Account::FeatureCapabilities)) { warning() << "Account filtering by capabilities can only be used with an AccountFactory" << "which makes Account::FeatureCapabilities ready"; return filterAccounts(AccountFilterConstPtr()); } AccountCapabilityFilterPtr filter = AccountCapabilityFilter::create(); filter->addRequestableChannelClassSubset(RequestableChannelClassSpec::videoCall()); return filterAccounts(filter); } /** * Return a set of accounts containing all accounts that support media calls (using the * StreamedMedia interface) by providing a contact identifier. * * For this method to work, you must use an AccountFactory which makes Account::FeatureCapabilities * ready. * * This method requires AccountManager::FeatureCore to be ready. * * \return A pointer to an AccountSet object containing the matching accounts. */ AccountSetPtr AccountManager::streamedMediaCallAccounts() const { if (!accountFactory()->features().contains(Account::FeatureCapabilities)) { warning() << "Account filtering by capabilities can only be used with an AccountFactory" << "which makes Account::FeatureCapabilities ready"; return filterAccounts(AccountFilterConstPtr()); } AccountCapabilityFilterPtr filter = AccountCapabilityFilter::create(); filter->addRequestableChannelClassSubset(RequestableChannelClassSpec::streamedMediaCall()); return filterAccounts(filter); } /** * Return a set of accounts containing all accounts that support audio calls (using the * StreamedMedia interface) by providing a contact identifier. * * For this method to work, you must use an AccountFactory which makes Account::FeatureCapabilities * ready. * * This method requires AccountManager::FeatureCore to be ready. * * \return A pointer to an AccountSet object containing the matching accounts. */ AccountSetPtr AccountManager::streamedMediaAudioCallAccounts() const { if (!accountFactory()->features().contains(Account::FeatureCapabilities)) { warning() << "Account filtering by capabilities can only be used with an AccountFactory" << "which makes Account::FeatureCapabilities ready"; return filterAccounts(AccountFilterConstPtr()); } AccountCapabilityFilterPtr filter = AccountCapabilityFilter::create(); filter->addRequestableChannelClassSubset(RequestableChannelClassSpec::streamedMediaAudioCall()); return filterAccounts(filter); } /** * Return a set of accounts containing all accounts that support video calls (using the * StreamedMedia interface) by providing a contact identifier. * * For this method to work, you must use an AccountFactory which makes Account::FeatureCapabilities * ready. * * This method requires AccountManager::FeatureCore to be ready. * * \return A pointer to an AccountSet object containing the matching accounts. */ AccountSetPtr AccountManager::streamedMediaVideoCallAccounts() const { if (!accountFactory()->features().contains(Account::FeatureCapabilities)) { warning() << "Account filtering by capabilities can only be used with an AccountFactory" << "which makes Account::FeatureCapabilities ready"; return filterAccounts(AccountFilterConstPtr()); } AccountCapabilityFilterPtr filter = AccountCapabilityFilter::create(); filter->addRequestableChannelClassSubset(RequestableChannelClassSpec::streamedMediaVideoCall()); return filterAccounts(filter); } /** * Return a set of accounts containing all accounts that support video calls with audio (using the * StreamedMedia interface) by providing a contact identifier. * * For this method to work, you must use an AccountFactory which makes Account::FeatureCapabilities * ready. * * This method requires AccountManager::FeatureCore to be ready. * * \return A pointer to an AccountSet object containing the matching accounts. */ AccountSetPtr AccountManager::streamedMediaVideoCallWithAudioAccounts() const { if (!accountFactory()->features().contains(Account::FeatureCapabilities)) { warning() << "Account filtering by capabilities can only be used with an AccountFactory" << "which makes Account::FeatureCapabilities ready"; return filterAccounts(AccountFilterConstPtr()); } AccountCapabilityFilterPtr filter = AccountCapabilityFilter::create(); filter->addRequestableChannelClassSubset( RequestableChannelClassSpec::streamedMediaVideoCallWithAudio()); return filterAccounts(filter); } /** * Return a set of accounts containing all accounts that support file transfers by * providing a contact identifier. * * For this method to work, you must use an AccountFactory which makes Account::FeatureCapabilities * ready. * * This method requires AccountManager::FeatureCore to be ready. * * \return A pointer to an AccountSet object containing the matching accounts. */ AccountSetPtr AccountManager::fileTransferAccounts() const { if (!accountFactory()->features().contains(Account::FeatureCapabilities)) { warning() << "Account filtering by capabilities can only be used with an AccountFactory" << "which makes Account::FeatureCapabilities ready"; return filterAccounts(AccountFilterConstPtr()); } AccountCapabilityFilterPtr filter = AccountCapabilityFilter::create(); filter->addRequestableChannelClassSubset(RequestableChannelClassSpec::fileTransfer()); return filterAccounts(filter); } /** * Return a set of accounts containing all accounts for the given \a * protocolName. * * This method requires AccountManager::FeatureCore to be ready. * * \param protocolName The name of the protocol used to filter accounts. * \return A pointer to an AccountSet object containing the matching accounts. */ AccountSetPtr AccountManager::accountsByProtocol( const QString &protocolName) const { if (!isReady(FeatureCore)) { warning() << "Account filtering requires AccountManager to be ready"; return filterAccounts(AccountFilterConstPtr()); } QVariantMap filter; filter.insert(QLatin1String("protocolName"), protocolName); return filterAccounts(filter); } /** * Return a set of accounts containing all accounts that match the given \a * filter criteria. * * For AccountCapabilityFilter filtering, an AccountFactory which makes * Account::FeatureCapabilities ready must be used. * * See AccountSet documentation for more details. * * This method requires AccountManager::FeatureCore to be ready. * * \param filter The desired filter. * \return A pointer to an AccountSet object containing the matching accounts. */ AccountSetPtr AccountManager::filterAccounts(const AccountFilterConstPtr &filter) const { if (!isReady(FeatureCore)) { warning() << "Account filtering requires AccountManager to be ready"; return AccountSetPtr(new AccountSet(AccountManagerPtr( (AccountManager *) this), AccountFilterConstPtr())); } return AccountSetPtr(new AccountSet(AccountManagerPtr( (AccountManager *) this), filter)); } /** * Return a set of accounts containing all accounts that match the given \a * filter criteria. * * The \a filter is composed by Account property names and values as map items. * * The following example will return all jabber accounts that are enabled: * * \code * * void MyClass::init() * { * mAM = AccountManager::create(); * connect(mAM->becomeReady(), * SIGNAL(finished(Tp::PendingOperation*)), * SLOT(onAccountManagerReady(Tp::PendingOperation*))); * } * * void MyClass::onAccountManagerReady(Tp::PendingOperation *op) * { * if (op->isError()) { * qWarning() << "Account manager cannot become ready:" << * op->errorName() << "-" << op->errorMessage(); * return; * } * * QVariantMap filter; * filter.insert(QLatin1String("protocolName"), QLatin1String("jabber")); * filter.insert(QLatin1String("enabled"), true); * filteredAccountSet = mAM->filterAccounts(filter); * // connect to AccountSet::accountAdded/accountRemoved signals * QList accounts = filteredAccountSet->accounts(); * // do something with accounts * } * * \endcode * * See AccountSet documentation for more details. * * This method requires AccountManager::FeatureCore to be ready. * * \param filter The desired filter. * \return A pointer to an AccountSet object containing the matching accounts. */ AccountSetPtr AccountManager::filterAccounts(const QVariantMap &filter) const { if (!isReady(FeatureCore)) { warning() << "Account filtering requires AccountManager to be ready"; return AccountSetPtr(new AccountSet(AccountManagerPtr( (AccountManager *) this), QVariantMap())); } return AccountSetPtr(new AccountSet(AccountManagerPtr( (AccountManager *) this), filter)); } /** * Return the account for the given \a path. * * This method requires AccountManager::FeatureCore to be ready. * * \param path The account object path. * \return A pointer to an AccountSet object containing the matching accounts. * \sa allAccounts(), accountsForObjectPaths() */ AccountPtr AccountManager::accountForObjectPath(const QString &path) const { if (!isReady(FeatureCore)) { return AccountPtr(); } return mPriv->accounts.value(path); } /** * \deprecated See accountForObjectPath() */ AccountPtr AccountManager::accountForPath(const QString &path) const { return accountForObjectPath(path); } /** * Return a list of accounts for the given \a paths. * * The returned list will have one AccountPtr object for each given path. If * a given path is invalid the returned AccountPtr object will point to 0. * AccountPtr::isNull() will return true. * * This method requires AccountManager::FeatureCore to be ready. * * \param paths List of accounts object paths. * \return A list of pointers to Account objects for the given * \a paths. Null AccountPtr objects will be used as list elements for each invalid path. * \sa allAccounts(), accountForObjectPath() */ QList AccountManager::accountsForObjectPaths(const QStringList &paths) const { if (!isReady(FeatureCore)) { return QList(); } QList result; foreach (const QString &path, paths) { result << accountForObjectPath(path); } return result; } /** * \deprecated See accountsForObjectPaths() */ QList AccountManager::accountsForPaths(const QStringList &paths) const { return accountsForObjectPaths(paths); } /** * Return a list of the fully qualified names of properties that can be set * when calling createAccount(). * * \return A list of fully qualified D-Bus property names, * such as "org.freedesktop.Telepathy.Account.Enabled". * \sa createAccount() */ QStringList AccountManager::supportedAccountProperties() const { return mPriv->supportedAccountProperties; } /** * Create an account with the given parameters. * * The optional \a properties argument can be used to set any property listed in * supportedAccountProperties() at the time the account is created. * * \param connectionManager The name of the connection manager to create the account * for. * \param protocol The name of the protocol to create the account for. * \param displayName The account display name. * \param parameters The account parameters. * \param properties An optional map from fully qualified D-Bus property * names such as "org.freedesktop.Telepathy.Account.Enabled" * to their values. * \return A PendingAccount object which will emit PendingAccount::finished * when the account has been created of failed its creation process. * \sa supportedAccountProperties() */ PendingAccount *AccountManager::createAccount(const QString &connectionManager, const QString &protocol, const QString &displayName, const QVariantMap ¶meters, const QVariantMap &properties) { return new PendingAccount(AccountManagerPtr(this), connectionManager, protocol, displayName, parameters, properties); } /** * Return the Client::AccountManagerInterface interface proxy object for this * account manager. 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::AccountManagerInterface object for * this AccountManager object. */ Client::AccountManagerInterface *AccountManager::baseInterface() const { return mPriv->baseInterface; } void AccountManager::introspectMain() { mPriv->introspectMain(mPriv); } void AccountManager::gotMainProperties(QDBusPendingCallWatcher *watcher) { QDBusPendingReply reply = *watcher; QVariantMap props; if (!reply.isError()) { mPriv->gotInitialAccounts = true; debug() << "Got reply to Properties.GetAll(AccountManager)"; props = reply.value(); if (props.contains(QLatin1String("Interfaces"))) { setInterfaces(qdbus_cast(props[QLatin1String("Interfaces")])); mPriv->readinessHelper->setInterfaces(interfaces()); } if (props.contains(QLatin1String("SupportedAccountProperties"))) { mPriv->supportedAccountProperties = qdbus_cast(props[QLatin1String("SupportedAccountProperties")]); } QSet paths = mPriv->getAccountPathsFromProps(props); foreach (const QString &path, paths) { mPriv->addAccountForPath(path); } mPriv->checkIntrospectionCompleted(); } else { if (mPriv->reintrospectionRetries++ < maxReintrospectionRetries) { int retryInterval = reintrospectionRetryInterval; if (reply.error().type() == QDBusError::TimedOut) { retryInterval = 0; } QTimer::singleShot(retryInterval, this, SLOT(introspectMain())); } else { warning() << "GetAll(AccountManager) failed with" << reply.error().name() << ":" << reply.error().message(); mPriv->readinessHelper->setIntrospectCompleted(FeatureCore, false, reply.error()); } } watcher->deleteLater(); } void AccountManager::onAccountReady(Tp::PendingOperation *op) { PendingReady *pr = qobject_cast(op); AccountPtr account(AccountPtr::qObjectCast(pr->proxy())); QString path = account->objectPath(); /* Some error occurred or the account was removed before become ready */ if (op->isError() || !mPriv->incompleteAccounts.contains(path)) { mPriv->incompleteAccounts.remove(path); mPriv->checkIntrospectionCompleted(); return; } mPriv->incompleteAccounts.remove(path); // We shouldn't end up here twice for the same account - that would also mean newAccount being // emitted twice for an account, and AccountSets getting confused as a result Q_ASSERT(!mPriv->accounts.contains(path)); mPriv->accounts.insert(path, account); if (isReady(FeatureCore)) { emit newAccount(account); } mPriv->checkIntrospectionCompleted(); } void AccountManager::onAccountValidityChanged(const QDBusObjectPath &objectPath, bool valid) { if (!mPriv->gotInitialAccounts) { return; } QString path = objectPath.path(); if (!mPriv->incompleteAccounts.contains(path) && !mPriv->accounts.contains(path)) { debug() << "New account" << path; mPriv->addAccountForPath(path); } } void AccountManager::onAccountRemoved(const QDBusObjectPath &objectPath) { if (!mPriv->gotInitialAccounts) { return; } QString path = objectPath.path(); /* the account is either in mPriv->incompleteAccounts or mPriv->accounts */ if (mPriv->accounts.contains(path)) { mPriv->accounts.remove(path); if (isReady(FeatureCore)) { debug() << "Account" << path << "removed"; } else { debug() << "Account" << path << "removed while the AM " "was not completely introspected"; } } else if (mPriv->incompleteAccounts.contains(path)) { mPriv->incompleteAccounts.remove(path); debug() << "Account" << path << "was removed, but it was " "not completely introspected, ignoring"; } else { debug() << "Got AccountRemoved for unknown account" << path << ", ignoring"; } } /** * \fn void AccountManager::newAccount(const Tp::AccountPtr &account) * * Emitted when a new account is created. * * The new \a account will have the features set in the AccountFactory used by this * account manager ready and the same connection, channel and contact factories as used by this * account manager. * * \param account The newly created account. */ } // Tp