/**
* 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/contact-manager-internal.h"
#include "TelepathyQt/_gen/contact-manager.moc.hpp"
#include "TelepathyQt/debug-internal.h"
#include "TelepathyQt/future-internal.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace Tp
{
struct TP_QT_NO_EXPORT ContactManager::Private
{
Private(ContactManager *parent, Connection *connection);
~Private();
// avatar specific methods
bool buildAvatarFileName(QString token, bool createDir,
QString &avatarFileName, QString &mimeTypeFileName);
Features realFeatures(const Features &features);
QSet interfacesForFeatures(const Features &features);
ContactManager *parent;
WeakPtr connection;
ContactManager::Roster *roster;
QHash > contacts;
QHash tracking;
Features supportedFeatures;
// avatar
QSet requestAvatarsQueue;
bool requestAvatarsIdle;
// contact info
PendingRefreshContactInfo *refreshInfoOp;
};
ContactManager::Private::Private(ContactManager *parent, Connection *connection)
: parent(parent),
connection(connection),
roster(new ContactManager::Roster(parent)),
requestAvatarsIdle(false),
refreshInfoOp(nullptr)
{
}
ContactManager::Private::~Private()
{
delete refreshInfoOp;
delete roster;
}
bool ContactManager::Private::buildAvatarFileName(QString token, bool createDir,
QString &avatarFileName, QString &mimeTypeFileName)
{
QString cacheDir = QString(QLatin1String(qgetenv("XDG_CACHE_HOME")));
if (cacheDir.isEmpty()) {
cacheDir = QString(QLatin1String("%1/.cache")).arg(QLatin1String(qgetenv("HOME")));
}
ConnectionPtr conn(parent->connection());
QString path = QString(QLatin1String("%1/telepathy/avatars/%2/%3")).
arg(cacheDir).arg(conn->cmName()).arg(conn->protocolName());
if (createDir && !QDir().mkpath(path)) {
return false;
}
avatarFileName = QString(QLatin1String("%1/%2")).arg(path).arg(escapeAsIdentifier(token));
mimeTypeFileName = QString(QLatin1String("%1.mime")).arg(avatarFileName);
return true;
}
Features ContactManager::Private::realFeatures(const Features &features)
{
Features ret(features);
ret.unite(parent->connection()->contactFactory()->features());
// FeatureAvatarData depends on FeatureAvatarToken
if (ret.contains(Contact::FeatureAvatarData) &&
!ret.contains(Contact::FeatureAvatarToken)) {
ret.insert(Contact::FeatureAvatarToken);
}
return ret;
}
QSet ContactManager::Private::interfacesForFeatures(const Features &features)
{
Features supported = parent->supportedFeatures();
QSet ret;
foreach (const Feature &feature, features) {
parent->ensureTracking(feature);
if (supported.contains(feature)) {
// Only query interfaces which are reported as supported to not get an error
ret.insert(parent->featureToInterface(feature));
}
}
return ret;
}
ContactManager::PendingRefreshContactInfo::PendingRefreshContactInfo(const ConnectionPtr &conn)
: PendingOperation(conn),
mConn(conn)
{
}
ContactManager::PendingRefreshContactInfo::~PendingRefreshContactInfo()
{
}
void ContactManager::PendingRefreshContactInfo::addContact(Contact *contact)
{
mToRequest.insert(contact->handle()[0]);
}
void ContactManager::PendingRefreshContactInfo::refreshInfo()
{
Q_ASSERT(!mToRequest.isEmpty());
if (!mConn->isValid()) {
setFinishedWithError(TP_QT_ERROR_NOT_AVAILABLE,
QLatin1String("Connection is invalid"));
return;
}
if (!mConn->hasInterface(TP_QT_IFACE_CONNECTION_INTERFACE_CONTACT_INFO)) {
setFinishedWithError(TP_QT_ERROR_NOT_IMPLEMENTED,
QLatin1String("Connection does not support ContactInfo interface"));
return;
}
debug() << "Calling ContactInfo.RefreshContactInfo for" << mToRequest.size() << "handles";
Client::ConnectionInterfaceContactInfoInterface *contactInfoInterface =
mConn->interface();
Q_ASSERT(contactInfoInterface);
PendingVoid *nested = new PendingVoid(
contactInfoInterface->RefreshContactInfo(mToRequest.toList()),
mConn);
connect(nested,
SIGNAL(finished(Tp::PendingOperation*)),
SLOT(onRefreshInfoFinished(Tp::PendingOperation*)));
}
void ContactManager::PendingRefreshContactInfo::onRefreshInfoFinished(PendingOperation *op)
{
if (op->isError()) {
warning() << "ContactInfo.RefreshContactInfo failed with" <<
op->errorName() << "-" << op->errorMessage();
setFinishedWithError(op->errorName(), op->errorMessage());
} else {
debug() << "Got reply to ContactInfo.RefreshContactInfo";
setFinished();
}
}
/**
* \class ContactManager
* \ingroup clientconn
* \headerfile TelepathyQt/contact-manager.h
*
* \brief The ContactManager class is responsible for managing contacts.
*
* See \ref async_model, \ref shared_ptr
*/
/**
* Construct a new ContactManager object.
*
* \param connection The connection owning this ContactManager.
*/
ContactManager::ContactManager(Connection *connection)
: Object(),
mPriv(new Private(this, connection))
{
}
/**
* Class destructor.
*/
ContactManager::~ContactManager()
{
delete mPriv;
}
/**
* Return the connection owning this ContactManager.
*
* \return A pointer to the Connection object.
*/
ConnectionPtr ContactManager::connection() const
{
return ConnectionPtr(mPriv->connection);
}
/**
* Return the features that are expected to work on contacts on this ContactManager connection.
*
* This method requires Connection::FeatureCore to be ready.
*
* \return The supported features as a set of Feature objects.
*/
Features ContactManager::supportedFeatures() const
{
if (mPriv->supportedFeatures.isEmpty() &&
connection()->interfaces().contains(TP_QT_IFACE_CONNECTION_INTERFACE_CONTACTS)) {
Features allFeatures = Features()
<< Contact::FeatureAlias
<< Contact::FeatureAvatarToken
<< Contact::FeatureAvatarData
<< Contact::FeatureSimplePresence
<< Contact::FeatureCapabilities
<< Contact::FeatureLocation
<< Contact::FeatureInfo
<< Contact::FeatureRosterGroups
<< Contact::FeatureAddresses
<< Contact::FeatureClientTypes;
QStringList interfaces = connection()->lowlevel()->contactAttributeInterfaces();
foreach (const Feature &feature, allFeatures) {
if (interfaces.contains(featureToInterface(feature))) {
mPriv->supportedFeatures.insert(feature);
}
}
debug() << mPriv->supportedFeatures.size() << "contact features supported using" << this;
}
return mPriv->supportedFeatures;
}
/**
* Return the progress made in retrieving the contact list.
*
* Change notification is via the stateChanged() signal.
*
* This method requires Connection::FeatureRoster to be ready.
*
* \return The contact list state as #ContactListState.
* \sa stateChanged()
*/
ContactListState ContactManager::state() const
{
return mPriv->roster->state();
}
/**
* Return a list of relevant contacts (a reasonable guess as to what should
* be displayed as "the contact list").
*
* This may include any or all of: contacts whose presence the user receives,
* contacts who are allowed to see the user's presence, contacts stored in
* some persistent contact list on the server, contacts who the user
* has blocked from communicating with them, or contacts who are relevant
* in some other way.
*
* User interfaces displaying a contact list will probably want to filter this
* list and display some suitable subset of it.
*
* On protocols where there is no concept of presence or a centrally-stored
* contact list (like IRC), this method may return an empty list.
*
* Change notification is via the allKnownContactsChanged() signal.
*
* This method requires Connection::FeatureRoster to be ready.
*
* \return A set of pointers to the Contact objects.
* \sa allKnownContactsChanged()
*/
Contacts ContactManager::allKnownContacts() const
{
if (!connection()->isReady(Connection::FeatureRoster)) {
warning() << "Calling allKnownContacts() before FeatureRoster is ready";
return Contacts();
}
return mPriv->roster->allKnownContacts();
}
/**
* Return a list of user-defined contact list groups' names.
*
* Change notification is via the groupAdded(), groupRemoved() and groupRenamed() signals.
*
* This method requires Connection::FeatureRosterGroups to be ready.
*
* \return The list of user-defined contact list groups names.
* \sa groupMembersChanged(), groupAdded(), groupRemoved(), groupRenamed()
*/
QStringList ContactManager::allKnownGroups() const
{
if (!connection()->isReady(Connection::FeatureRosterGroups)) {
return QStringList();
}
return mPriv->roster->allKnownGroups();
}
/**
* Attempt to add an user-defined contact list group named \a group.
*
* On some protocols (e.g. XMPP) empty groups are not represented on the server,
* so disconnecting from the server and reconnecting might cause empty groups to
* vanish.
*
* The returned pending operation will finish successfully if the group already
* exists.
*
* Change notification is via the groupAdded() signal.
*
* This method requires Connection::FeatureRosterGroups to be ready.
*
* \param group The group name.
* \return A PendingOperation which will emit PendingOperation::finished
* when an attempt has been made to add an user-defined contact list group.
* \sa allKnownGroups(), groupAdded(), addContactsToGroup()
*/
PendingOperation *ContactManager::addGroup(const QString &group)
{
if (!connection()->isValid()) {
return new PendingFailure(TP_QT_ERROR_NOT_AVAILABLE,
QLatin1String("Connection is invalid"),
connection());
} else if (!connection()->isReady(Connection::FeatureRosterGroups)) {
return new PendingFailure(TP_QT_ERROR_NOT_AVAILABLE,
QLatin1String("Connection::FeatureRosterGroups is not ready"),
connection());
}
return mPriv->roster->addGroup(group);
}
/**
* Attempt to remove an user-defined contact list group named \a group.
*
* Change notification is via the groupRemoved() signal.
*
* This method requires Connection::FeatureRosterGroups to be ready.
*
* \param group The group name.
* \return A PendingOperation which will emit PendingOperation::finished()
* when an attempt has been made to remove an user-defined contact list group.
* \sa allKnownGroups(), groupRemoved(), removeContactsFromGroup()
*/
PendingOperation *ContactManager::removeGroup(const QString &group)
{
if (!connection()->isValid()) {
return new PendingFailure(TP_QT_ERROR_NOT_AVAILABLE,
QLatin1String("Connection is invalid"),
connection());
} else if (!connection()->isReady(Connection::FeatureRosterGroups)) {
return new PendingFailure(TP_QT_ERROR_NOT_AVAILABLE,
QLatin1String("Connection::FeatureRosterGroups is not ready"),
connection());
}
return mPriv->roster->removeGroup(group);
}
/**
* Return the contacts in the given user-defined contact list group
* named \a group.
*
* Change notification is via the groupMembersChanged() signal.
*
* This method requires Connection::FeatureRosterGroups to be ready.
*
* \param group The group name.
* \return A set of pointers to the Contact objects, or an empty set if the group does not exist.
* \sa allKnownGroups(), groupMembersChanged()
*/
Contacts ContactManager::groupContacts(const QString &group) const
{
if (!connection()->isReady(Connection::FeatureRosterGroups)) {
return Contacts();
}
return mPriv->roster->groupContacts(group);
}
/**
* Attempt to add the given \a contacts to the user-defined contact list
* group named \a group.
*
* Change notification is via the groupMembersChanged() signal.
*
* This method requires Connection::FeatureRosterGroups to be ready.
*
* \param group The group name.
* \param contacts Contacts to add.
* \return A PendingOperation which will emit PendingOperation::finished()
* when an attempt has been made to add the contacts to the user-defined
* contact list group.
* \sa groupMembersChanged(), groupContacts()
*/
PendingOperation *ContactManager::addContactsToGroup(const QString &group,
const QList &contacts)
{
if (!connection()->isValid()) {
return new PendingFailure(TP_QT_ERROR_NOT_AVAILABLE,
QLatin1String("Connection is invalid"),
connection());
} else if (!connection()->isReady(Connection::FeatureRosterGroups)) {
return new PendingFailure(TP_QT_ERROR_NOT_AVAILABLE,
QLatin1String("Connection::FeatureRosterGroups is not ready"),
connection());
}
return mPriv->roster->addContactsToGroup(group, contacts);
}
/**
* Attempt to remove the given \a contacts from the user-defined contact list
* group named \a group.
*
* Change notification is via the groupMembersChanged() signal.
*
* This method requires Connection::FeatureRosterGroups to be ready.
*
* \param group The group name.
* \param contacts Contacts to remove.
* \return A PendingOperation which will PendingOperation::finished
* when an attempt has been made to remove the contacts from the user-defined
* contact list group.
* \sa groupMembersChanged(), groupContacts()
*/
PendingOperation *ContactManager::removeContactsFromGroup(const QString &group,
const QList &contacts)
{
if (!connection()->isValid()) {
return new PendingFailure(TP_QT_ERROR_NOT_AVAILABLE,
QLatin1String("Connection is invalid"),
connection());
} else if (!connection()->isReady(Connection::FeatureRosterGroups)) {
return new PendingFailure(TP_QT_ERROR_NOT_AVAILABLE,
QLatin1String("Connection::FeatureRosterGroups is not ready"),
connection());
}
return mPriv->roster->removeContactsFromGroup(group, contacts);
}
/**
* Return whether subscribing to additional contacts' presence is supported.
*
* In some protocols, the list of contacts whose presence can be seen is
* fixed, so we can't subscribe to the presence of additional contacts.
*
* Notably, in link-local XMPP, you can see the presence of everyone on the
* local network, and trying to add more subscriptions would be meaningless.
*
* This method requires Connection::FeatureRoster to be ready.
*
* \return \c true if Contact::requestPresenceSubscription() and
* requestPresenceSubscription() are likely to succeed, \c false otherwise.
* \sa requestPresenceSubscription(), subscriptionRequestHasMessage()
*/
bool ContactManager::canRequestPresenceSubscription() const
{
if (!connection()->isReady(Connection::FeatureRoster)) {
return false;
}
return mPriv->roster->canRequestPresenceSubscription();
}
/**
* Return whether a message can be sent when subscribing to contacts'
* presence.
*
* If no message will actually be sent, user interfaces should avoid prompting
* the user for a message, and use an empty string for the message argument.
*
* This method requires Connection::FeatureRoster to be ready.
*
* \return \c true if the message argument to Contact::requestPresenceSubscription() and
* requestPresenceSubscription() is actually used, \c false otherwise.
* \sa canRemovePresenceSubscription(), requestPresenceSubscription()
*/
bool ContactManager::subscriptionRequestHasMessage() const
{
if (!connection()->isReady(Connection::FeatureRoster)) {
return false;
}
return mPriv->roster->subscriptionRequestHasMessage();
}
/**
* Attempt to subscribe to the presence of the given contacts.
*
* This operation is sometimes called "adding contacts to the buddy
* list" or "requesting authorization".
*
* On most protocols, the contacts will need to give permission
* before the user will be able to receive their presence: if so, they will
* be in presence state Contact::PresenceStateAsk until they authorize
* or deny the request.
*
* The returned PendingOperation will return successfully when a request to
* subscribe to the contacts' presence has been submitted, or fail if this
* cannot happen. In particular, it does not wait for the contacts to give
* permission for the presence subscription.
*
* This method requires Connection::FeatureRoster to be ready.
*
* \param contacts Contacts whose presence is desired
* \param message A message from the user which is either transmitted to the
* contacts, or ignored, depending on the protocol
* \return A PendingOperation which will PendingOperation::finished()
* when an attempt has been made to subscribe to the contacts' presence.
* \sa canRequestPresenceSubscription(), subscriptionRequestHasMessage()
*/
PendingOperation *ContactManager::requestPresenceSubscription(
const QList &contacts, const QString &message)
{
if (!connection()->isValid()) {
return new PendingFailure(TP_QT_ERROR_NOT_AVAILABLE,
QLatin1String("Connection is invalid"),
connection());
} else if (!connection()->isReady(Connection::FeatureRoster)) {
return new PendingFailure(TP_QT_ERROR_NOT_AVAILABLE,
QLatin1String("Connection::FeatureRoster is not ready"),
connection());
}
return mPriv->roster->requestPresenceSubscription(contacts, message);
}
/**
* Return whether the user can stop receiving the presence of a contact
* whose presence they have subscribed to.
*
* This method requires Connection::FeatureRoster to be ready.
*
* \return \c true if Contact::removePresenceSubscription() and
* removePresenceSubscription() are likely to succeed
* for contacts with subscription state Contact::PresenceStateYes,
* \c false otherwise.
* \sa removePresenceSubscription(), subscriptionRemovalHasMessage()
*/
bool ContactManager::canRemovePresenceSubscription() const
{
if (!connection()->isReady(Connection::FeatureRoster)) {
return false;
}
return mPriv->roster->canRemovePresenceSubscription();
}
/**
* Return whether a message can be sent when removing an existing subscription
* to the presence of a contact.
*
* If no message will actually be sent, user interfaces should avoid prompting
* the user for a message, and use an empty string for the message argument.
*
* This method requires Connection::FeatureRoster to be ready.
*
* \return \c true if the message argument to Contact::removePresenceSubscription() and
* removePresenceSubscription() is actually used,
* for contacts with subscription state Contact::PresenceStateYes,
* \c false otherwise.
* \sa canRemovePresencePublication(), removePresenceSubscription()
*/
bool ContactManager::subscriptionRemovalHasMessage() const
{
if (!connection()->isReady(Connection::FeatureRoster)) {
return false;
}
return mPriv->roster->subscriptionRemovalHasMessage();
}
/**
* Return whether the user can cancel a request to subscribe to a contact's
* presence before that contact has responded.
*
* This method requires Connection::FeatureRoster to be ready.
*
* \return \c true if Contact::removePresenceSubscription() and
* removePresenceSubscription() are likely to succeed
* for contacts with subscription state Contact::PresenceStateAsk,
* \c false otherwise.
* \sa removePresenceSubscription(), subscriptionRescindingHasMessage()
*/
bool ContactManager::canRescindPresenceSubscriptionRequest() const
{
if (!connection()->isReady(Connection::FeatureRoster)) {
return false;
}
return mPriv->roster->canRescindPresenceSubscriptionRequest();
}
/**
* Return whether a message can be sent when cancelling a request to
* subscribe to the presence of a contact.
*
* If no message will actually be sent, user interfaces should avoid prompting
* the user for a message, and use an empty string for the message argument.
*
* This method requires Connection::FeatureRoster to be ready.
*
* \return \c true if the message argument to Contact::removePresenceSubscription() and
* removePresenceSubscription() is actually used,
* for contacts with subscription state Contact::PresenceStateAsk,
* \c false otherwise.
* \sa canRescindPresenceSubscriptionRequest(), removePresenceSubscription()
*/
bool ContactManager::subscriptionRescindingHasMessage() const
{
if (!connection()->isReady(Connection::FeatureRoster)) {
return false;
}
return mPriv->roster->subscriptionRescindingHasMessage();
}
/**
* Attempt to stop receiving the presence of the given contacts, or cancel
* a request to subscribe to their presence that was previously sent.
*
* This method requires Connection::FeatureRoster to be ready.
*
* \param contacts Contacts whose presence is no longer required
* \message A message from the user which is either transmitted to the
* contacts, or ignored, depending on the protocol
* \return A PendingOperation which will PendingOperation::finished()
* when an attempt has been made to remove any subscription to the contacts' presence.
* \sa canRemovePresenceSubscription(), canRescindPresenceSubscriptionRequest(),
* subscriptionRemovalHasMessage(), subscriptionRescindingHasMessage()
*/
PendingOperation *ContactManager::removePresenceSubscription(
const QList &contacts, const QString &message)
{
if (!connection()->isValid()) {
return new PendingFailure(TP_QT_ERROR_NOT_AVAILABLE,
QLatin1String("Connection is invalid"),
connection());
} else if (!connection()->isReady(Connection::FeatureRoster)) {
return new PendingFailure(TP_QT_ERROR_NOT_AVAILABLE,
QLatin1String("Connection::FeatureRoster is not ready"),
connection());
}
return mPriv->roster->removePresenceSubscription(contacts, message);
}
/**
* Return true if the publication of the user's presence to contacts can be
* authorized.
*
* This is always true, unless the protocol has no concept of authorizing
* publication (in which case contacts' publication status can never be
* Contact::PresenceStateAsk).
*
* This method requires Connection::FeatureRoster to be ready.
*
* \return \c true if Contact::authorizePresencePublication() and
* authorizePresencePublication() are likely to succeed
* for contacts with subscription state Contact::PresenceStateAsk,
* \c false otherwise.
* \sa publicationAuthorizationHasMessage(), authorizePresencePublication()
*/
bool ContactManager::canAuthorizePresencePublication() const
{
if (!connection()->isReady(Connection::FeatureRoster)) {
return false;
}
return mPriv->roster->canAuthorizePresencePublication();
}
/**
* Return whether a message can be sent when authorizing a request from a
* contact that the user's presence is published to them.
*
* If no message will actually be sent, user interfaces should avoid prompting
* the user for a message, and use an empty string for the message argument.
*
* This method requires Connection::FeatureRoster to be ready.
*
* \return \c true if the message argument to Contact::authorizePresencePublication() and
* authorizePresencePublication() is actually used,
* for contacts with subscription state Contact::PresenceStateAsk,
* \c false otherwise.
* \sa canAuthorizePresencePublication(), authorizePresencePublication()
*/
bool ContactManager::publicationAuthorizationHasMessage() const
{
if (!connection()->isReady(Connection::FeatureRoster)) {
return false;
}
return mPriv->roster->publicationAuthorizationHasMessage();
}
/**
* If the given contacts have asked the user to publish presence to them,
* grant permission for this publication to take place.
*
* This method requires Connection::FeatureRoster to be ready.
*
* \param contacts Contacts who should be allowed to receive the user's
* presence
* \message A message from the user which is either transmitted to the
* contacts, or ignored, depending on the protocol
* \return A PendingOperation which will emit PendingOperation::fininshed
* when an attempt has been made to authorize publication of the user's presence
* to the contacts.
* \sa canAuthorizePresencePublication(), publicationAuthorizationHasMessage()
*/
PendingOperation *ContactManager::authorizePresencePublication(
const QList &contacts, const QString &message)
{
if (!connection()->isValid()) {
return new PendingFailure(TP_QT_ERROR_NOT_AVAILABLE,
QLatin1String("Connection is invalid"),
connection());
} else if (!connection()->isReady(Connection::FeatureRoster)) {
return new PendingFailure(TP_QT_ERROR_NOT_AVAILABLE,
QLatin1String("Connection::FeatureRoster is not ready"),
connection());
}
return mPriv->roster->authorizePresencePublication(contacts, message);
}
/**
* Return whether a message can be sent when rejecting a request from a
* contact that the user's presence is published to them.
*
* If no message will actually be sent, user interfaces should avoid prompting
* the user for a message, and use an empty string for the message argument.
*
* This method requires Connection::FeatureRoster to be ready.
*
* \return \c true if the message argument to Contact::removePresencePublication() and
* removePresencePublication() is actually used,
* for contacts with subscription state Contact::PresenceStateAsk,
* \c false otherwise.
* \sa canRemovePresencePublication(), removePresencePublication()
*/
bool ContactManager::publicationRejectionHasMessage() const
{
if (!connection()->isReady(Connection::FeatureRoster)) {
return false;
}
return mPriv->roster->publicationRejectionHasMessage();
}
/**
* Return true if the publication of the user's presence to contacts can be
* removed, even after permission has been given.
*
* (Rejecting requests for presence to be published is always allowed.)
*
* This method requires Connection::FeatureRoster to be ready.
*
* \return \c true if Contact::removePresencePublication() and
* removePresencePublication() are likely to succeed
* for contacts with subscription state Contact::PresenceStateYes,
* \c false otherwise.
* \sa publicationRemovalHasMessage(), removePresencePublication()
*/
bool ContactManager::canRemovePresencePublication() const
{
if (!connection()->isReady(Connection::FeatureRoster)) {
return false;
}
return mPriv->roster->canRemovePresencePublication();
}
/**
* Return whether a message can be sent when revoking earlier permission
* that the user's presence is published to a contact.
*
* If no message will actually be sent, user interfaces should avoid prompting
* the user for a message, and use an empty string for the message argument.
*
* This method requires Connection::FeatureRoster to be ready.
*
* \return \c true if the message argument to Contact::removePresencePublication and
* removePresencePublication() is actually used,
* for contacts with subscription state Contact::PresenceStateYes,
* \c false otherwise.
* \sa canRemovePresencePublication(), removePresencePublication()
*/
bool ContactManager::publicationRemovalHasMessage() const
{
if (!connection()->isReady(Connection::FeatureRoster)) {
return false;
}
return mPriv->roster->publicationRemovalHasMessage();
}
/**
* If the given contacts have asked the user to publish presence to them,
* deny this request (this should always succeed, unless a network error
* occurs).
*
* If the given contacts already have permission to receive
* the user's presence, attempt to revoke that permission (this might not
* be supported by the protocol - canRemovePresencePublication
* indicates whether it is likely to succeed).
*
* This method requires Connection::FeatureRoster to be ready.
*
* \param contacts Contacts who should no longer be allowed to receive the
* user's presence
* \message A message from the user which is either transmitted to the
* contacts, or ignored, depending on the protocol
* \return A PendingOperation which will emit PendingOperation::finished()
* when an attempt has been made to remove any publication of the user's presence to the
* contacts.
* \sa canRemovePresencePublication(), publicationRejectionHasMessage(),
* publicationRemovalHasMessage()
*/
PendingOperation *ContactManager::removePresencePublication(
const QList &contacts, const QString &message)
{
if (!connection()->isValid()) {
return new PendingFailure(TP_QT_ERROR_NOT_AVAILABLE,
QLatin1String("Connection is invalid"),
connection());
} else if (!connection()->isReady(Connection::FeatureRoster)) {
return new PendingFailure(TP_QT_ERROR_NOT_AVAILABLE,
QLatin1String("Connection::FeatureRoster is not ready"),
connection());
}
return mPriv->roster->removePresencePublication(contacts, message);
}
/**
* Remove completely contacts from the server. It has the same effect than
* calling removePresencePublication() and removePresenceSubscription(),
* but also remove from 'stored' list if it exists.
*
* This method requires Connection::FeatureRoster to be ready.
*
* \param contacts Contacts who should be removed
* \message A message from the user which is either transmitted to the
* contacts, or ignored, depending on the protocol
* \return A PendingOperation which will emit PendingOperation::finished
* when an attempt has been made to remove any publication of the user's presence to
* the contacts.
*/
PendingOperation *ContactManager::removeContacts(
const QList &contacts, const QString &message)
{
if (!connection()->isValid()) {
return new PendingFailure(TP_QT_ERROR_NOT_AVAILABLE,
QLatin1String("Connection is invalid"),
connection());
} else if (!connection()->isReady(Connection::FeatureRoster)) {
return new PendingFailure(TP_QT_ERROR_NOT_AVAILABLE,
QLatin1String("Connection::FeatureRoster is not ready"),
connection());
}
return mPriv->roster->removeContacts(contacts, message);
}
/**
* Return whether this protocol has a list of blocked contacts.
*
* This method requires Connection::FeatureRoster to be ready.
*
* \return \c true if blockContacts() is likely to succeed, \c false otherwise.
*/
bool ContactManager::canBlockContacts() const
{
if (!connection()->isReady(Connection::FeatureRoster)) {
return false;
}
return mPriv->roster->canBlockContacts();
}
/**
* Return whether this protocol can report abusive contacts.
*
* This method requires Connection::FeatureRoster to be ready.
*
* \return \c true if reporting abuse when blocking contacts is supported, \c false otherwise.
*/
bool ContactManager::canReportAbuse() const
{
if (!connection()->isReady(Connection::FeatureRoster)) {
return false;
}
return mPriv->roster->canReportAbuse();
}
/**
* Block the given contacts. Blocked contacts cannot send messages
* to the user; depending on the protocol, blocking a contact may
* have other effects.
*
* This method requires Connection::FeatureRoster to be ready.
*
* \param contacts Contacts that should be blocked.
* \return A PendingOperation which will emit PendingOperation::finished()
* when an attempt has been made to take the requested action.
* \sa canBlockContacts(), unblockContacts(), blockContactsAndReportAbuse()
*/
PendingOperation *ContactManager::blockContacts(const QList &contacts)
{
return mPriv->roster->blockContacts(contacts, true, false);
}
/**
* Block the given contacts and additionally report abusive behaviour
* to the server.
*
* If reporting abusive behaviour is not supported by the protocol,
* this method has the same effect as blockContacts().
*
* This method requires Connection::FeatureRoster to be ready.
*
* \param contacts Contacts who should be added to the list of blocked contacts.
* \return A PendingOperation which will emit PendingOperation::finished()
* when an attempt has been made to take the requested action.
* \sa canBlockContacts(), canReportAbuse(), blockContacts()
*/
PendingOperation *ContactManager::blockContactsAndReportAbuse(
const QList &contacts)
{
return mPriv->roster->blockContacts(contacts, true, true);
}
/**
* Unblock the given contacts.
*
* This method requires Connection::FeatureRoster to be ready.
*
* \param contacts Contacts that should be unblocked.
* \return A PendingOperation which will emit PendingOperation::finished()
* when an attempt has been made to take the requested action.
* \sa canBlockContacts(), blockContacts(), blockContactsAndReportAbuse()
*/
PendingOperation *ContactManager::unblockContacts(const QList &contacts)
{
return mPriv->roster->blockContacts(contacts, false, false);
}
PendingContacts *ContactManager::contactsForHandles(const UIntList &handles,
const Features &features)
{
QMap satisfyingContacts;
QSet otherContacts;
Features missingFeatures;
if (!connection()->isValid()) {
return new PendingContacts(ContactManagerPtr(this), handles, features, Features(),
QStringList(), satisfyingContacts, otherContacts,
TP_QT_ERROR_NOT_AVAILABLE,
QLatin1String("Connection is invalid"));
} else if (!connection()->isReady(Connection::FeatureCore)) {
return new PendingContacts(ContactManagerPtr(this), handles, features, Features(),
QStringList(), satisfyingContacts, otherContacts,
TP_QT_ERROR_NOT_AVAILABLE,
QLatin1String("Connection::FeatureCore is not ready"));
}
Features realFeatures = mPriv->realFeatures(features);
ConnectionLowlevelPtr connLowlevel = connection()->lowlevel();
if (connLowlevel->hasImmortalHandles() && realFeatures.isEmpty()) {
// try to avoid a roundtrip if all handles have an id set and no feature was requested
foreach (uint handle, handles) {
if (connLowlevel->hasContactId(handle)) {
ContactPtr contact = ensureContact(handle,
connLowlevel->contactId(handle), realFeatures);
satisfyingContacts.insert(handle, contact);
}
}
}
foreach (uint handle, handles) {
ContactPtr contact = lookupContactByHandle(handle);
if (contact) {
if ((realFeatures - contact->requestedFeatures()).isEmpty()) {
// Contact exists and has all the requested features
satisfyingContacts.insert(handle, contact);
} else {
// Contact exists but is missing features
otherContacts.insert(handle);
missingFeatures.unite(realFeatures - contact->requestedFeatures());
}
} else {
// Contact doesn't exist - we need to get all of the features (same as unite(features))
missingFeatures = realFeatures;
otherContacts.insert(handle);
}
}
QSet interfaces = mPriv->interfacesForFeatures(missingFeatures);
PendingContacts *contacts =
new PendingContacts(ContactManagerPtr(this), handles, features, missingFeatures,
interfaces.toList(), satisfyingContacts, otherContacts);
return contacts;
}
PendingContacts *ContactManager::contactsForHandles(const ReferencedHandles &handles,
const Features &features)
{
return contactsForHandles(handles.toList(), features);
}
PendingContacts *ContactManager::contactsForHandles(const HandleIdentifierMap &handles,
const Features &features)
{
connection()->lowlevel()->injectContactIds(handles);
return contactsForHandles(handles.keys(), features);
}
PendingContacts *ContactManager::contactsForIdentifiers(const QStringList &identifiers,
const Features &features)
{
if (!connection()->isValid()) {
return new PendingContacts(ContactManagerPtr(this), identifiers,
PendingContacts::ForIdentifiers, features, QStringList(),
TP_QT_ERROR_NOT_AVAILABLE,
QLatin1String("Connection is invalid"));
} else if (!connection()->isReady(Connection::FeatureCore)) {
return new PendingContacts(ContactManagerPtr(this), identifiers,
PendingContacts::ForIdentifiers, features, QStringList(),
TP_QT_ERROR_NOT_AVAILABLE,
QLatin1String("Connection::FeatureCore is not ready"));
}
Features realFeatures = mPriv->realFeatures(features);
PendingContacts *contacts = new PendingContacts(ContactManagerPtr(this), identifiers,
PendingContacts::ForIdentifiers, realFeatures, QStringList());
return contacts;
}
/**
* Request contacts and enable their \a features using a given field in their vcards.
*
* This method requires Connection::FeatureCore to be ready.
*
* \param vcardField The vcard field of the addresses we are requesting.
* Supported fields can be found in ProtocolInfo::addressableVCardFields().
* \param vcardAddresses The addresses to get contacts for. The address types must match
* the given vcard field.
* \param features The Contact features to enable.
* \return A PendingContacts, which will emit PendingContacts::finished
* when the contacts are retrieved or an error occurred.
* \sa contactsForHandles(), contactsForIdentifiers(), contactsForUris(),
* ProtocolInfo::normalizeVCardAddress()
*/
PendingContacts *ContactManager::contactsForVCardAddresses(const QString &vcardField,
const QStringList &vcardAddresses, const Features &features)
{
if (!connection()->isValid()) {
return new PendingContacts(ContactManagerPtr(this), vcardField, vcardAddresses,
features, QStringList(),
TP_QT_ERROR_NOT_AVAILABLE,
QLatin1String("Connection is invalid"));
} else if (!connection()->isReady(Connection::FeatureCore)) {
return new PendingContacts(ContactManagerPtr(this), vcardField, vcardAddresses,
features, QStringList(),
TP_QT_ERROR_NOT_AVAILABLE,
QLatin1String("Connection::FeatureCore is not ready"));
}
Features realFeatures = mPriv->realFeatures(features);
QSet interfaces = mPriv->interfacesForFeatures(realFeatures);
PendingContacts *contacts = new PendingContacts(ContactManagerPtr(this), vcardField,
vcardAddresses, realFeatures, interfaces.toList());
return contacts;
}
/**
* Request contacts and enable their \a features using the given URI addresses.
*
* This method requires Connection::FeatureCore to be ready.
*
* \param uris The URI addresses to get contacts for.
* Supported schemes can be found in ProtocolInfo::addressableUriSchemes().
* \param features The Contact features to enable.
* \return A PendingContacts, which will emit PendingContacts::finished
* when the contacts are retrieved or an error occurred.
* \sa contactsForHandles(), contactsForIdentifiers(), contactsForVCardAddresses(),
* ProtocolInfo::normalizeContactUri()
*/
PendingContacts *ContactManager::contactsForUris(const QStringList &uris,
const Features &features)
{
if (!connection()->isValid()) {
return new PendingContacts(ContactManagerPtr(this), uris,
PendingContacts::ForUris, features, QStringList(),
TP_QT_ERROR_NOT_AVAILABLE,
QLatin1String("Connection is invalid"));
} else if (!connection()->isReady(Connection::FeatureCore)) {
return new PendingContacts(ContactManagerPtr(this), uris,
PendingContacts::ForUris, features, QStringList(),
TP_QT_ERROR_NOT_AVAILABLE,
QLatin1String("Connection::FeatureCore is not ready"));
}
Features realFeatures = mPriv->realFeatures(features);
QSet interfaces = mPriv->interfacesForFeatures(realFeatures);
PendingContacts *contacts = new PendingContacts(ContactManagerPtr(this), uris,
PendingContacts::ForUris, realFeatures, interfaces.toList());
return contacts;
}
PendingContacts *ContactManager::upgradeContacts(const QList &contacts,
const Features &features)
{
if (!connection()->isValid()) {
return new PendingContacts(ContactManagerPtr(this), contacts, features,
TP_QT_ERROR_NOT_AVAILABLE,
QLatin1String("Connection is invalid"));
} else if (!connection()->isReady(Connection::FeatureCore)) {
return new PendingContacts(ContactManagerPtr(this), contacts, features,
TP_QT_ERROR_NOT_AVAILABLE,
QLatin1String("Connection::FeatureCore is not ready"));
}
return new PendingContacts(ContactManagerPtr(this), contacts, features);
}
ContactPtr ContactManager::lookupContactByHandle(uint handle)
{
ContactPtr contact;
if (mPriv->contacts.contains(handle)) {
contact = ContactPtr(mPriv->contacts.value(handle));
if (!contact) {
// Dangling weak pointer, remove it
mPriv->contacts.remove(handle);
}
}
return contact;
}
/**
* Start a request to retrieve the avatar for the given \a contacts.
*
* Force the request of the avatar data. This method returns directly, emitting
* Contact::avatarTokenChanged() and Contact::avatarDataChanged() signals once the token
* and data are fetched from the server.
*
* This is only useful if the avatar token is unknown; see Contact::isAvatarTokenKnown().
* It happens in the case of offline XMPP contacts, because the server does not
* send the token for them and an explicit request of the avatar data is needed.
*
* This method requires Contact::FeatureAvatarData to be ready.
*
* \sa Contact::avatarData(), Contact::avatarDataChanged(),
* Contact::avatarToken(), Contact::avatarTokenChanged()
*/
void ContactManager::requestContactAvatars(const QList &contacts)
{
if (contacts.isEmpty()) {
return;
}
if (!mPriv->requestAvatarsIdle) {
mPriv->requestAvatarsIdle = true;
QTimer::singleShot(0, this, SLOT(doRequestAvatars()));
}
mPriv->requestAvatarsQueue.unite(contacts.toSet());
}
/**
* Refresh information for the given contact.
*
* Once the information is retrieved infoFieldsChanged() will be emitted.
*
* This method requires Contact::FeatureInfo to be ready.
*
* \return A PendingOperation, which will emit PendingOperation::finished
* when the call has finished.
* \sa infoFieldsChanged()
*/
PendingOperation *ContactManager::refreshContactInfo(const QList &contacts)
{
if (!mPriv->refreshInfoOp) {
mPriv->refreshInfoOp = new PendingRefreshContactInfo(connection());
QTimer::singleShot(0, this, SLOT(doRefreshInfo()));
}
foreach (const ContactPtr &contact, contacts) {
mPriv->refreshInfoOp->addContact(contact.data());
}
return mPriv->refreshInfoOp;
}
void ContactManager::onAliasesChanged(const AliasPairList &aliases)
{
debug() << "Got AliasesChanged for" << aliases.size() << "contacts";
foreach (AliasPair pair, aliases) {
ContactPtr contact = lookupContactByHandle(pair.handle);
if (contact) {
contact->receiveAlias(pair.alias);
}
}
}
void ContactManager::doRequestAvatars()
{
Q_ASSERT(mPriv->requestAvatarsIdle);
QSet contacts = mPriv->requestAvatarsQueue;
Q_ASSERT(contacts.size() > 0);
mPriv->requestAvatarsQueue.clear();
mPriv->requestAvatarsIdle = false;
int found = 0;
UIntList notFound;
foreach (const ContactPtr &contact, contacts) {
if (!contact) {
continue;
}
QString avatarFileName;
QString mimeTypeFileName;
bool success = (contact->isAvatarTokenKnown() &&
mPriv->buildAvatarFileName(contact->avatarToken(), false,
avatarFileName, mimeTypeFileName));
/* Check if the avatar is already in the cache */
if (success && QFile::exists(avatarFileName)) {
QFile mimeTypeFile(mimeTypeFileName);
mimeTypeFile.open(QIODevice::ReadOnly);
QString mimeType = QString(QLatin1String(mimeTypeFile.readAll()));
mimeTypeFile.close();
found++;
contact->receiveAvatarData(AvatarData(avatarFileName, mimeType));
continue;
}
notFound << contact->handle()[0];
}
if (found > 0) {
debug() << "Avatar(s) found in cache for" << found << "contact(s)";
}
if (found == contacts.size()) {
return;
}
debug() << "Requesting avatar(s) for" << contacts.size() - found << "contact(s)";
Client::ConnectionInterfaceAvatarsInterface *avatarsInterface =
connection()->interface();
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
avatarsInterface->RequestAvatars(notFound),
this);
connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), watcher,
SLOT(deleteLater()));
}
void ContactManager::onAvatarUpdated(uint handle, const QString &token)
{
debug() << "Got AvatarUpdate for contact with handle" << handle;
ContactPtr contact = lookupContactByHandle(handle);
if (contact) {
contact->receiveAvatarToken(token);
}
}
void ContactManager::onAvatarRetrieved(uint handle, const QString &token,
const QByteArray &data, const QString &mimeType)
{
QString avatarFileName;
QString mimeTypeFileName;
debug() << "Got AvatarRetrieved for contact with handle" << handle;
bool success = mPriv->buildAvatarFileName(token, true, avatarFileName,
mimeTypeFileName);
if (success) {
debug() << "Write avatar in cache for handle" << handle;
debug() << "Filename:" << avatarFileName;
debug() << "MimeType:" << mimeType;
if (!QFile::exists(mimeTypeFileName)) {
QTemporaryFile mimeTypeFile(mimeTypeFileName);
if (mimeTypeFile.open()) {
mimeTypeFile.write(mimeType.toLatin1());
mimeTypeFile.setAutoRemove(false);
if (!mimeTypeFile.rename(mimeTypeFileName)) {
mimeTypeFile.remove();
}
}
}
if (!QFile::exists(avatarFileName)) {
QTemporaryFile avatarFile(avatarFileName);
if (avatarFile.open()) {
avatarFile.write(data);
avatarFile.setAutoRemove(false);
if (!avatarFile.rename(avatarFileName)) {
avatarFile.remove();
}
}
}
}
ContactPtr contact = lookupContactByHandle(handle);
if (contact) {
contact->setAvatarToken(token);
contact->receiveAvatarData(AvatarData(avatarFileName, mimeType));
}
}
void ContactManager::onPresencesChanged(const SimpleContactPresences &presences)
{
debug() << "Got PresencesChanged for" << presences.size() << "contacts";
foreach (uint handle, presences.keys()) {
ContactPtr contact = lookupContactByHandle(handle);
if (contact) {
contact->receiveSimplePresence(presences[handle]);
}
}
}
void ContactManager::onCapabilitiesChanged(const ContactCapabilitiesMap &caps)
{
debug() << "Got ContactCapabilitiesChanged for" << caps.size() << "contacts";
foreach (uint handle, caps.keys()) {
ContactPtr contact = lookupContactByHandle(handle);
if (contact) {
contact->receiveCapabilities(caps[handle]);
}
}
}
void ContactManager::onLocationUpdated(uint handle, const QVariantMap &location)
{
debug() << "Got LocationUpdated for contact with handle" << handle;
ContactPtr contact = lookupContactByHandle(handle);
if (contact) {
contact->receiveLocation(location);
}
}
void ContactManager::onContactInfoChanged(uint handle, const Tp::ContactInfoFieldList &info)
{
debug() << "Got ContactInfoChanged for contact with handle" << handle;
ContactPtr contact = lookupContactByHandle(handle);
if (contact) {
contact->receiveInfo(info);
}
}
void ContactManager::onClientTypesUpdated(uint handle, const QStringList &clientTypes)
{
debug() << "Got ClientTypesUpdated for contact with handle" << handle;
ContactPtr contact = lookupContactByHandle(handle);
if (contact) {
contact->receiveClientTypes(clientTypes);
}
}
void ContactManager::doRefreshInfo()
{
PendingRefreshContactInfo *op = mPriv->refreshInfoOp;
Q_ASSERT(op);
mPriv->refreshInfoOp = nullptr;
op->refreshInfo();
}
ContactPtr ContactManager::ensureContact(const ReferencedHandles &handle,
const Features &features, const QVariantMap &attributes)
{
uint bareHandle = handle[0];
ContactPtr contact = lookupContactByHandle(bareHandle);
if (!contact) {
contact = connection()->contactFactory()->construct(this, handle, features, attributes);
mPriv->contacts.insert(bareHandle, contact);
}
contact->augment(features, attributes);
return contact;
}
ContactPtr ContactManager::ensureContact(uint bareHandle, const QString &id,
const Features &features)
{
ContactPtr contact = lookupContactByHandle(bareHandle);
if (!contact) {
QVariantMap attributes;
attributes.insert(TP_QT_IFACE_CONNECTION + QLatin1String("/contact-id"), id);
contact = connection()->contactFactory()->construct(this,
ReferencedHandles(connection(), HandleTypeContact, UIntList() << bareHandle),
features, attributes);
mPriv->contacts.insert(bareHandle, contact);
// do not call augment here as this is a fake contact
}
return contact;
}
QString ContactManager::featureToInterface(const Feature &feature)
{
if (feature == Contact::FeatureAlias) {
return TP_QT_IFACE_CONNECTION_INTERFACE_ALIASING;
} else if (feature == Contact::FeatureAvatarToken) {
return TP_QT_IFACE_CONNECTION_INTERFACE_AVATARS;
} else if (feature == Contact::FeatureAvatarData) {
return TP_QT_IFACE_CONNECTION_INTERFACE_AVATARS;
} else if (feature == Contact::FeatureSimplePresence) {
return TP_QT_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE;
} else if (feature == Contact::FeatureCapabilities) {
return TP_QT_IFACE_CONNECTION_INTERFACE_CONTACT_CAPABILITIES;
} else if (feature == Contact::FeatureLocation) {
return TP_QT_IFACE_CONNECTION_INTERFACE_LOCATION;
} else if (feature == Contact::FeatureInfo) {
return TP_QT_IFACE_CONNECTION_INTERFACE_CONTACT_INFO;
} else if (feature == Contact::FeatureRosterGroups) {
return TP_QT_IFACE_CONNECTION_INTERFACE_CONTACT_GROUPS;
} else if (feature == Contact::FeatureAddresses) {
return TP_QT_IFACE_CONNECTION_INTERFACE_ADDRESSING;
} else if (feature == Contact::FeatureClientTypes) {
return TP_QT_IFACE_CONNECTION_INTERFACE_CLIENT_TYPES;
} else {
warning() << "ContactManager doesn't know which interface corresponds to feature"
<< feature;
return QString();
}
}
void ContactManager::ensureTracking(const Feature &feature)
{
if (mPriv->tracking[feature]) {
return;
}
ConnectionPtr conn(connection());
if (feature == Contact::FeatureAlias) {
Client::ConnectionInterfaceAliasingInterface *aliasingInterface =
conn->interface();
connect(aliasingInterface,
SIGNAL(AliasesChanged(Tp::AliasPairList)),
SLOT(onAliasesChanged(Tp::AliasPairList)));
} else if (feature == Contact::FeatureAvatarData) {
Client::ConnectionInterfaceAvatarsInterface *avatarsInterface =
conn->interface();
connect(avatarsInterface,
SIGNAL(AvatarRetrieved(uint,QString,QByteArray,QString)),
SLOT(onAvatarRetrieved(uint,QString,QByteArray,QString)));
} else if (feature == Contact::FeatureAvatarToken) {
Client::ConnectionInterfaceAvatarsInterface *avatarsInterface =
conn->interface();
connect(avatarsInterface,
SIGNAL(AvatarUpdated(uint,QString)),
SLOT(onAvatarUpdated(uint,QString)));
} else if (feature == Contact::FeatureCapabilities) {
Client::ConnectionInterfaceContactCapabilitiesInterface *contactCapabilitiesInterface =
conn->interface();
connect(contactCapabilitiesInterface,
SIGNAL(ContactCapabilitiesChanged(Tp::ContactCapabilitiesMap)),
SLOT(onCapabilitiesChanged(Tp::ContactCapabilitiesMap)));
} else if (feature == Contact::FeatureInfo) {
Client::ConnectionInterfaceContactInfoInterface *contactInfoInterface =
conn->interface();
connect(contactInfoInterface,
SIGNAL(ContactInfoChanged(uint,Tp::ContactInfoFieldList)),
SLOT(onContactInfoChanged(uint,Tp::ContactInfoFieldList)));
} else if (feature == Contact::FeatureLocation) {
Client::ConnectionInterfaceLocationInterface *locationInterface =
conn->interface();
connect(locationInterface,
SIGNAL(LocationUpdated(uint,QVariantMap)),
SLOT(onLocationUpdated(uint,QVariantMap)));
} else if (feature == Contact::FeatureSimplePresence) {
Client::ConnectionInterfaceSimplePresenceInterface *simplePresenceInterface =
conn->interface();
connect(simplePresenceInterface,
SIGNAL(PresencesChanged(Tp::SimpleContactPresences)),
SLOT(onPresencesChanged(Tp::SimpleContactPresences)));
} else if (feature == Contact::FeatureClientTypes) {
Client::ConnectionInterfaceClientTypesInterface *clientTypesInterface =
conn->interface();
connect(clientTypesInterface,
SIGNAL(ClientTypesUpdated(uint,QStringList)),
SLOT(onClientTypesUpdated(uint,QStringList)));
} else if (feature == Contact::FeatureRosterGroups || feature == Contact::FeatureAddresses) {
// nothing to do here, but we don't want to warn
;
} else {
warning() << " Unknown feature" << feature
<< "when trying to figure out how to connect change notification!";
}
mPriv->tracking[feature] = true;
}
PendingOperation *ContactManager::introspectRoster()
{
return mPriv->roster->introspect();
}
PendingOperation *ContactManager::introspectRosterGroups()
{
return mPriv->roster->introspectGroups();
}
void ContactManager::resetRoster()
{
mPriv->roster->reset();
}
/**
* \fn void ContactManager::presencePublicationRequested(const Tp::Contacts &contacts)
*
* Emitted whenever some contacts request for presence publication.
*
* \param contacts A set of contacts which requested presence publication.
*/
/**
* \fn void ContactManager::groupAdded(const QString &group)
*
* Emitted when a new contact list group is created.
*
* \param group The group name.
* \sa allKnownGroups()
*/
/**
* \fn void ContactManager::groupRenamed(const QString &oldGroup, const QString &newGroup)
*
* Emitted when a new contact list group is renamed.
*
* \param oldGroup The old group name.
* \param newGroup The new group name.
* \sa allKnownGroups()
*/
/**
* \fn void ContactManager::groupRemoved(const QString &group)
*
* Emitted when a contact list group is removed.
*
* \param group The group name.
* \sa allKnownGroups()
*/
/**
* \fn void ContactManager::groupMembersChanged(const QString &group,
* const Tp::Contacts &groupMembersAdded,
* const Tp::Contacts &groupMembersRemoved,
* const Tp::Channel::GroupMemberChangeDetails &details)
*
* Emitted whenever some contacts got removed or added from
* a group.
*
* \param group The name of the group that changed.
* \param groupMembersAdded A set of contacts which were added to the group \a group.
* \param groupMembersRemoved A set of contacts which were removed from the group \a group.
* \param details The change details.
* \sa groupContacts()
*/
/**
* \fn void ContactManager::allKnownContactsChanged(const Tp::Contacts &contactsAdded,
* const Tp::Contacts &contactsRemoved,
* const Tp::Channel::GroupMemberChangeDetails &details)
*
* Emitted whenever some contacts got removed or added from
* ContactManager's known contact list. It is useful for monitoring which contacts
* are currently known by ContactManager.
*
* Note that, in some protocols, this signal could stream newly added contacts
* with both presence subscription and publication state set to No. Be sure to watch
* over publication and/or subscription state changes if that is the case.
*
* \param contactsAdded A set of contacts which were added to the known contact list.
* \param contactsRemoved A set of contacts which were removed from the known contact list.
* \param details The change details.
* \sa allKnownContacts()
*/
} // Tp