diff options
author | Andre Moreira Magalhaes (andrunko) <andre.magalhaes@collabora.co.uk> | 2010-12-31 11:22:05 -0200 |
---|---|---|
committer | Andre Moreira Magalhaes (andrunko) <andre.magalhaes@collabora.co.uk> | 2010-12-31 11:22:10 -0200 |
commit | 994a3c140969fab694288e9252dc31bcf09f73f9 (patch) | |
tree | ac3ed01b704152d6dcb6e5ab0579678d2ef7efe0 /TelepathyQt4 | |
parent | 5e2e943a37f4ae525ad7a69e73d47a1fb87f7148 (diff) | |
parent | a398ff89229a9599e0d21f5976cb00f2a459eca2 (diff) |
Merge branch 'contactlist'
Reviewed-by: Olli Salli (oggis) <olli.salli@collabora.co.uk>
Diffstat (limited to 'TelepathyQt4')
-rw-r--r-- | TelepathyQt4/channel.h | 2 | ||||
-rw-r--r-- | TelepathyQt4/connection.cpp | 268 | ||||
-rw-r--r-- | TelepathyQt4/connection.h | 11 | ||||
-rw-r--r-- | TelepathyQt4/contact-manager.cpp | 1112 | ||||
-rw-r--r-- | TelepathyQt4/contact-manager.h | 42 | ||||
-rw-r--r-- | TelepathyQt4/contact.cpp | 161 | ||||
-rw-r--r-- | TelepathyQt4/contact.h | 30 | ||||
-rw-r--r-- | TelepathyQt4/referenced-handles.h | 1 |
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; |