summaryrefslogtreecommitdiff
path: root/TelepathyQt4
diff options
context:
space:
mode:
authorAndre Moreira Magalhaes (andrunko) <andre.magalhaes@collabora.co.uk>2010-12-31 11:22:05 -0200
committerAndre Moreira Magalhaes (andrunko) <andre.magalhaes@collabora.co.uk>2010-12-31 11:22:10 -0200
commit994a3c140969fab694288e9252dc31bcf09f73f9 (patch)
treeac3ed01b704152d6dcb6e5ab0579678d2ef7efe0 /TelepathyQt4
parent5e2e943a37f4ae525ad7a69e73d47a1fb87f7148 (diff)
parenta398ff89229a9599e0d21f5976cb00f2a459eca2 (diff)
Merge branch 'contactlist'
Reviewed-by: Olli Salli (oggis) <olli.salli@collabora.co.uk>
Diffstat (limited to 'TelepathyQt4')
-rw-r--r--TelepathyQt4/channel.h2
-rw-r--r--TelepathyQt4/connection.cpp268
-rw-r--r--TelepathyQt4/connection.h11
-rw-r--r--TelepathyQt4/contact-manager.cpp1112
-rw-r--r--TelepathyQt4/contact-manager.h42
-rw-r--r--TelepathyQt4/contact.cpp161
-rw-r--r--TelepathyQt4/contact.h30
-rw-r--r--TelepathyQt4/referenced-handles.h1
8 files changed, 1359 insertions, 268 deletions
diff --git a/TelepathyQt4/channel.h b/TelepathyQt4/channel.h
index dce22c4e..eb10e581 100644
--- a/TelepathyQt4/channel.h
+++ b/TelepathyQt4/channel.h
@@ -131,6 +131,8 @@ public:
private:
friend class Channel;
+ friend class Contact;
+ friend class ContactManager;
GroupMemberChangeDetails(const ContactPtr &actor, const QVariantMap &details);
diff --git a/TelepathyQt4/connection.cpp b/TelepathyQt4/connection.cpp
index 24a15da3..b7fde4ba 100644
--- a/TelepathyQt4/connection.cpp
+++ b/TelepathyQt4/connection.cpp
@@ -1,8 +1,8 @@
/*
* This file is part of TelepathyQt4
*
- * Copyright (C) 2008, 2009 Collabora Ltd. <http://www.collabora.co.uk/>
- * Copyright (C) 2008, 2009 Nokia Corporation
+ * Copyright (C) 2008-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2008-2010 Nokia Corporation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -41,6 +41,7 @@
#include <TelepathyQt4/PendingFailure>
#include <TelepathyQt4/PendingHandles>
#include <TelepathyQt4/PendingReady>
+#include <TelepathyQt4/PendingVariantMap>
#include <TelepathyQt4/PendingVoid>
#include <TelepathyQt4/ReferencedHandles>
@@ -76,6 +77,8 @@ struct TELEPATHY_QT4_NO_EXPORT Connection::Private
static void introspectSelfContact(Private *self);
static void introspectSimplePresence(Private *self);
static void introspectRoster(Private *self);
+ void introspectContactList();
+ void introspectContactListContacts();
static void introspectRosterGroups(Private *self);
static void introspectBalance(Private *self);
@@ -139,6 +142,10 @@ struct TELEPATHY_QT4_NO_EXPORT Connection::Private
SimpleStatusSpecMap simplePresenceStatuses;
// FeatureRoster
+ // new roster API
+ uint contactListState;
+
+ // old roster API
QMap<uint, ContactManager::ContactListChannel> contactListChannels;
uint contactListChannelsReady;
@@ -217,6 +224,7 @@ Connection::Private::Private(Connection *parent,
statusReason(ConnectionStatusReasonNoneSpecified),
selfHandle(0),
contactManager(ContactManagerPtr(new ContactManager(parent))),
+ contactListState((uint) -1),
contactListChannelsReady(0),
featureRosterGroupsTodo(0),
handleContext(0)
@@ -492,47 +500,130 @@ void Connection::Private::introspectSimplePresence(Connection::Private *self)
void Connection::Private::introspectRoster(Connection::Private *self)
{
- debug() << "Requesting handles for contact lists";
+ if (self->parent->hasInterface(TP_QT4_IFACE_CONNECTION_INTERFACE_CONTACT_LIST)) {
+ self->contactManager->setUseFallbackContactList(false);
+
+ debug() << "Introspecting deny channel";
- for (uint i = 0; i < ContactManager::ContactListChannel::LastType; ++i) {
- self->contactListChannels.insert(i,
- ContactManager::ContactListChannel(
- (ContactManager::ContactListChannel::Type) i));
+ self->contactListChannels.insert(ContactManager::ContactListChannel::TypeDeny,
+ ContactManager::ContactListChannel(ContactManager::ContactListChannel::TypeDeny));
PendingHandles *pending = self->parent->lowlevel()->requestHandles(
HandleTypeList,
QStringList() << ContactManager::ContactListChannel::identifierForType(
- (ContactManager::ContactListChannel::Type) i));
+ ContactManager::ContactListChannel::TypeDeny));
self->parent->connect(pending,
SIGNAL(finished(Tp::PendingOperation*)),
SLOT(gotContactListsHandles(Tp::PendingOperation*)));
+ } else {
+ debug() << "Requesting handles for contact lists channels";
+
+ self->contactManager->setUseFallbackContactList(true);
+
+ for (uint i = 0; i < ContactManager::ContactListChannel::LastType; ++i) {
+ self->contactListChannels.insert(i,
+ ContactManager::ContactListChannel(
+ (ContactManager::ContactListChannel::Type) i));
+
+ PendingHandles *pending = self->parent->lowlevel()->requestHandles(
+ HandleTypeList,
+ QStringList() << ContactManager::ContactListChannel::identifierForType(
+ (ContactManager::ContactListChannel::Type) i));
+ self->parent->connect(pending,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(gotContactListsHandles(Tp::PendingOperation*)));
+ }
}
}
-void Connection::Private::introspectRosterGroups(Connection::Private *self)
+void Connection::Private::introspectContactList()
{
- debug() << "Introspecting roster groups";
+ debug() << "Requesting contact list properties";
- ++self->featureRosterGroupsTodo; // decremented in gotChannels
+ Client::ConnectionInterfaceContactListInterface *iface =
+ parent->interface<Client::ConnectionInterfaceContactListInterface>();
- // we already checked if requests interface exists, so bypass requests
- // interface checking
- Client::ConnectionInterfaceRequestsInterface *iface =
- self->parent->interface<Client::ConnectionInterfaceRequestsInterface>();
+ parent->connect(iface,
+ SIGNAL(ContactListStateChanged(uint)),
+ SLOT(onContactListStateChanged(uint)));
+ parent->connect(iface,
+ SIGNAL(ContactsChanged(Tp::ContactSubscriptionMap,Tp::UIntList)),
+ SLOT(onContactListContactsChanged(Tp::ContactSubscriptionMap,Tp::UIntList)));
- debug() << "Connecting to Requests.NewChannels";
- self->parent->connect(iface,
- SIGNAL(NewChannels(Tp::ChannelDetailsList)),
- SLOT(onNewChannels(Tp::ChannelDetailsList)));
+ PendingVariantMap *pvm = iface->requestAllProperties();
+ parent->connect(pvm,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(gotContactListProperties(Tp::PendingOperation*)));
+}
- debug() << "Retrieving channels";
+void Connection::Private::introspectContactListContacts()
+{
+ Client::ConnectionInterfaceContactListInterface *iface =
+ parent->interface<Client::ConnectionInterfaceContactListInterface>();
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
- self->properties->Get(
- QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_REQUESTS),
- QLatin1String("Channels")), self->parent);
- self->parent->connect(watcher,
+ iface->GetContactListAttributes(
+ QStringList() << QLatin1String(TP_QT4_IFACE_CONNECTION_INTERFACE_CONTACT_LIST),
+ true), parent);
+ parent->connect(watcher,
SIGNAL(finished(QDBusPendingCallWatcher*)),
- SLOT(gotChannels(QDBusPendingCallWatcher*)));
+ SLOT(gotContactListContacts(QDBusPendingCallWatcher*)));
+}
+
+void Connection::Private::introspectRosterGroups(Connection::Private *self)
+{
+ if (self->parent->hasInterface(TP_QT4_IFACE_CONNECTION_INTERFACE_CONTACT_LIST)) {
+ if (!self->parent->hasInterface(TP_QT4_IFACE_CONNECTION_INTERFACE_CONTACT_GROUPS)) {
+ self->readinessHelper->setIntrospectCompleted(FeatureRosterGroups, false,
+ TP_QT4_ERROR_NOT_IMPLEMENTED, QLatin1String("Roster groups not supported"));
+ return;
+ }
+
+ debug() << "Introspecting contact list groups";
+
+ Client::ConnectionInterfaceContactGroupsInterface *iface =
+ self->parent->interface<Client::ConnectionInterfaceContactGroupsInterface>();
+
+ self->contactManager->connect(iface,
+ SIGNAL(GroupsChanged(Tp::UIntList,QStringList,QStringList)),
+ SLOT(onContactListGroupsChanged(Tp::UIntList,QStringList,QStringList)));
+ self->contactManager->connect(iface,
+ SIGNAL(GroupsCreated(QStringList)),
+ SLOT(onContactListGroupsCreated(QStringList)));
+ self->contactManager->connect(iface,
+ SIGNAL(GroupRenamed(QString,QString)),
+ SLOT(onContactListGroupRenamed(QString,QString)));
+ self->contactManager->connect(iface,
+ SIGNAL(GroupsRemoved(QStringList)),
+ SLOT(onContactListGroupsRemoved(QStringList)));
+
+ PendingVariantMap *pvm = iface->requestAllProperties();
+ self->parent->connect(pvm,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(gotContactListGroupsProperties(Tp::PendingOperation*)));
+ } else {
+ debug() << "Introspecting contact list group channels";
+
+ ++self->featureRosterGroupsTodo; // decremented in gotChannels
+
+ // we already checked if requests interface exists, so bypass requests
+ // interface checking
+ Client::ConnectionInterfaceRequestsInterface *iface =
+ self->parent->interface<Client::ConnectionInterfaceRequestsInterface>();
+
+ debug() << "Connecting to Requests.NewChannels";
+ self->parent->connect(iface,
+ SIGNAL(NewChannels(Tp::ChannelDetailsList)),
+ SLOT(onNewChannels(Tp::ChannelDetailsList)));
+
+ debug() << "Retrieving channels";
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
+ self->properties->Get(
+ QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_REQUESTS),
+ QLatin1String("Channels")), self->parent);
+ self->parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotChannels(QDBusPendingCallWatcher*)));
+ }
}
void Connection::Private::introspectBalance(Connection::Private *self)
@@ -622,10 +713,8 @@ void Connection::Private::checkFeatureRosterGroupsReady()
}
debug() << "FeatureRosterGroups ready";
- contactManager->setContactListGroupChannels(
- contactListGroupChannels);
- readinessHelper->setIntrospectCompleted(
- FeatureRosterGroups, true);
+ contactManager->setContactListGroupChannelsFallback(contactListGroupChannels);
+ readinessHelper->setIntrospectCompleted(FeatureRosterGroups, true);
contactListGroupChannels.clear();
}
@@ -1643,6 +1732,110 @@ void Connection::gotSelfContact(PendingOperation *op)
}
}
+void Connection::gotContactListProperties(PendingOperation *op)
+{
+ if (op->isError()) {
+ // We may have been in state Failure and then Success, and we are already ready
+ if (!isReady(FeatureRoster)) {
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureRoster, false,
+ op->errorName(), op->errorMessage());
+ }
+ return;
+ }
+
+ debug() << "Got contact list properties";
+ PendingVariantMap *pvm = qobject_cast<PendingVariantMap*>(op);
+
+ QVariantMap props = pvm->result();
+
+ // only update the status if we did not get it from ContactListStateChanged
+ if (mPriv->contactListState == (uint) -1) {
+ uint state = qdbus_cast<uint>(props[QLatin1String("ContactListState")]);
+ onContactListStateChanged(state);
+ }
+
+ mPriv->contactManager->setContactListProperties(props);
+}
+
+void Connection::gotContactListContacts(QDBusPendingCallWatcher *watcher)
+{
+ QDBusPendingReply<ContactAttributesMap> reply = *watcher;
+
+ if (watcher->isError()) {
+ // We may have been in state Failure and then Success, and we are already ready
+ if (!isReady(FeatureRoster)) {
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureRoster, false,
+ reply.error());
+ }
+ return;
+ }
+
+ ContactAttributesMap attrs = reply.value();
+ mPriv->contactManager->setContactListContacts(attrs);
+
+ // We may have been in state Failure and then Success, and we are already ready
+ if (!isReady(FeatureRoster)) {
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureRoster, true);
+ }
+}
+
+void Connection::gotContactListGroupsProperties(PendingOperation *op)
+{
+ if (op->isError()) {
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureRosterGroups, false,
+ op->errorName(), op->errorMessage());
+ return;
+ }
+
+ debug() << "Got contact list groups properties";
+ PendingVariantMap *pvm = qobject_cast<PendingVariantMap*>(op);
+
+ QVariantMap props = pvm->result();
+ mPriv->contactManager->setContactListGroupsProperties(props);
+
+ PendingContacts *pc = mPriv->contactManager->upgradeContacts(
+ mPriv->contactManager->allKnownContacts().toList(),
+ Contact::FeatureRosterGroups);
+ connect(pc,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onContactListContactsUpgraded(Tp::PendingOperation*)));
+}
+
+void Connection::onContactListContactsUpgraded(PendingOperation *op)
+{
+ if (op->isError()) {
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureRosterGroups, false,
+ op->errorName(), op->errorMessage());
+ return;
+ }
+
+ debug() << "Contact list groups ready";
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureRosterGroups, true);
+}
+
+void Connection::onContactListStateChanged(uint state)
+{
+ if (mPriv->contactListState == state) {
+ // ignore redundant state changes
+ return;
+ }
+
+ mPriv->contactListState = state;
+
+ if (state == ContactListStateSuccess) {
+ mPriv->introspectContactListContacts();
+ } else if (state == ContactListStateFailure) {
+ // Consider it ready here as the state may go from Failure to Success afterwards, in which
+ // case the contacts will appear in ContactManager.
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureRoster, true);
+ }
+}
+
+void Connection::onContactListContactsChanged(const Tp::ContactSubscriptionMap &changes,
+ const Tp::UIntList &removals)
+{
+ mPriv->contactManager->updateContactListContacts(changes, removals);
+}
void Connection::gotContactListsHandles(PendingOperation *op)
{
@@ -1675,7 +1868,11 @@ void Connection::gotContactListsHandles(PendingOperation *op)
ReferencedHandles handle = pending->handles();
uint type = ContactManager::ContactListChannel::typeForIdentifier(
pending->namesRequested().first());
- Q_ASSERT(type != (uint) -1 && type < ContactManager::ContactListChannel::LastType);
+ if (hasInterface(TP_QT4_IFACE_CONNECTION_INTERFACE_CONTACT_LIST)) {
+ Q_ASSERT(type == ContactManager::ContactListChannel::TypeDeny);
+ } else {
+ Q_ASSERT(type != (uint) -1 && type < ContactManager::ContactListChannel::LastType);
+ }
mPriv->contactListChannels[type].handle = handle;
request[QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandle")] = handle[0];
connect(lowlevel()->ensureChannel(request),
@@ -1696,7 +1893,8 @@ void Connection::gotContactListChannel(PendingOperation *op)
Q_ASSERT(channel);
Q_ASSERT(handle);
for (int i = 0; i < ContactManager::ContactListChannel::LastType; ++i) {
- if (mPriv->contactListChannels[i].handle.size() > 0 &&
+ if (mPriv->contactListChannels.contains(i) &&
+ mPriv->contactListChannels[i].handle.size() > 0 &&
mPriv->contactListChannels[i].handle[0] == handle) {
Q_ASSERT(!mPriv->contactListChannels[i].channel);
mPriv->contactListChannels[i].channel = channel;
@@ -1709,8 +1907,10 @@ void Connection::gotContactListChannel(PendingOperation *op)
void Connection::contactListChannelReady()
{
- if (++mPriv->contactListChannelsReady ==
- ContactManager::ContactListChannel::LastType) {
+ if (hasInterface(TP_QT4_IFACE_CONNECTION_INTERFACE_CONTACT_LIST)) {
+ mPriv->contactManager->setContactListChannels(mPriv->contactListChannels);
+ mPriv->introspectContactList();
+ } else if (++mPriv->contactListChannelsReady == ContactManager::ContactListChannel::LastType) {
debug() << "FeatureRoster ready";
mPriv->contactManager->setContactListChannels(mPriv->contactListChannels);
mPriv->readinessHelper->setIntrospectCompleted(FeatureRoster, true);
@@ -1753,7 +1953,7 @@ void Connection::onContactListGroupChannelReady(Tp::PendingOperation *op)
} else {
PendingReady *pr = qobject_cast<PendingReady*>(op);
ChannelPtr channel = ChannelPtr::qObjectCast(pr->proxy());
- mPriv->contactManager->addContactListGroupChannel(channel);
+ mPriv->contactManager->addContactListGroupChannelFallback(channel);
mPriv->contactListGroupChannels.removeOne(channel);
}
}
diff --git a/TelepathyQt4/connection.h b/TelepathyQt4/connection.h
index 531d3c2f..075feb52 100644
--- a/TelepathyQt4/connection.h
+++ b/TelepathyQt4/connection.h
@@ -1,8 +1,8 @@
/*
* This file is part of TelepathyQt4
*
- * Copyright (C) 2008, 2009 Collabora Ltd. <http://www.collabora.co.uk/>
- * Copyright (C) 2008, 2009 Nokia Corporation
+ * Copyright (C) 2008-2010 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2008-2010 Nokia Corporation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -196,6 +196,13 @@ private Q_SLOTS:
void gotContactAttributeInterfaces(QDBusPendingCallWatcher *watcher);
void gotSimpleStatuses(QDBusPendingCallWatcher *watcher);
void gotSelfContact(Tp::PendingOperation *op);
+ void gotContactListProperties(Tp::PendingOperation *op);
+ void gotContactListContacts(QDBusPendingCallWatcher *watcher);
+ void gotContactListGroupsProperties(Tp::PendingOperation *op);
+ void onContactListContactsUpgraded(Tp::PendingOperation *op);
+ void onContactListStateChanged(uint state);
+ void onContactListContactsChanged(const Tp::ContactSubscriptionMap &changes,
+ const Tp::UIntList &removals);
void gotContactListsHandles(Tp::PendingOperation *op);
void gotContactListChannel(Tp::PendingOperation *op);
void contactListChannelReady();
diff --git a/TelepathyQt4/contact-manager.cpp b/TelepathyQt4/contact-manager.cpp
index 21e47cdf..d49d9415 100644
--- a/TelepathyQt4/contact-manager.cpp
+++ b/TelepathyQt4/contact-manager.cpp
@@ -59,19 +59,47 @@ struct TELEPATHY_QT4_NO_EXPORT ContactManager::Private
void ensureTracking(const Feature &feature);
// roster specific methods
- Contacts allKnownContacts() const;
- void computeKnownContactsChanges(const Contacts &added,
+ void processContactListChanges();
+ void processContactListUpdates();
+ void processContactListGroupsUpdates();
+ void processContactListGroupsCreated();
+ void processContactListGroupRenamed();
+ void processContactListGroupsRemoved();
+
+ Contacts allKnownContactsFallback() const;
+ void computeKnownContactsChangesFallback(const Contacts &added,
const Contacts &pendingAdded, const Contacts &remotePendingAdded,
const Contacts &removed, const Channel::GroupMemberChangeDetails &details);
- void updateContactsPresenceState();
+ void updateContactsBlockState();
+ void updateContactsPresenceStateFallback();
+ PendingOperation *requestPresenceSubscriptionFallback(
+ const QList<ContactPtr> &contacts, const QString &message);
+ PendingOperation *removePresenceSubscriptionFallback(
+ const QList<ContactPtr> &contacts, const QString &message);
+ PendingOperation *authorizePresencePublicationFallback(
+ const QList<ContactPtr> &contacts, const QString &message);
+ PendingOperation *removePresencePublicationFallback(
+ const QList<ContactPtr> &contacts, const QString &message);
+ PendingOperation *removeContactsFallback(
+ const QList<ContactPtr> &contacts, const QString &message);
// roster group specific methods
- QString addContactListGroupChannel(const ChannelPtr &contactListGroupChannel);
+ QString addContactListGroupChannelFallback(const ChannelPtr &contactListGroupChannel);
+ PendingOperation *addGroupFallback(const QString &group);
+ PendingOperation *removeGroupFallback(const QString &group);
+ PendingOperation *addContactsToGroupFallback(const QString &group,
+ const QList<ContactPtr> &contacts);
+ PendingOperation *removeContactsFromGroupFallback(const QString &group,
+ const QList<ContactPtr> &contacts);
// avatar specific methods
bool buildAvatarFileName(QString token, bool createDir,
QString &avatarFileName, QString &mimeTypeFileName);
+ struct ContactListUpdateInfo;
+ struct ContactListGroupsUpdateInfo;
+ struct ContactListGroupRenamedInfo;
+
ContactManager *parent;
QWeakPointer<Connection> connection;
@@ -81,7 +109,23 @@ struct TELEPATHY_QT4_NO_EXPORT ContactManager::Private
Features supportedFeatures;
// roster
+ bool fallbackContactList;
Contacts cachedAllKnownContacts;
+
+ // new roster API
+ bool canChangeContactList;
+ bool contactListRequestUsesMessage;
+ QSet<QString> allKnownGroups;
+ bool contactListGroupPropertiesReceived;
+ QQueue<void (Private::*)()> contactListChangesQueue;
+ QQueue<ContactListUpdateInfo> contactListUpdatesQueue;
+ QQueue<ContactListGroupsUpdateInfo> contactListGroupsUpdatesQueue;
+ QQueue<QStringList> contactListGroupsCreatedQueue;
+ QQueue<ContactListGroupRenamedInfo> contactListGroupRenamedQueue;
+ QQueue<QStringList> contactListGroupsRemovedQueue;
+ bool processingContactListChanges;
+
+ // old roster API
QMap<uint, ContactListChannel> contactListChannels;
ChannelPtr subscribeChannel;
ChannelPtr publishChannel;
@@ -94,9 +138,53 @@ struct TELEPATHY_QT4_NO_EXPORT ContactManager::Private
bool requestAvatarsIdle;
};
+struct ContactManager::Private::ContactListUpdateInfo
+{
+ ContactListUpdateInfo(const ContactSubscriptionMap &changes, const UIntList &removals)
+ : changes(changes),
+ removals(removals)
+ {
+ }
+
+ ContactSubscriptionMap changes;
+ UIntList removals;
+};
+
+struct ContactManager::Private::ContactListGroupsUpdateInfo
+{
+ ContactListGroupsUpdateInfo(const UIntList &contacts,
+ const QStringList &groupsAdded, const QStringList &groupsRemoved)
+ : contacts(contacts),
+ groupsAdded(groupsAdded),
+ groupsRemoved(groupsRemoved)
+ {
+ }
+
+ UIntList contacts;
+ QStringList groupsAdded;
+ QStringList groupsRemoved;
+};
+
+struct ContactManager::Private::ContactListGroupRenamedInfo
+{
+ ContactListGroupRenamedInfo(const QString &oldName, const QString &newName)
+ : oldName(oldName),
+ newName(newName)
+ {
+ }
+
+ QString oldName;
+ QString newName;
+};
+
ContactManager::Private::Private(ContactManager *parent, Connection *connection)
: parent(parent),
connection(connection),
+ fallbackContactList(false),
+ canChangeContactList(false),
+ contactListRequestUsesMessage(false),
+ contactListGroupPropertiesReceived(false),
+ processingContactListChanges(false),
requestAvatarsIdle(false)
{
}
@@ -165,6 +253,9 @@ void ContactManager::Private::ensureTracking(const Feature &feature)
simplePresenceInterface,
SIGNAL(PresencesChanged(Tp::SimpleContactPresences)),
SLOT(onPresencesChanged(Tp::SimpleContactPresences)));
+ } else if (feature == Contact::FeatureRosterGroups) {
+ // 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!";
@@ -173,7 +264,118 @@ void ContactManager::Private::ensureTracking(const Feature &feature)
tracking[feature] = true;
}
-Contacts ContactManager::Private::allKnownContacts() const
+
+void ContactManager::Private::processContactListChanges()
+{
+ if (processingContactListChanges || contactListChangesQueue.isEmpty()) {
+ return;
+ }
+
+ processingContactListChanges = true;
+ (this->*(contactListChangesQueue.dequeue()))();
+}
+
+void ContactManager::Private::processContactListUpdates()
+{
+ ContactListUpdateInfo info = contactListUpdatesQueue.head();
+
+ // construct Contact objects for all contacts in added to the contact list
+ UIntList contacts;
+ ContactSubscriptionMap::const_iterator begin = info.changes.constBegin();
+ ContactSubscriptionMap::const_iterator end = info.changes.constEnd();
+ for (ContactSubscriptionMap::const_iterator i = begin; i != end; ++i) {
+ uint bareHandle = i.key();
+ contacts << bareHandle;
+ }
+
+ Features features;
+ if (parent->connection()->isReady(Connection::FeatureRosterGroups)) {
+ features << Contact::FeatureRosterGroups;
+ }
+ PendingContacts *pc = parent->contactsForHandles(contacts, features);
+ parent->connect(pc,
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(onContactListNewContactsConstructed(Tp::PendingOperation*)));
+}
+
+void ContactManager::Private::processContactListGroupsUpdates()
+{
+ ContactListGroupsUpdateInfo info = contactListGroupsUpdatesQueue.dequeue();
+
+ foreach (const QString &group, info.groupsAdded) {
+ Contacts contacts;
+ foreach (uint bareHandle, info.contacts) {
+ ContactPtr contact = parent->lookupContactByHandle(bareHandle);
+ if (!contact) {
+ warning() << "contact with handle" << bareHandle << "was added to a group but "
+ "never added to the contact list, ignoring";
+ continue;
+ }
+ contacts << contact;
+ contact->setAddedToGroup(group);
+ }
+
+ emit parent->groupMembersChanged(group, contacts,
+ Contacts(), Channel::GroupMemberChangeDetails());
+ }
+
+ foreach (const QString &group, info.groupsRemoved) {
+ Contacts contacts;
+ foreach (uint bareHandle, info.contacts) {
+ ContactPtr contact = parent->lookupContactByHandle(bareHandle);
+ if (!contact) {
+ warning() << "contact with handle" << bareHandle << "was removed from a group but "
+ "never added to the contact list, ignoring";
+ continue;
+ }
+ contacts << contact;
+ contact->setRemovedFromGroup(group);
+ }
+
+ emit parent->groupMembersChanged(group, Contacts(),
+ contacts, Channel::GroupMemberChangeDetails());
+ }
+
+ processingContactListChanges = false;
+ processContactListChanges();
+}
+
+void ContactManager::Private::processContactListGroupsCreated()
+{
+ QStringList names = contactListGroupsCreatedQueue.dequeue();
+ foreach (const QString &name, names) {
+ allKnownGroups.insert(name);
+ emit parent->groupAdded(name);
+ }
+
+ processingContactListChanges = false;
+ processContactListChanges();
+}
+
+void ContactManager::Private::processContactListGroupRenamed()
+{
+ Private::ContactListGroupRenamedInfo info = contactListGroupRenamedQueue.dequeue();
+ allKnownGroups.remove(info.oldName);
+ allKnownGroups.insert(info.newName);
+ emit parent->groupRenamed(info.oldName, info.newName);
+
+ processingContactListChanges = false;
+ processContactListChanges();
+}
+
+void ContactManager::Private::processContactListGroupsRemoved()
+{
+ QStringList names = contactListGroupsRemovedQueue.dequeue();
+ foreach (const QString &name, names) {
+ allKnownGroups.remove(name);
+ emit parent->groupRemoved(name);
+ }
+
+ processingContactListChanges = false;
+ processContactListChanges();
+}
+
+Contacts ContactManager::Private::allKnownContactsFallback() const
{
Contacts contacts;
foreach (const ContactListChannel &contactListChannel, contactListChannels) {
@@ -188,7 +390,7 @@ Contacts ContactManager::Private::allKnownContacts() const
return contacts;
}
-void ContactManager::Private::computeKnownContactsChanges(const Tp::Contacts& added,
+void ContactManager::Private::computeKnownContactsChangesFallback(const Tp::Contacts& added,
const Tp::Contacts& pendingAdded, const Tp::Contacts& remotePendingAdded,
const Tp::Contacts& removed, const Channel::GroupMemberChangeDetails &details)
{
@@ -221,8 +423,28 @@ void ContactManager::Private::computeKnownContactsChanges(const Tp::Contacts& ad
}
}
-void ContactManager::Private::updateContactsPresenceState()
+void ContactManager::Private::updateContactsBlockState()
+{
+ if (!denyChannel) {
+ return;
+ }
+
+ Contacts denyContacts;
+ if (denyChannel) {
+ denyContacts = denyChannel->groupContacts();
+ }
+
+ foreach (ContactPtr contact, denyContacts) {
+ contact->setBlocked(true);
+ }
+}
+
+void ContactManager::Private::updateContactsPresenceStateFallback()
{
+ if (!subscribeChannel && !publishChannel) {
+ return;
+ }
+
Contacts subscribeContacts;
Contacts subscribeContactsRP;
@@ -238,48 +460,117 @@ void ContactManager::Private::updateContactsPresenceState()
publishContactsLP = publishChannel->groupLocalPendingContacts();
}
- Contacts denyContacts;
- if (denyChannel) {
- denyContacts = denyChannel->groupContacts();
- }
-
- if (!subscribeChannel && !publishChannel && !denyChannel) {
- return;
- }
-
- Contacts contacts = allKnownContacts();
+ Contacts contacts = allKnownContactsFallback();
foreach (ContactPtr contact, contacts) {
if (subscribeChannel) {
// not in "subscribe" -> No, in "subscribe" lp -> Ask, in "subscribe" current -> Yes
if (subscribeContacts.contains(contact)) {
- contact->setSubscriptionState(Contact::PresenceStateYes);
+ contact->setSubscriptionState(SubscriptionStateYes);
} else if (subscribeContactsRP.contains(contact)) {
- contact->setSubscriptionState(Contact::PresenceStateAsk);
+ contact->setSubscriptionState(SubscriptionStateAsk);
} else {
- contact->setSubscriptionState(Contact::PresenceStateNo);
+ contact->setSubscriptionState(SubscriptionStateNo);
}
}
if (publishChannel) {
// not in "publish" -> No, in "subscribe" rp -> Ask, in "publish" current -> Yes
if (publishContacts.contains(contact)) {
- contact->setPublishState(Contact::PresenceStateYes);
+ contact->setPublishState(SubscriptionStateYes);
} else if (publishContactsLP.contains(contact)) {
- contact->setPublishState(Contact::PresenceStateAsk);
+ contact->setPublishState(SubscriptionStateAsk,
+ publishChannel->groupLocalPendingContactChangeInfo(contact).message());
} else {
- contact->setPublishState(Contact::PresenceStateNo);
+ contact->setPublishState(SubscriptionStateNo);
}
}
+ }
+}
- if (denyChannel) {
- if (denyContacts.contains(contact)) {
- contact->setBlocked(true);
- }
- }
+PendingOperation *ContactManager::Private::requestPresenceSubscriptionFallback(
+ const QList<ContactPtr> &contacts, const QString &message)
+{
+ if (!subscribeChannel) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("Cannot subscribe to contacts' presence on this protocol"),
+ parent->connection());
+ }
+
+ return subscribeChannel->groupAddContacts(contacts, message);
+}
+
+PendingOperation *ContactManager::Private::removePresenceSubscriptionFallback(
+ const QList<ContactPtr> &contacts, const QString &message)
+{
+ if (!subscribeChannel) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("Cannot subscribe to contacts' presence on this protocol"),
+ parent->connection());
+ }
+
+ return subscribeChannel->groupRemoveContacts(contacts, message);
+}
+
+PendingOperation *ContactManager::Private::authorizePresencePublicationFallback(
+ const QList<ContactPtr> &contacts, const QString &message)
+{
+ if (!publishChannel) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("Cannot control publication of presence on this protocol"),
+ parent->connection());
}
+
+ return publishChannel->groupAddContacts(contacts, message);
}
-QString ContactManager::Private::addContactListGroupChannel(
+PendingOperation *ContactManager::Private::removePresencePublicationFallback(
+ const QList<ContactPtr> &contacts, const QString &message)
+{
+ if (!publishChannel) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("Cannot control publication of presence on this protocol"),
+ parent->connection());
+ }
+
+ return publishChannel->groupRemoveContacts(contacts, message);
+}
+
+PendingOperation *ContactManager::Private::removeContactsFallback(
+ const QList<ContactPtr> &contacts, const QString &message)
+{
+ /* If the CM implements stored channel correctly, it should have the
+ * wanted behaviour. Otherwise we have to fallback to remove from publish
+ * and subscribe channels.
+ */
+
+ if (storedChannel &&
+ storedChannel->groupCanRemoveContacts()) {
+ debug() << "Removing contacts from stored list";
+ return storedChannel->groupRemoveContacts(contacts, message);
+ }
+
+ QList<PendingOperation*> operations;
+
+ if (parent->canRemovePresenceSubscription()) {
+ debug() << "Removing contacts from subscribe list";
+ operations << parent->removePresenceSubscription(contacts, message);
+ }
+
+ if (parent->canRemovePresencePublication()) {
+ debug() << "Removing contacts from publish list";
+ operations << parent->removePresencePublication(contacts, message);
+ }
+
+ if (operations.isEmpty()) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("Cannot remove contacts on this protocol"),
+ parent->connection());
+ }
+
+ return new PendingComposite(operations, parent->connection());
+}
+
+QString ContactManager::Private::addContactListGroupChannelFallback(
const ChannelPtr &contactListGroupChannel)
{
QString id = contactListGroupChannel->immutableProperties().value(
@@ -292,7 +583,7 @@ QString ContactManager::Private::addContactListGroupChannel(
Tp::Contacts,
Tp::Contacts,
Tp::Channel::GroupMemberChangeDetails)),
- SLOT(onContactListGroupMembersChanged(
+ SLOT(onContactListGroupMembersChangedFallback(
Tp::Contacts,
Tp::Contacts,
Tp::Contacts,
@@ -300,7 +591,7 @@ QString ContactManager::Private::addContactListGroupChannel(
Tp::Channel::GroupMemberChangeDetails)));
parent->connect(contactListGroupChannel.data(),
SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)),
- SLOT(onContactListGroupRemoved(Tp::DBusProxy*,QString,QString)));
+ SLOT(onContactListGroupRemovedFallback(Tp::DBusProxy*,QString,QString)));
foreach (const ContactPtr &contact, contactListGroupChannel->groupContacts()) {
contact->setAddedToGroup(id);
@@ -308,6 +599,58 @@ QString ContactManager::Private::addContactListGroupChannel(
return id;
}
+PendingOperation *ContactManager::Private::addGroupFallback(const QString &group)
+{
+ QVariantMap request;
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType"),
+ QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_CONTACT_LIST));
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType"),
+ (uint) Tp::HandleTypeGroup);
+ request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetID"),
+ group);
+ return parent->connection()->lowlevel()->ensureChannel(request);
+}
+
+PendingOperation *ContactManager::Private::removeGroupFallback(const QString &group)
+{
+ if (!contactListGroupChannels.contains(group)) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT),
+ QLatin1String("Invalid group"),
+ parent->connection());
+ }
+
+ ChannelPtr channel = contactListGroupChannels[group];
+ PendingContactManagerRemoveContactListGroup *op =
+ new PendingContactManagerRemoveContactListGroup(channel);
+ return op;
+}
+
+PendingOperation *ContactManager::Private::addContactsToGroupFallback(const QString &group,
+ const QList<ContactPtr> &contacts)
+{
+ if (!contactListGroupChannels.contains(group)) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT),
+ QLatin1String("Invalid group"),
+ parent->connection());
+ }
+
+ ChannelPtr channel = contactListGroupChannels[group];
+ return channel->groupAddContacts(contacts);
+}
+
+PendingOperation *ContactManager::Private::removeContactsFromGroupFallback(const QString &group,
+ const QList<ContactPtr> &contacts)
+{
+ if (!contactListGroupChannels.contains(group)) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT),
+ QLatin1String("Invalid group"),
+ parent->connection());
+ }
+
+ ChannelPtr channel = contactListGroupChannels[group];
+ return channel->groupRemoveContacts(contacts);
+}
+
bool ContactManager::Private::buildAvatarFileName(QString token, bool createDir,
QString &avatarFileName, QString &mimeTypeFileName)
{
@@ -330,34 +673,6 @@ bool ContactManager::Private::buildAvatarFileName(QString token, bool createDir,
return true;
}
-namespace
-{
-
-QString featureToInterface(const Feature &feature)
-{
- if (feature == Contact::FeatureAlias) {
- return QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_ALIASING);
- } else if (feature == Contact::FeatureAvatarToken) {
- return QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_AVATARS);
- } else if (feature == Contact::FeatureAvatarData) {
- return QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_AVATARS);
- } else if (feature == Contact::FeatureSimplePresence) {
- return QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE);
- } else if (feature == Contact::FeatureCapabilities) {
- return QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_CONTACT_CAPABILITIES);
- } if (feature == Contact::FeatureLocation) {
- return QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_LOCATION);
- } if (feature == Contact::FeatureInfo) {
- return QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_CONTACT_INFO);
- } else {
- warning() << "ContactManager doesn't know which interface corresponds to feature"
- << feature;
- return QString();
- }
-}
-
-}
-
ContactManager::ContactManager(Connection *connection)
: Object(),
mPriv(new Private(this, connection))
@@ -419,7 +734,15 @@ Features ContactManager::supportedFeatures() const
*/
Contacts ContactManager::allKnownContacts() const
{
- return mPriv->allKnownContacts();
+ if (!connection()->isReady(Connection::FeatureRoster)) {
+ return Contacts();
+ }
+
+ if (mPriv->fallbackContactList) {
+ return mPriv->allKnownContactsFallback();
+ }
+
+ return mPriv->cachedAllKnownContacts;
}
/**
@@ -431,7 +754,15 @@ Contacts ContactManager::allKnownContacts() const
*/
QStringList ContactManager::allKnownGroups() const
{
- return mPriv->contactListGroupChannels.keys();
+ if (!connection()->isReady(Connection::FeatureRosterGroups)) {
+ return QStringList();
+ }
+
+ if (mPriv->fallbackContactList) {
+ return mPriv->contactListGroupChannels.keys();
+ }
+
+ return mPriv->allKnownGroups.toList();
}
/**
@@ -469,14 +800,20 @@ PendingOperation *ContactManager::addGroup(const QString &group)
connection());
}
- QVariantMap request;
- request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".ChannelType"),
- QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_CONTACT_LIST));
- request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetHandleType"),
- (uint) Tp::HandleTypeGroup);
- request.insert(QLatin1String(TELEPATHY_INTERFACE_CHANNEL ".TargetID"),
- group);
- return connection()->lowlevel()->ensureChannel(request);
+ if (mPriv->fallbackContactList) {
+ return mPriv->addGroupFallback(group);
+ }
+
+ if (!connection()->hasInterface(TP_QT4_IFACE_CONNECTION_INTERFACE_CONTACT_GROUPS)) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("Not implemented"),
+ connection());
+ }
+
+ Client::ConnectionInterfaceContactGroupsInterface *iface =
+ connection()->interface<Client::ConnectionInterfaceContactGroupsInterface>();
+ Q_ASSERT(iface);
+ return new PendingVoid(iface->AddToGroup(group, UIntList()), connection());
}
/**
@@ -508,16 +845,20 @@ PendingOperation *ContactManager::removeGroup(const QString &group)
connection());
}
- if (!mPriv->contactListGroupChannels.contains(group)) {
- return new PendingFailure(QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT),
- QLatin1String("Invalid group"),
+ if (mPriv->fallbackContactList) {
+ return mPriv->removeGroupFallback(group);
+ }
+
+ if (!connection()->hasInterface(TP_QT4_IFACE_CONNECTION_INTERFACE_CONTACT_GROUPS)) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("Not implemented"),
connection());
}
- ChannelPtr channel = mPriv->contactListGroupChannels[group];
- PendingContactManagerRemoveContactListGroup *op =
- new PendingContactManagerRemoveContactListGroup(channel);
- return op;
+ Client::ConnectionInterfaceContactGroupsInterface *iface =
+ connection()->interface<Client::ConnectionInterfaceContactGroupsInterface>();
+ Q_ASSERT(iface);
+ return new PendingVoid(iface->RemoveGroup(group), connection());
}
/**
@@ -533,12 +874,25 @@ PendingOperation *ContactManager::removeGroup(const QString &group)
*/
Contacts ContactManager::groupContacts(const QString &group) const
{
- if (!mPriv->contactListGroupChannels.contains(group)) {
+ if (!connection()->isReady(Connection::FeatureRosterGroups)) {
return Contacts();
}
- ChannelPtr channel = mPriv->contactListGroupChannels[group];
- return channel->groupContacts();
+ if (mPriv->fallbackContactList) {
+ if (!mPriv->contactListGroupChannels.contains(group)) {
+ return Contacts();
+ }
+
+ ChannelPtr channel = mPriv->contactListGroupChannels[group];
+ return channel->groupContacts();
+ }
+
+ Contacts ret;
+ foreach (const ContactPtr &contact, allKnownContacts()) {
+ if (contact->groups().contains(group))
+ ret << contact;
+ }
+ return ret;
}
/**
@@ -565,14 +919,25 @@ PendingOperation *ContactManager::addContactsToGroup(const QString &group,
connection());
}
- if (!mPriv->contactListGroupChannels.contains(group)) {
- return new PendingFailure(QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT),
- QLatin1String("Invalid group"),
+ if (mPriv->fallbackContactList) {
+ return mPriv->addContactsToGroupFallback(group, contacts);
+ }
+
+ if (!connection()->hasInterface(TP_QT4_IFACE_CONNECTION_INTERFACE_CONTACT_GROUPS)) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("Not implemented"),
connection());
}
- ChannelPtr channel = mPriv->contactListGroupChannels[group];
- return channel->groupAddContacts(contacts);
+ UIntList handles;
+ foreach (const ContactPtr &contact, contacts) {
+ handles << contact->handle()[0];
+ }
+
+ Client::ConnectionInterfaceContactGroupsInterface *iface =
+ connection()->interface<Client::ConnectionInterfaceContactGroupsInterface>();
+ Q_ASSERT(iface);
+ return new PendingVoid(iface->AddToGroup(group, handles), connection());
}
/**
@@ -599,14 +964,25 @@ PendingOperation *ContactManager::removeContactsFromGroup(const QString &group,
connection());
}
- if (!mPriv->contactListGroupChannels.contains(group)) {
- return new PendingFailure(QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT),
- QLatin1String("Invalid group"),
+ if (mPriv->fallbackContactList) {
+ return mPriv->removeContactsFromGroupFallback(group, contacts);
+ }
+
+ if (!connection()->hasInterface(TP_QT4_IFACE_CONNECTION_INTERFACE_CONTACT_GROUPS)) {
+ return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
+ QLatin1String("Not implemented"),
connection());
}
- ChannelPtr channel = mPriv->contactListGroupChannels[group];
- return channel->groupRemoveContacts(contacts);
+ UIntList handles;
+ foreach (const ContactPtr &contact, contacts) {
+ handles << contact->handle()[0];
+ }
+
+ Client::ConnectionInterfaceContactGroupsInterface *iface =
+ connection()->interface<Client::ConnectionInterfaceContactGroupsInterface>();
+ Q_ASSERT(iface);
+ return new PendingVoid(iface->RemoveFromGroup(group, handles), connection());
}
/**
@@ -624,8 +1000,16 @@ PendingOperation *ContactManager::removeContactsFromGroup(const QString &group,
*/
bool ContactManager::canRequestPresenceSubscription() const
{
- return mPriv->subscribeChannel &&
- mPriv->subscribeChannel->groupCanAddContacts();
+ if (!connection()->isReady(Connection::FeatureRoster)) {
+ return false;
+ }
+
+ if (mPriv->fallbackContactList) {
+ return mPriv->subscribeChannel &&
+ mPriv->subscribeChannel->groupCanAddContacts();
+ }
+
+ return mPriv->canChangeContactList;
}
/**
@@ -641,9 +1025,17 @@ bool ContactManager::canRequestPresenceSubscription() const
*/
bool ContactManager::subscriptionRequestHasMessage() const
{
- return mPriv->subscribeChannel &&
- (mPriv->subscribeChannel->groupFlags() &
- ChannelGroupFlagMessageAdd);
+ if (!connection()->isReady(Connection::FeatureRoster)) {
+ return false;
+ }
+
+ if (mPriv->fallbackContactList) {
+ return mPriv->subscribeChannel &&
+ (mPriv->subscribeChannel->groupFlags() &
+ ChannelGroupFlagMessageAdd);
+ }
+
+ return mPriv->contactListRequestUsesMessage;
}
/**
@@ -683,13 +1075,19 @@ PendingOperation *ContactManager::requestPresenceSubscription(
connection());
}
- if (!mPriv->subscribeChannel) {
- return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
- QLatin1String("Cannot subscribe to contacts' presence on this protocol"),
- connection());
+ if (mPriv->fallbackContactList) {
+ return mPriv->requestPresenceSubscriptionFallback(contacts, message);
}
- return mPriv->subscribeChannel->groupAddContacts(contacts, message);
+ UIntList handles;
+ foreach (const ContactPtr &contact, contacts) {
+ handles << contact->handle()[0];
+ }
+
+ Client::ConnectionInterfaceContactListInterface *iface =
+ connection()->interface<Client::ConnectionInterfaceContactListInterface>();
+ Q_ASSERT(iface);
+ return new PendingVoid(iface->RequestSubscription(handles, message), connection());
}
/**
@@ -702,8 +1100,16 @@ PendingOperation *ContactManager::requestPresenceSubscription(
*/
bool ContactManager::canRemovePresenceSubscription() const
{
- return mPriv->subscribeChannel &&
- mPriv->subscribeChannel->groupCanRemoveContacts();
+ if (!connection()->isReady(Connection::FeatureRoster)) {
+ return false;
+ }
+
+ if (mPriv->fallbackContactList) {
+ return mPriv->subscribeChannel &&
+ mPriv->subscribeChannel->groupCanRemoveContacts();
+ }
+
+ return mPriv->canChangeContactList;
}
/**
@@ -720,9 +1126,17 @@ bool ContactManager::canRemovePresenceSubscription() const
*/
bool ContactManager::subscriptionRemovalHasMessage() const
{
- return mPriv->subscribeChannel &&
- (mPriv->subscribeChannel->groupFlags() &
- ChannelGroupFlagMessageRemove);
+ if (!connection()->isReady(Connection::FeatureRoster)) {
+ return false;
+ }
+
+ if (mPriv->fallbackContactList) {
+ return mPriv->subscribeChannel &&
+ (mPriv->subscribeChannel->groupFlags() &
+ ChannelGroupFlagMessageRemove);
+ }
+
+ return false;
}
/**
@@ -735,8 +1149,16 @@ bool ContactManager::subscriptionRemovalHasMessage() const
*/
bool ContactManager::canRescindPresenceSubscriptionRequest() const
{
- return mPriv->subscribeChannel &&
- mPriv->subscribeChannel->groupCanRescindContacts();
+ if (!connection()->isReady(Connection::FeatureRoster)) {
+ return false;
+ }
+
+ if (mPriv->fallbackContactList) {
+ return mPriv->subscribeChannel &&
+ mPriv->subscribeChannel->groupCanRescindContacts();
+ }
+
+ return mPriv->canChangeContactList;
}
/**
@@ -753,9 +1175,17 @@ bool ContactManager::canRescindPresenceSubscriptionRequest() const
*/
bool ContactManager::subscriptionRescindingHasMessage() const
{
- return mPriv->subscribeChannel &&
- (mPriv->subscribeChannel->groupFlags() &
- ChannelGroupFlagMessageRescind);
+ if (!connection()->isReady(Connection::FeatureRoster)) {
+ return false;
+ }
+
+ if (mPriv->fallbackContactList) {
+ return mPriv->subscribeChannel &&
+ (mPriv->subscribeChannel->groupFlags() &
+ ChannelGroupFlagMessageRescind);
+ }
+
+ return false;
}
/**
@@ -783,13 +1213,19 @@ PendingOperation *ContactManager::removePresenceSubscription(
connection());
}
- if (!mPriv->subscribeChannel) {
- return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
- QLatin1String("Cannot subscribe to contacts' presence on this protocol"),
- connection());
+ if (mPriv->fallbackContactList) {
+ return mPriv->removePresenceSubscriptionFallback(contacts, message);
+ }
+
+ UIntList handles;
+ foreach (const ContactPtr &contact, contacts) {
+ handles << contact->handle()[0];
}
- return mPriv->subscribeChannel->groupRemoveContacts(contacts, message);
+ Client::ConnectionInterfaceContactListInterface *iface =
+ connection()->interface<Client::ConnectionInterfaceContactListInterface>();
+ Q_ASSERT(iface);
+ return new PendingVoid(iface->Unsubscribe(handles), connection());
}
/**
@@ -802,10 +1238,18 @@ PendingOperation *ContactManager::removePresenceSubscription(
*/
bool ContactManager::canAuthorizePresencePublication() const
{
- // do not check for Channel::groupCanAddContacts as all contacts in local
- // pending can be added, even if the Channel::groupFlags() does not contain
- // the flag CanAdd
- return (bool) mPriv->publishChannel;
+ if (!connection()->isReady(Connection::FeatureRoster)) {
+ return false;
+ }
+
+ if (mPriv->fallbackContactList) {
+ // do not check for Channel::groupCanAddContacts as all contacts in local
+ // pending can be added, even if the Channel::groupFlags() does not contain
+ // the flag CanAdd
+ return (bool) mPriv->publishChannel;
+ }
+
+ return mPriv->canChangeContactList;
}
/**
@@ -822,9 +1266,17 @@ bool ContactManager::canAuthorizePresencePublication() const
*/
bool ContactManager::publicationAuthorizationHasMessage() const
{
- return mPriv->subscribeChannel &&
- (mPriv->subscribeChannel->groupFlags() &
- ChannelGroupFlagMessageAccept);
+ if (!connection()->isReady(Connection::FeatureRoster)) {
+ return false;
+ }
+
+ if (mPriv->fallbackContactList) {
+ return mPriv->subscribeChannel &&
+ (mPriv->subscribeChannel->groupFlags() &
+ ChannelGroupFlagMessageAccept);
+ }
+
+ return false;
}
/**
@@ -853,13 +1305,19 @@ PendingOperation *ContactManager::authorizePresencePublication(
connection());
}
- if (!mPriv->publishChannel) {
- return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
- QLatin1String("Cannot control publication of presence on this protocol"),
- connection());
+ if (mPriv->fallbackContactList) {
+ return mPriv->authorizePresencePublicationFallback(contacts, message);
+ }
+
+ UIntList handles;
+ foreach (const ContactPtr &contact, contacts) {
+ handles << contact->handle()[0];
}
- return mPriv->publishChannel->groupAddContacts(contacts, message);
+ Client::ConnectionInterfaceContactListInterface *iface =
+ connection()->interface<Client::ConnectionInterfaceContactListInterface>();
+ Q_ASSERT(iface);
+ return new PendingVoid(iface->AuthorizePublication(handles), connection());
}
/**
@@ -876,9 +1334,17 @@ PendingOperation *ContactManager::authorizePresencePublication(
*/
bool ContactManager::publicationRejectionHasMessage() const
{
- return mPriv->subscribeChannel &&
- (mPriv->subscribeChannel->groupFlags() &
- ChannelGroupFlagMessageReject);
+ if (!connection()->isReady(Connection::FeatureRoster)) {
+ return false;
+ }
+
+ if (mPriv->fallbackContactList) {
+ return mPriv->subscribeChannel &&
+ (mPriv->subscribeChannel->groupFlags() &
+ ChannelGroupFlagMessageReject);
+ }
+
+ return false;
}
/**
@@ -893,8 +1359,16 @@ bool ContactManager::publicationRejectionHasMessage() const
*/
bool ContactManager::canRemovePresencePublication() const
{
- return mPriv->publishChannel &&
- mPriv->publishChannel->groupCanRemoveContacts();
+ if (!connection()->isReady(Connection::FeatureRoster)) {
+ return false;
+ }
+
+ if (mPriv->fallbackContactList) {
+ return mPriv->publishChannel &&
+ mPriv->publishChannel->groupCanRemoveContacts();
+ }
+
+ return mPriv->canChangeContactList;
}
/**
@@ -911,9 +1385,17 @@ bool ContactManager::canRemovePresencePublication() const
*/
bool ContactManager::publicationRemovalHasMessage() const
{
- return mPriv->subscribeChannel &&
- (mPriv->subscribeChannel->groupFlags() &
- ChannelGroupFlagMessageRemove);
+ if (!connection()->isReady(Connection::FeatureRoster)) {
+ return false;
+ }
+
+ if (mPriv->fallbackContactList) {
+ return mPriv->subscribeChannel &&
+ (mPriv->subscribeChannel->groupFlags() &
+ ChannelGroupFlagMessageRemove);
+ }
+
+ return false;
}
/**
@@ -948,13 +1430,19 @@ PendingOperation *ContactManager::removePresencePublication(
connection());
}
- if (!mPriv->publishChannel) {
- return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
- QLatin1String("Cannot control publication of presence on this protocol"),
- connection());
+ if (mPriv->fallbackContactList) {
+ return mPriv->removePresencePublicationFallback(contacts, message);
+ }
+
+ UIntList handles;
+ foreach (const ContactPtr &contact, contacts) {
+ handles << contact->handle()[0];
}
- return mPriv->publishChannel->groupRemoveContacts(contacts, message);
+ Client::ConnectionInterfaceContactListInterface *iface =
+ connection()->interface<Client::ConnectionInterfaceContactListInterface>();
+ Q_ASSERT(iface);
+ return new PendingVoid(iface->Unpublish(handles), connection());
}
/**
@@ -981,36 +1469,19 @@ PendingOperation *ContactManager::removeContacts(
connection());
}
- /* If the CM implements stored channel correctly, it should have the
- * wanted behaviour. Otherwise we have to fallback to remove from publish
- * and subscribe channels.
- */
-
- if (mPriv->storedChannel &&
- mPriv->storedChannel->groupCanRemoveContacts()) {
- debug() << "Removing contacts from stored list";
- return mPriv->storedChannel->groupRemoveContacts(contacts, message);
- }
-
- QList<PendingOperation*> operations;
-
- if (canRemovePresenceSubscription()) {
- debug() << "Removing contacts from subscribe list";
- operations << removePresenceSubscription(contacts, message);
- }
-
- if (canRemovePresencePublication()) {
- debug() << "Removing contacts from publish list";
- operations << removePresencePublication(contacts, message);
+ if (mPriv->fallbackContactList) {
+ return mPriv->removeContactsFallback(contacts, message);
}
- if (operations.isEmpty()) {
- return new PendingFailure(QLatin1String(TELEPATHY_ERROR_NOT_IMPLEMENTED),
- QLatin1String("Cannot remove contacts on this protocol"),
- connection());
+ UIntList handles;
+ foreach (const ContactPtr &contact, contacts) {
+ handles << contact->handle()[0];
}
- return new PendingComposite(operations, connection());
+ Client::ConnectionInterfaceContactListInterface *iface =
+ connection()->interface<Client::ConnectionInterfaceContactListInterface>();
+ Q_ASSERT(iface);
+ return new PendingVoid(iface->RemoveContacts(handles), connection());
}
/**
@@ -1020,6 +1491,10 @@ PendingOperation *ContactManager::removeContacts(
*/
bool ContactManager::canBlockContacts() const
{
+ if (!connection()->isReady(Connection::FeatureRoster)) {
+ return false;
+ }
+
return (bool) mPriv->denyChannel;
}
@@ -1334,7 +1809,132 @@ void ContactManager::onContactInfoChanged(uint handle,
}
}
-void ContactManager::onStoredChannelMembersChanged(
+void ContactManager::onContactListNewContactsConstructed(Tp::PendingOperation *op)
+{
+ if (op->isError()) {
+ mPriv->contactListUpdatesQueue.dequeue();
+ mPriv->processingContactListChanges = false;
+ mPriv->processContactListChanges();
+ return;
+ }
+
+ Private::ContactListUpdateInfo info = mPriv->contactListUpdatesQueue.dequeue();
+
+ Tp::Contacts added;
+ Tp::Contacts removed;
+
+ ContactSubscriptionMap::const_iterator begin = info.changes.constBegin();
+ ContactSubscriptionMap::const_iterator end = info.changes.constEnd();
+ for (ContactSubscriptionMap::const_iterator i = begin; i != end; ++i) {
+ uint bareHandle = i.key();
+ ContactSubscriptions subscriptions = i.value();
+
+ ContactPtr contact = lookupContactByHandle(bareHandle);
+ if (!contact) {
+ warning() << "Unable to construct contact for handle" << bareHandle;
+ continue;
+ }
+
+ if (!mPriv->cachedAllKnownContacts.contains(contact)) {
+ mPriv->cachedAllKnownContacts.insert(contact);
+ added << contact;
+ }
+
+ contact->setSubscriptionState((SubscriptionState) subscriptions.subscribe);
+ if (!subscriptions.publishRequest.isEmpty() &&
+ subscriptions.publish == SubscriptionStateAsk) {
+ Channel::GroupMemberChangeDetails publishRequestDetails;
+ QVariantMap detailsMap;
+ detailsMap.insert(QLatin1String("message"), subscriptions.publishRequest);
+ publishRequestDetails = Channel::GroupMemberChangeDetails(ContactPtr(), detailsMap);
+ // FIXME (API/ABI break) remove signal with details
+ emit presencePublicationRequested(Contacts() << contact, publishRequestDetails);
+
+ emit presencePublicationRequested(Contacts() << contact, subscriptions.publishRequest);
+ }
+ contact->setPublishState((SubscriptionState) subscriptions.publish,
+ subscriptions.publishRequest);
+ }
+
+ foreach (uint bareHandle, info.removals) {
+ ContactPtr contact = lookupContactByHandle(bareHandle);
+ if (!contact) {
+ warning() << "Unable to find removed contact with handle" << bareHandle;
+ continue;
+ }
+
+ Q_ASSERT(mPriv->cachedAllKnownContacts.contains(contact));
+
+ contact->setSubscriptionState(SubscriptionStateNo);
+ contact->setPublishState(SubscriptionStateNo);
+ mPriv->cachedAllKnownContacts.remove(contact);
+ removed << contact;
+ }
+
+ if (!added.isEmpty() || !removed.isEmpty()) {
+ emit allKnownContactsChanged(added, removed, Channel::GroupMemberChangeDetails());
+ }
+
+ mPriv->processingContactListChanges = false;
+ mPriv->processContactListChanges();
+}
+
+void ContactManager::onContactListGroupsChanged(const Tp::UIntList &contacts,
+ const QStringList &added, const QStringList &removed)
+{
+ Q_ASSERT(mPriv->fallbackContactList == false);
+
+ if (!mPriv->contactListGroupPropertiesReceived) {
+ return;
+ }
+
+ mPriv->contactListGroupsUpdatesQueue.enqueue(Private::ContactListGroupsUpdateInfo(contacts,
+ added, removed));
+ mPriv->contactListChangesQueue.enqueue(&Private::processContactListGroupsUpdates);
+ mPriv->processContactListChanges();
+}
+
+void ContactManager::onContactListGroupsCreated(const QStringList &names)
+{
+ Q_ASSERT(mPriv->fallbackContactList == false);
+
+ if (!mPriv->contactListGroupPropertiesReceived) {
+ return;
+ }
+
+ mPriv->contactListGroupsCreatedQueue.enqueue(names);
+ mPriv->contactListChangesQueue.enqueue(&Private::processContactListGroupsCreated);
+ mPriv->processContactListChanges();
+}
+
+void ContactManager::onContactListGroupRenamed(const QString &oldName, const QString &newName)
+{
+ Q_ASSERT(mPriv->fallbackContactList == false);
+
+ if (!mPriv->contactListGroupPropertiesReceived) {
+ return;
+ }
+
+ mPriv->contactListGroupRenamedQueue.enqueue(
+ Private::ContactListGroupRenamedInfo(oldName, newName));
+ mPriv->contactListChangesQueue.enqueue(&Private::processContactListGroupRenamed);
+ mPriv->processContactListChanges();
+}
+
+void ContactManager::onContactListGroupsRemoved(const QStringList &names)
+{
+ Q_ASSERT(mPriv->fallbackContactList == false);
+
+ if (!mPriv->contactListGroupPropertiesReceived) {
+ return;
+ }
+
+ mPriv->contactListGroupsRemovedQueue.enqueue(names);
+ mPriv->contactListChangesQueue.enqueue(&Private::processContactListGroupsRemoved);
+ mPriv->processContactListChanges();
+}
+
+void ContactManager::onStoredChannelMembersChangedFallback(
const Contacts &groupMembersAdded,
const Contacts &groupLocalPendingMembersAdded,
const Contacts &groupRemotePendingMembersAdded,
@@ -1358,13 +1958,12 @@ void ContactManager::onStoredChannelMembersChanged(
}
// Perform the needed computation for allKnownContactsChanged
- mPriv->computeKnownContactsChanges(groupMembersAdded,
- groupLocalPendingMembersAdded,
- groupRemotePendingMembersAdded,
- groupMembersRemoved, details);
+ mPriv->computeKnownContactsChangesFallback(groupMembersAdded,
+ groupLocalPendingMembersAdded, groupRemotePendingMembersAdded,
+ groupMembersRemoved, details);
}
-void ContactManager::onSubscribeChannelMembersChanged(
+void ContactManager::onSubscribeChannelMembersChangedFallback(
const Contacts &groupMembersAdded,
const Contacts &groupLocalPendingMembersAdded,
const Contacts &groupRemotePendingMembersAdded,
@@ -1377,27 +1976,26 @@ void ContactManager::onSubscribeChannelMembersChanged(
foreach (ContactPtr contact, groupMembersAdded) {
debug() << "Contact" << contact->id() << "on subscribe list";
- contact->setSubscriptionState(Contact::PresenceStateYes, details);
+ contact->setSubscriptionState(SubscriptionStateYes);
}
foreach (ContactPtr contact, groupRemotePendingMembersAdded) {
debug() << "Contact" << contact->id() << "added to subscribe list";
- contact->setSubscriptionState(Contact::PresenceStateAsk, details);
+ contact->setSubscriptionState(SubscriptionStateAsk);
}
foreach (ContactPtr contact, groupMembersRemoved) {
debug() << "Contact" << contact->id() << "removed from subscribe list";
- contact->setSubscriptionState(Contact::PresenceStateNo, details);
+ contact->setSubscriptionState(SubscriptionStateNo);
}
// Perform the needed computation for allKnownContactsChanged
- mPriv->computeKnownContactsChanges(groupMembersAdded,
- groupLocalPendingMembersAdded,
- groupRemotePendingMembersAdded,
- groupMembersRemoved, details);
+ mPriv->computeKnownContactsChangesFallback(groupMembersAdded,
+ groupLocalPendingMembersAdded, groupRemotePendingMembersAdded,
+ groupMembersRemoved, details);
}
-void ContactManager::onPublishChannelMembersChanged(
+void ContactManager::onPublishChannelMembersChangedFallback(
const Contacts &groupMembersAdded,
const Contacts &groupLocalPendingMembersAdded,
const Contacts &groupRemotePendingMembersAdded,
@@ -1410,29 +2008,32 @@ void ContactManager::onPublishChannelMembersChanged(
foreach (ContactPtr contact, groupMembersAdded) {
debug() << "Contact" << contact->id() << "on publish list";
- contact->setPublishState(Contact::PresenceStateYes, details);
+ contact->setPublishState(SubscriptionStateYes);
}
foreach (ContactPtr contact, groupLocalPendingMembersAdded) {
debug() << "Contact" << contact->id() << "added to publish list";
- contact->setPublishState(Contact::PresenceStateAsk, details);
+ contact->setPublishState(SubscriptionStateAsk, details.message());
}
foreach (ContactPtr contact, groupMembersRemoved) {
debug() << "Contact" << contact->id() << "removed from publish list";
- contact->setPublishState(Contact::PresenceStateNo, details);
+ contact->setPublishState(SubscriptionStateNo);
}
if (!groupLocalPendingMembersAdded.isEmpty()) {
+ // FIXME (API/ABI break) remove signal with details
emit presencePublicationRequested(groupLocalPendingMembersAdded,
details);
+
+ emit presencePublicationRequested(groupLocalPendingMembersAdded,
+ details.message());
}
// Perform the needed computation for allKnownContactsChanged
- mPriv->computeKnownContactsChanges(groupMembersAdded,
- groupLocalPendingMembersAdded,
- groupRemotePendingMembersAdded,
- groupMembersRemoved, details);
+ mPriv->computeKnownContactsChangesFallback(groupMembersAdded,
+ groupLocalPendingMembersAdded, groupRemotePendingMembersAdded,
+ groupMembersRemoved, details);
}
void ContactManager::onDenyChannelMembersChanged(
@@ -1452,16 +2053,16 @@ void ContactManager::onDenyChannelMembersChanged(
foreach (ContactPtr contact, groupMembersAdded) {
debug() << "Contact" << contact->id() << "added to deny list";
- contact->setBlocked(true, details);
+ contact->setBlocked(true);
}
foreach (ContactPtr contact, groupMembersRemoved) {
debug() << "Contact" << contact->id() << "removed from deny list";
- contact->setBlocked(false, details);
+ contact->setBlocked(false);
}
}
-void ContactManager::onContactListGroupMembersChanged(
+void ContactManager::onContactListGroupMembersChangedFallback(
const Tp::Contacts &groupMembersAdded,
const Tp::Contacts &groupLocalPendingMembersAdded,
const Tp::Contacts &groupRemotePendingMembersAdded,
@@ -1483,7 +2084,7 @@ void ContactManager::onContactListGroupMembersChanged(
emit groupMembersChanged(id, groupMembersAdded, groupMembersRemoved, details);
}
-void ContactManager::onContactListGroupRemoved(Tp::DBusProxy *proxy,
+void ContactManager::onContactListGroupRemovedFallback(Tp::DBusProxy *proxy,
const QString &errorName, const QString &errorMessage)
{
Q_UNUSED(errorName);
@@ -1516,10 +2117,64 @@ ContactPtr ContactManager::ensureContact(const ReferencedHandles &handle,
return contact;
}
+void ContactManager::setUseFallbackContactList(bool value)
+{
+ mPriv->fallbackContactList = value;
+}
+
+void ContactManager::setContactListProperties(const QVariantMap &props)
+{
+ Q_ASSERT(mPriv->fallbackContactList == false);
+
+ mPriv->canChangeContactList = qdbus_cast<uint>(props[QLatin1String("CanChangeContactList")]);
+ mPriv->contactListRequestUsesMessage = qdbus_cast<uint>(props[QLatin1String("RequestUsesMessage")]);
+}
+
+void ContactManager::setContactListContacts(const ContactAttributesMap &attrsMap)
+{
+ Q_ASSERT(mPriv->fallbackContactList == false);
+
+ ContactAttributesMap::const_iterator begin = attrsMap.constBegin();
+ ContactAttributesMap::const_iterator end = attrsMap.constEnd();
+ for (ContactAttributesMap::const_iterator i = begin; i != end; ++i) {
+ uint bareHandle = i.key();
+ QVariantMap attrs = i.value();
+
+ ContactPtr contact = ensureContact(ReferencedHandles(connection(),
+ HandleTypeContact, UIntList() << bareHandle),
+ Features(), attrs);
+ mPriv->cachedAllKnownContacts.insert(contact);
+ }
+}
+
+void ContactManager::updateContactListContacts(const ContactSubscriptionMap &changes,
+ const UIntList &removals)
+{
+ Q_ASSERT(mPriv->fallbackContactList == false);
+
+ mPriv->contactListUpdatesQueue.enqueue(Private::ContactListUpdateInfo(changes, removals));
+ mPriv->contactListChangesQueue.enqueue(&Private::processContactListUpdates);
+ mPriv->processContactListChanges();
+}
+
+void ContactManager::setContactListGroupsProperties(const QVariantMap &props)
+{
+ Q_ASSERT(mPriv->fallbackContactList == false);
+ Q_ASSERT(mPriv->contactListGroupPropertiesReceived == false);
+
+ mPriv->allKnownGroups = qdbus_cast<QStringList>(props[QLatin1String("Groups")]).toSet();
+ mPriv->contactListGroupPropertiesReceived = true;
+}
+
void ContactManager::setContactListChannels(
const QMap<uint, ContactListChannel> &contactListChannels)
{
- Q_ASSERT(mPriv->contactListChannels.isEmpty());
+ if (!mPriv->fallbackContactList) {
+ Q_ASSERT(!contactListChannels.contains(ContactListChannel::TypeSubscribe));
+ Q_ASSERT(!contactListChannels.contains(ContactListChannel::TypePublish));
+ Q_ASSERT(!contactListChannels.contains(ContactListChannel::TypeStored));
+ }
+
mPriv->contactListChannels = contactListChannels;
if (mPriv->contactListChannels.contains(ContactListChannel::TypeSubscribe)) {
@@ -1538,9 +2193,13 @@ void ContactManager::setContactListChannels(
mPriv->denyChannel = mPriv->contactListChannels[ContactListChannel::TypeDeny].channel;
}
- mPriv->updateContactsPresenceState();
- // Refresh the cache for the current known contacts
- mPriv->cachedAllKnownContacts = allKnownContacts();
+ mPriv->updateContactsBlockState();
+
+ if (mPriv->fallbackContactList) {
+ mPriv->updateContactsPresenceStateFallback();
+ // Refresh the cache for the current known contacts
+ mPriv->cachedAllKnownContacts = allKnownContacts();
+ }
uint type;
ChannelPtr channel;
@@ -1554,21 +2213,21 @@ void ContactManager::setContactListChannels(
}
if (type == ContactListChannel::TypeStored) {
- method = SLOT(onStoredChannelMembersChanged(
+ method = SLOT(onStoredChannelMembersChangedFallback(
Tp::Contacts,
Tp::Contacts,
Tp::Contacts,
Tp::Contacts,
Tp::Channel::GroupMemberChangeDetails));
}else if (type == ContactListChannel::TypeSubscribe) {
- method = SLOT(onSubscribeChannelMembersChanged(
+ method = SLOT(onSubscribeChannelMembersChangedFallback(
Tp::Contacts,
Tp::Contacts,
Tp::Contacts,
Tp::Contacts,
Tp::Channel::GroupMemberChangeDetails));
} else if (type == ContactListChannel::TypePublish) {
- method = SLOT(onPublishChannelMembersChanged(
+ method = SLOT(onPublishChannelMembersChangedFallback(
Tp::Contacts,
Tp::Contacts,
Tp::Contacts,
@@ -1596,23 +2255,52 @@ void ContactManager::setContactListChannels(
}
}
-void ContactManager::setContactListGroupChannels(
+void ContactManager::setContactListGroupChannelsFallback(
const QList<ChannelPtr> &contactListGroupChannels)
{
+ Q_ASSERT(mPriv->fallbackContactList == true);
+
Q_ASSERT(mPriv->contactListGroupChannels.isEmpty());
foreach (const ChannelPtr &contactListGroupChannel, contactListGroupChannels) {
- mPriv->addContactListGroupChannel(contactListGroupChannel);
+ mPriv->addContactListGroupChannelFallback(contactListGroupChannel);
}
}
-void ContactManager::addContactListGroupChannel(
+void ContactManager::addContactListGroupChannelFallback(
const ChannelPtr &contactListGroupChannel)
{
- QString id = mPriv->addContactListGroupChannel(contactListGroupChannel);
+ Q_ASSERT(mPriv->fallbackContactList == true);
+
+ QString id = mPriv->addContactListGroupChannelFallback(contactListGroupChannel);
emit groupAdded(id);
}
+QString ContactManager::featureToInterface(const Feature &feature)
+{
+ if (feature == Contact::FeatureAlias) {
+ return TP_QT4_IFACE_CONNECTION_INTERFACE_ALIASING;
+ } else if (feature == Contact::FeatureAvatarToken) {
+ return TP_QT4_IFACE_CONNECTION_INTERFACE_AVATARS;
+ } else if (feature == Contact::FeatureAvatarData) {
+ return TP_QT4_IFACE_CONNECTION_INTERFACE_AVATARS;
+ } else if (feature == Contact::FeatureSimplePresence) {
+ return TP_QT4_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE;
+ } else if (feature == Contact::FeatureCapabilities) {
+ return TP_QT4_IFACE_CONNECTION_INTERFACE_CONTACT_CAPABILITIES;
+ } else if (feature == Contact::FeatureLocation) {
+ return TP_QT4_IFACE_CONNECTION_INTERFACE_LOCATION;
+ } else if (feature == Contact::FeatureInfo) {
+ return TP_QT4_IFACE_CONNECTION_INTERFACE_CONTACT_INFO;
+ } else if (feature == Contact::FeatureRosterGroups) {
+ return TP_QT4_IFACE_CONNECTION_INTERFACE_CONTACT_GROUPS;
+ } else {
+ warning() << "ContactManager doesn't know which interface corresponds to feature"
+ << feature;
+ return QString();
+ }
+}
+
QString ContactManager::ContactListChannel::identifierForType(Type type)
{
static QString identifiers[LastType] = {
@@ -1640,13 +2328,22 @@ uint ContactManager::ContactListChannel::typeForIdentifier(const QString &identi
}
/**
- * \fn void ContactManager::presencePublicationRequested(const Tp::Contacts &contacts);
- * const Tp::Channel::GroupMemberChangeDetails &details);
+ * \fn void ContactManager::presencePublicationRequested(const Tp::Contacts &contacts,
+ * const QString &message);
*
* This signal is emitted whenever some contacts request for presence publication.
*
* \param contacts A set of contacts which requested presence publication.
- * \param details The request details.
+ * \param message An optional message that was sent by the contacts asking to receive the local
+ * user's presence.
+ */
+
+/**
+ * \fn void ContactManager::presencePublicationRequested(const Tp::Contacts &contacts,
+ * const Tp::Channel::GroupMemberChangeDetails &details);
+ *
+ * \deprecated Use presencePublicationRequested(const Tp::Contacts &contact, const QString &message)
+ * instead.
*/
/**
@@ -1721,4 +2418,11 @@ void PendingContactManagerRemoveContactListGroup::onChannelClosed(PendingOperati
}
}
+void ContactManager::connectNotify(const char *signalName)
+{
+ if (qstrcmp(signalName, SIGNAL(presencePublicationRequested(Tp::Contacts,Tp::Channel::GroupMemberChangeDetails))) == 0) {
+ warning() << "Connecting to deprecated signal presencePublicationRequested(Tp::Contacts,Tp::Channel::GroupMemberChangeDetails)";
+ }
+}
+
} // Tp
diff --git a/TelepathyQt4/contact-manager.h b/TelepathyQt4/contact-manager.h
index 2055f868..a676c386 100644
--- a/TelepathyQt4/contact-manager.h
+++ b/TelepathyQt4/contact-manager.h
@@ -117,10 +117,13 @@ public:
void requestContactAvatar(Contact *contact);
Q_SIGNALS:
+ void presencePublicationRequested(const Tp::Contacts &contacts, const QString &message);
+ // deprecated
void presencePublicationRequested(const Tp::Contacts &contacts,
const Tp::Channel::GroupMemberChangeDetails &details);
void groupAdded(const QString &group);
+ void groupRenamed(const QString &oldGroup, const QString &newGroup);
void groupRemoved(const QString &group);
void groupMembersChanged(const QString &group,
@@ -132,6 +135,10 @@ Q_SIGNALS:
const Tp::Contacts &contactsRemoved,
const Tp::Channel::GroupMemberChangeDetails &details);
+protected:
+ // FIXME: (API/ABI break) Remove connectNotify
+ void connectNotify(const char *);
+
private Q_SLOTS:
void onAliasesChanged(const Tp::AliasPairList &);
void doRequestAvatars();
@@ -142,19 +149,26 @@ private Q_SLOTS:
void onLocationUpdated(uint, const QVariantMap &);
void onContactInfoChanged(uint, const Tp::ContactInfoFieldList &);
- void onStoredChannelMembersChanged(
+ void onContactListNewContactsConstructed(Tp::PendingOperation *op);
+ void onContactListGroupsChanged(const Tp::UIntList &contacts,
+ const QStringList &added, const QStringList &removed);
+ void onContactListGroupsCreated(const QStringList &names);
+ void onContactListGroupRenamed(const QString &oldName, const QString &newName);
+ void onContactListGroupsRemoved(const QStringList &names);
+
+ void onStoredChannelMembersChangedFallback(
const Tp::Contacts &groupMembersAdded,
const Tp::Contacts &groupLocalPendingMembersAdded,
const Tp::Contacts &groupRemotePendingMembersAdded,
const Tp::Contacts &groupMembersRemoved,
const Tp::Channel::GroupMemberChangeDetails &details);
- void onSubscribeChannelMembersChanged(
+ void onSubscribeChannelMembersChangedFallback(
const Tp::Contacts &groupMembersAdded,
const Tp::Contacts &groupLocalPendingMembersAdded,
const Tp::Contacts &groupRemotePendingMembersAdded,
const Tp::Contacts &groupMembersRemoved,
const Tp::Channel::GroupMemberChangeDetails &details);
- void onPublishChannelMembersChanged(
+ void onPublishChannelMembersChangedFallback(
const Tp::Contacts &groupMembersAdded,
const Tp::Contacts &groupLocalPendingMembersAdded,
const Tp::Contacts &groupRemotePendingMembersAdded,
@@ -167,14 +181,14 @@ private Q_SLOTS:
const Tp::Contacts &groupMembersRemoved,
const Tp::Channel::GroupMemberChangeDetails &details);
- void onContactListGroupMembersChanged(
+ void onContactListGroupMembersChangedFallback(
const Tp::Contacts &groupMembersAdded,
const Tp::Contacts &groupLocalPendingMembersAdded,
const Tp::Contacts &groupRemotePendingMembersAdded,
const Tp::Contacts &groupMembersRemoved,
const Tp::Channel::GroupMemberChangeDetails &details);
- void onContactListGroupRemoved(Tp::DBusProxy *,
- const QString &, const QString &);
+ void onContactListGroupRemovedFallback(Tp::DBusProxy *proxy,
+ const QString &errorName, const QString &errorMessage);
private:
friend class Connection;
@@ -218,11 +232,23 @@ private:
const Features &features,
const QVariantMap &attributes);
+ void setUseFallbackContactList(bool value);
+
+ void setContactListProperties(const QVariantMap &props);
+ void setContactListContacts(const ContactAttributesMap &attrs);
+ void updateContactListContacts(const ContactSubscriptionMap &changes,
+ const UIntList &removals);
+ void setContactListGroupsProperties(const QVariantMap &props);
+
void setContactListChannels(
const QMap<uint, ContactListChannel> &contactListChannels);
- void setContactListGroupChannels(
+
+ void setContactListGroupChannelsFallback(
const QList<ChannelPtr> &contactListGroupChannels);
- void addContactListGroupChannel(const ChannelPtr &contactListGroupChannel);
+ void addContactListGroupChannelFallback(
+ const ChannelPtr &contactListGroupChannel);
+
+ static QString featureToInterface(const Feature &feature);
struct Private;
friend struct Private;
diff --git a/TelepathyQt4/contact.cpp b/TelepathyQt4/contact.cpp
index 8da6f5a8..b4136fc0 100644
--- a/TelepathyQt4/contact.cpp
+++ b/TelepathyQt4/contact.cpp
@@ -51,8 +51,8 @@ struct TELEPATHY_QT4_NO_EXPORT Contact::Private
ContactCapabilities(true) : ContactCapabilities(
manager->connection()->capabilities().allClassSpecs(), false)),
isAvatarTokenKnown(false),
- subscriptionState(PresenceStateNo),
- publishState(PresenceStateNo),
+ subscriptionState(SubscriptionStateUnknown),
+ publishState(SubscriptionStateUnknown),
blocked(false)
{
}
@@ -78,8 +78,9 @@ struct TELEPATHY_QT4_NO_EXPORT Contact::Private
QString avatarToken;
AvatarData avatarData;
- PresenceState subscriptionState;
- PresenceState publishState;
+ SubscriptionState subscriptionState;
+ SubscriptionState publishState;
+ QString publishStateMessage;
bool blocked;
QSet<QString> groups;
@@ -165,6 +166,7 @@ const Feature Contact::FeatureCapabilities = Feature(QLatin1String(Contact::stat
const Feature Contact::FeatureInfo = Feature(QLatin1String(Contact::staticMetaObject.className()), 4, false);
const Feature Contact::FeatureLocation = Feature(QLatin1String(Contact::staticMetaObject.className()), 5, false);
const Feature Contact::FeatureSimplePresence = Feature(QLatin1String(Contact::staticMetaObject.className()), 6, false);
+const Feature Contact::FeatureRosterGroups = Feature(QLatin1String(Contact::staticMetaObject.className()), 7, false);
Contact::Contact(ContactManager *manager, const ReferencedHandles &handle,
const Features &requestedFeatures, const QVariantMap &attributes)
@@ -391,14 +393,39 @@ PendingContactInfo *Contact::requestInfo()
return new PendingContactInfo(self);
}
+bool Contact::isSubscriptionStateKnown() const
+{
+ return mPriv->subscriptionState != SubscriptionStateUnknown;
+}
+
+bool Contact::isSubscriptionRejected() const
+{
+ return mPriv->subscriptionState == SubscriptionStateRemovedRemotely;
+}
+
Contact::PresenceState Contact::subscriptionState() const
{
- return mPriv->subscriptionState;
+ return subscriptionStateToPresenceState(mPriv->subscriptionState);
+}
+
+bool Contact::isPublishStateKnown() const
+{
+ return mPriv->publishState != SubscriptionStateUnknown;
+}
+
+bool Contact::isPublishCancelled() const
+{
+ return mPriv->publishState == SubscriptionStateRemovedRemotely;
}
Contact::PresenceState Contact::publishState() const
{
- return mPriv->publishState;
+ return subscriptionStateToPresenceState(mPriv->publishState);
+}
+
+QString Contact::publishStateMessage() const
+{
+ return mPriv->publishStateMessage;
}
PendingOperation *Contact::requestPresenceSubscription(const QString &message)
@@ -489,6 +516,22 @@ void Contact::augment(const Features &requestedFeatures, const QVariantMap &attr
mPriv->id = qdbus_cast<QString>(attributes[
QLatin1String(TELEPATHY_INTERFACE_CONNECTION "/contact-id")]);
+ if (attributes.contains(TP_QT4_IFACE_CONNECTION_INTERFACE_CONTACT_LIST +
+ QLatin1String("/subscribe"))) {
+ uint subscriptionState = qdbus_cast<uint>(attributes.value(
+ TP_QT4_IFACE_CONNECTION_INTERFACE_CONTACT_LIST + QLatin1String("/subscribe")));
+ setSubscriptionState((SubscriptionState) subscriptionState);
+ }
+
+ if (attributes.contains(TP_QT4_IFACE_CONNECTION_INTERFACE_CONTACT_LIST +
+ QLatin1String("/publish"))) {
+ uint publishState = qdbus_cast<uint>(attributes.value(
+ TP_QT4_IFACE_CONNECTION_INTERFACE_CONTACT_LIST + QLatin1String("/publish")));
+ QString publishRequest = qdbus_cast<QString>(attributes.value(
+ TP_QT4_IFACE_CONNECTION_INTERFACE_CONTACT_LIST + QLatin1String("/publish-request")));
+ setPublishState((SubscriptionState) publishState, publishRequest);
+ }
+
foreach (const Feature &feature, requestedFeatures) {
QString maybeAlias;
SimplePresence maybePresence;
@@ -580,6 +623,10 @@ void Contact::augment(const Features &requestedFeatures, const QVariantMap &attr
mPriv->presence.setStatus(ConnectionPresenceTypeUnknown,
QLatin1String("unknown"), QLatin1String(""));
}
+ } else if (feature == FeatureRosterGroups) {
+ QStringList groups = qdbus_cast<QStringList>(attributes.value(
+ TP_QT4_IFACE_CONNECTION_INTERFACE_CONTACT_GROUPS + QLatin1String("/groups")));
+ mPriv->groups = groups.toSet();
} else {
warning() << "Unknown feature" << feature << "encountered when augmenting Contact";
}
@@ -687,33 +734,63 @@ void Contact::receiveInfo(const ContactInfoFieldList &info)
}
}
-void Contact::setSubscriptionState(Contact::PresenceState state,
- const Channel::GroupMemberChangeDetails &details)
+Contact::PresenceState Contact::subscriptionStateToPresenceState(uint subscriptionState)
+{
+ switch (subscriptionState) {
+ case SubscriptionStateAsk:
+ return PresenceStateAsk;
+ case SubscriptionStateYes:
+ return PresenceStateYes;
+ default:
+ return PresenceStateNo;
+ }
+}
+
+void Contact::setSubscriptionState(SubscriptionState state)
{
if (mPriv->subscriptionState == state) {
return;
}
+
mPriv->subscriptionState = state;
- emit subscriptionStateChanged(state, details);
+
+ // FIXME (API/ABI break) remove signal with details
+ emit subscriptionStateChanged(subscriptionStateToPresenceState(state),
+ Channel::GroupMemberChangeDetails());
+
+ emit subscriptionStateChanged(subscriptionStateToPresenceState(state));
}
-void Contact::setPublishState(Contact::PresenceState state,
- const Channel::GroupMemberChangeDetails &details)
+void Contact::setPublishState(SubscriptionState state, const QString &message)
{
if (mPriv->publishState == state) {
return;
}
+
mPriv->publishState = state;
- emit publishStateChanged(state, details);
+ mPriv->publishStateMessage = message;
+
+ // FIXME (API/ABI break) remove signal with details
+ QVariantMap detailsMap;
+ detailsMap.insert(QLatin1String("message"), message);
+ emit publishStateChanged(subscriptionStateToPresenceState(state),
+ Channel::GroupMemberChangeDetails(ContactPtr(), detailsMap));
+
+ emit publishStateChanged(subscriptionStateToPresenceState(state), message);
}
-void Contact::setBlocked(bool value, const Channel::GroupMemberChangeDetails &details)
+void Contact::setBlocked(bool value)
{
if (mPriv->blocked == value) {
return;
}
+
mPriv->blocked = value;
- emit blockStatusChanged(value, details);
+
+ // FIXME (API/ABI break) remove signal with details
+ emit blockStatusChanged(value, Channel::GroupMemberChangeDetails());
+
+ emit blockStatusChanged(value);
}
void Contact::setAddedToGroup(const QString &group)
@@ -749,4 +826,60 @@ void Contact::setRemovedFromGroup(const QString &group)
* \sa infoFields()
*/
+/**
+ * \fn void Contact::subscriptionStateChanged(Tp::Contact::PresenceState state)
+ *
+ * This signal is emitted whenever the value of subscriptionState() changes.
+ *
+ * \param state The new subscription state.
+ */
+
+/**
+ * \fn void Contact::subscriptionStateChanged(Tp::Contact::PresenceState state,
+ * const Tp::Channel::GroupMemberChangeDetails &details)
+ *
+ * \deprecated Use subscriptionStateChanged(Tp::Contact::PresenceState state) instead.
+ */
+
+/**
+ * \fn void Contact::publishStateChanged(Tp::Contact::PresenceState state)
+ *
+ * This signal is emitted whenever the value of publishState() changes.
+ *
+ * \param state The new publish state.
+ */
+
+/**
+ * \fn void Contact::publishStateChanged(Tp::Contact::PresenceState state,
+ * const Tp::Channel::GroupMemberChangeDetails &details)
+ *
+ * \deprecated Use publishStateChanged(Tp::Contact::PresenceState state) instead.
+ */
+
+/**
+ * \fn void Contact::blockStatusChanged(bool blocked)
+ *
+ * This signal is emitted whenever the value of isBlocked() changes.
+ *
+ * \param status The new block status.
+ */
+
+/**
+ * \fn void Contact::blockStatusChanged(bool blocked,
+ * const Tp::Channel::GroupMemberChangeDetails &details)
+ *
+ * \deprecated Use blockStatusChanged(bool blocked) instead.
+ */
+
+void Contact::connectNotify(const char *signalName)
+{
+ if (qstrcmp(signalName, SIGNAL(subscriptionStateChanged(Tp::Contact::PresenceState,Tp::Channel::GroupMemberChangeDetails))) == 0) {
+ warning() << "Connecting to deprecated signal subscriptionStateChanged(Tp::Contact::PresenceState,Tp::Channel::GroupMemberChangeDetails)";
+ } else if (qstrcmp(signalName, SIGNAL(publishStateChanged(Tp::Contact::PresenceState,Tp::Channel::GroupMemberChangeDetails))) == 0) {
+ warning() << "Connecting to deprecated signal publishStateChanged(Tp::Contact::PresenceState,Tp::Channel::GroupMemberChangeDetails)";
+ } else if (qstrcmp(signalName, SIGNAL(blockStatusChanged(bool,Tp::Channel::GroupMemberChangeDetails))) == 0) {
+ warning() << "Connecting to deprecated signal blockStatusChanged(bool,Tp::Channel::GroupMemberChangeDetails)";
+ }
+}
+
} // Tp
diff --git a/TelepathyQt4/contact.h b/TelepathyQt4/contact.h
index fa9097cf..b2544bca 100644
--- a/TelepathyQt4/contact.h
+++ b/TelepathyQt4/contact.h
@@ -131,8 +131,13 @@ public:
/*
* Filters on exact values of these, but also the "in your contact list at all or not" usecase
*/
+ bool isSubscriptionStateKnown() const;
+ bool isSubscriptionRejected() const;
PresenceState subscriptionState() const;
+ bool isPublishStateKnown() const;
+ bool isPublishCancelled() const;
PresenceState publishState() const;
+ QString publishStateMessage() const;
PendingOperation *requestPresenceSubscription(const QString &message = QString());
PendingOperation *removePresenceSubscription(const QString &message = QString());
@@ -168,10 +173,18 @@ Q_SIGNALS:
void infoFieldsChanged(const Tp::Contact::InfoFields &infoFields);
+ void subscriptionStateChanged(Tp::Contact::PresenceState state);
+ // deprecated
void subscriptionStateChanged(Tp::Contact::PresenceState state,
const Tp::Channel::GroupMemberChangeDetails &details);
+
+ void publishStateChanged(Tp::Contact::PresenceState state, const QString &message);
+ // deprecated
void publishStateChanged(Tp::Contact::PresenceState state,
const Tp::Channel::GroupMemberChangeDetails &details);
+
+ void blockStatusChanged(bool blocked);
+ // deprecated
void blockStatusChanged(bool blocked, const Tp::Channel::GroupMemberChangeDetails &details);
void addedToGroup(const QString &group);
@@ -183,7 +196,13 @@ Q_SIGNALS:
// with that contact getting the same features requested as the current one. Or would we rather
// want to signal that change right away with a handle?
+protected:
+ // FIXME: (API/ABI break) Remove connectNotify
+ void connectNotify(const char *);
+
private:
+ static const Feature FeatureRosterGroups;
+
Contact(ContactManager *manager, const ReferencedHandles &handle,
const Features &requestedFeatures, const QVariantMap &attributes);
@@ -198,17 +217,16 @@ private:
void receiveLocation(const QVariantMap &location);
void receiveInfo(const ContactInfoFieldList &info);
- void setSubscriptionState(PresenceState state, const Channel::GroupMemberChangeDetails &details =
- Channel::GroupMemberChangeDetails());
- void setPublishState(PresenceState state, const Channel::GroupMemberChangeDetails &details =
- Channel::GroupMemberChangeDetails());
- void setBlocked(bool value, const Channel::GroupMemberChangeDetails &details =
- Channel::GroupMemberChangeDetails());
+ static PresenceState subscriptionStateToPresenceState(uint subscriptionState);
+ void setSubscriptionState(SubscriptionState state);
+ void setPublishState(SubscriptionState state, const QString &message = QString());
+ void setBlocked(bool value);
void setAddedToGroup(const QString &group);
void setRemovedFromGroup(const QString &group);
struct Private;
+ friend class Connection;
friend class ContactManager;
friend struct Private;
Private *mPriv;
diff --git a/TelepathyQt4/referenced-handles.h b/TelepathyQt4/referenced-handles.h
index 4704b10f..7da5624e 100644
--- a/TelepathyQt4/referenced-handles.h
+++ b/TelepathyQt4/referenced-handles.h
@@ -241,6 +241,7 @@ public:
private:
// For access to the "prime" constructor
+ friend class ContactManager;
friend class PendingContactAttributes;
friend class PendingContacts;
friend class PendingHandles;