diff options
Diffstat (limited to 'Swift/Controllers/Roster/RosterController.cpp')
-rw-r--r-- | Swift/Controllers/Roster/RosterController.cpp | 582 |
1 files changed, 321 insertions, 261 deletions
diff --git a/Swift/Controllers/Roster/RosterController.cpp b/Swift/Controllers/Roster/RosterController.cpp index d277799..90c5ce1 100644 --- a/Swift/Controllers/Roster/RosterController.cpp +++ b/Swift/Controllers/Roster/RosterController.cpp @@ -1,359 +1,419 @@ /* - * Copyright (c) 2010-2013 Kevin Smith - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2018 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ -#include "Swift/Controllers/Roster/RosterController.h" +#include <Swift/Controllers/Roster/RosterController.h> + +#include <memory> #include <boost/bind.hpp> -#include <boost/smart_ptr/make_shared.hpp> - -#include "Swiften/JID/JID.h" -#include "Swiften/Base/foreach.h" -#include "Swift/Controllers/UIInterfaces/MainWindow.h" -#include "Swift/Controllers/UIInterfaces/MainWindowFactory.h" -#include "Swiften/Client/NickResolver.h" -#include "Swiften/Roster/GetRosterRequest.h" -#include "Swiften/Roster/SetRosterRequest.h" -#include "Swift/Controllers/XMPPEvents/SubscriptionRequestEvent.h" -#include "Swift/Controllers/XMPPEvents/ErrorEvent.h" -#include "Swiften/Presence/PresenceOracle.h" -#include "Swiften/Presence/SubscriptionManager.h" -#include "Swift/Controllers/XMPPEvents/EventController.h" -#include "Swiften/Queries/IQRouter.h" -#include "Swift/Controllers/Roster/Roster.h" -#include "Swift/Controllers/Roster/SetPresence.h" -#include "Swift/Controllers/Roster/AppearOffline.h" -#include "Swift/Controllers/Roster/SetAvatar.h" -#include "Swift/Controllers/Roster/SetName.h" -#include "Swift/Controllers/Roster/OfflineRosterFilter.h" -#include "Swift/Controllers/Roster/GroupRosterItem.h" -#include "Swiften/Roster/XMPPRoster.h" -#include "Swiften/Roster/XMPPRosterItem.h" -#include "Swift/Controllers/UIEvents/AddContactUIEvent.h" -#include "Swift/Controllers/UIEvents/RemoveRosterItemUIEvent.h" -#include "Swift/Controllers/UIEvents/RenameRosterItemUIEvent.h" -#include "Swift/Controllers/UIEvents/RenameGroupUIEvent.h" -#include "Swift/Controllers/UIEvents/SendFileUIEvent.h" -#include <Swiften/FileTransfer/FileTransferManager.h> -#include <Swiften/Client/NickManager.h> -#include <Swift/Controllers/Intl.h> -#include <Swiften/Base/format.h> + +#include <Swiften/Avatars/AvatarManager.h> #include <Swiften/Base/Path.h> -#include <Swiften/Elements/DiscoInfo.h> +#include <Swiften/Base/format.h> +#include <Swiften/Client/ClientBlockListManager.h> +#include <Swiften/Client/NickManager.h> +#include <Swiften/Client/NickResolver.h> #include <Swiften/Disco/EntityCapsManager.h> +#include <Swiften/Disco/FeatureOracle.h> +#include <Swiften/Elements/DiscoInfo.h> +#include <Swiften/FileTransfer/FileTransferManager.h> +#include <Swiften/JID/JID.h> #include <Swiften/Jingle/JingleSessionManager.h> +#include <Swiften/Presence/PresenceOracle.h> +#include <Swiften/Presence/SubscriptionManager.h> +#include <Swiften/Queries/IQRouter.h> +#include <Swiften/Roster/GetRosterRequest.h> +#include <Swiften/Roster/SetRosterRequest.h> +#include <Swiften/Roster/XMPPRoster.h> +#include <Swiften/Roster/XMPPRosterItem.h> +#include <Swiften/VCards/VCardManager.h> + +#include <Swift/Controllers/Chat/Chattables.h> +#include <Swift/Controllers/Intl.h> +#include <Swift/Controllers/Roster/GroupRosterItem.h> +#include <Swift/Controllers/Roster/ItemOperations/AppearOffline.h> +#include <Swift/Controllers/Roster/ItemOperations/SetAvailableFeatures.h> +#include <Swift/Controllers/Roster/ItemOperations/SetAvatar.h> +#include <Swift/Controllers/Roster/ItemOperations/SetBlockingState.h> +#include <Swift/Controllers/Roster/ItemOperations/SetName.h> +#include <Swift/Controllers/Roster/ItemOperations/SetPresence.h> +#include <Swift/Controllers/Roster/ItemOperations/SetVCard.h> +#include <Swift/Controllers/Roster/OfflineRosterFilter.h> +#include <Swift/Controllers/Roster/Roster.h> +#include <Swift/Controllers/Roster/RosterGroupExpandinessPersister.h> +#include <Swift/Controllers/Roster/RosterVCardProvider.h> #include <Swift/Controllers/SettingConstants.h> -#include <Swiften/Client/ClientBlockListManager.h> +#include <Swift/Controllers/UIEvents/AddContactUIEvent.h> +#include <Swift/Controllers/UIEvents/RemoveRosterItemUIEvent.h> +#include <Swift/Controllers/UIEvents/RenameGroupUIEvent.h> +#include <Swift/Controllers/UIEvents/RenameRosterItemUIEvent.h> +#include <Swift/Controllers/UIEvents/UIEventStream.h> +#include <Swift/Controllers/UIInterfaces/MainWindow.h> +#include <Swift/Controllers/UIInterfaces/MainWindowFactory.h> +#include <Swift/Controllers/XMPPEvents/ErrorEvent.h> +#include <Swift/Controllers/XMPPEvents/EventController.h> +#include <Swift/Controllers/XMPPEvents/SubscriptionRequestEvent.h> namespace Swift { /** * The controller does not gain ownership of these parameters. */ -RosterController::RosterController(const JID& jid, XMPPRoster* xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickManager* nickManager, NickResolver* nickResolver, PresenceOracle* presenceOracle, SubscriptionManager* subscriptionManager, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter, SettingsProvider* settings, EntityCapsProvider* entityCapsManager, FileTransferOverview* fileTransferOverview, ClientBlockListManager* clientBlockListManager) - : myJID_(jid), xmppRoster_(xmppRoster), mainWindowFactory_(mainWindowFactory), mainWindow_(mainWindowFactory_->createMainWindow(uiEventStream)), roster_(new Roster()), offlineFilter_(new OfflineRosterFilter()), nickManager_(nickManager), nickResolver_(nickResolver), uiEventStream_(uiEventStream), entityCapsManager_(entityCapsManager), ftOverview_(fileTransferOverview), clientBlockListManager_(clientBlockListManager) { - assert(fileTransferOverview); - iqRouter_ = iqRouter; - presenceOracle_ = presenceOracle; - subscriptionManager_ = subscriptionManager; - eventController_ = eventController; - settings_ = settings; - expandiness_ = new RosterGroupExpandinessPersister(roster_, settings); - mainWindow_->setRosterModel(roster_); - - changeStatusConnection_ = mainWindow_->onChangeStatusRequest.connect(boost::bind(&RosterController::handleChangeStatusRequest, this, _1, _2)); - signOutConnection_ = mainWindow_->onSignOutRequest.connect(boost::bind(boost::ref(onSignOutRequest))); - xmppRoster_->onJIDAdded.connect(boost::bind(&RosterController::handleOnJIDAdded, this, _1)); - xmppRoster_->onJIDUpdated.connect(boost::bind(&RosterController::handleOnJIDUpdated, this, _1, _2, _3)); - xmppRoster_->onJIDRemoved.connect(boost::bind(&RosterController::handleOnJIDRemoved, this, _1)); - xmppRoster_->onRosterCleared.connect(boost::bind(&RosterController::handleRosterCleared, this)); - subscriptionManager_->onPresenceSubscriptionRequest.connect(boost::bind(&RosterController::handleSubscriptionRequest, this, _1, _2)); - presenceOracle_->onPresenceChange.connect(boost::bind(&RosterController::handleIncomingPresence, this, _1)); - uiEventConnection_ = uiEventStream->onUIEvent.connect(boost::bind(&RosterController::handleUIEvent, this, _1)); - avatarManager_ = avatarManager; - avatarManager_->onAvatarChanged.connect(boost::bind(&RosterController::handleAvatarChanged, this, _1)); - mainWindow_->setMyAvatarPath(pathToString(avatarManager_->getAvatarPath(myJID_))); - - nickManager_->onOwnNickChanged.connect(boost::bind(&MainWindow::setMyNick, mainWindow_, _1)); - mainWindow_->setMyJID(jid); - mainWindow_->setMyNick(nickManager_->getOwnNick()); - - entityCapsManager_->onCapsChanged.connect(boost::bind(&RosterController::handleOnCapsChanged, this, _1)); - - settings_->onSettingChanged.connect(boost::bind(&RosterController::handleSettingChanged, this, _1)); - - handleShowOfflineToggled(settings_->getSetting(SettingConstants::SHOW_OFFLINE)); +RosterController::RosterController(const JID& jid, XMPPRoster* xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickManager* nickManager, NickResolver* nickResolver, PresenceOracle* presenceOracle, SubscriptionManager* subscriptionManager, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter, SettingsProvider* settings, EntityCapsProvider* entityCapsManager, ClientBlockListManager* clientBlockListManager, VCardManager* vcardManager, Chattables& chattables) + : myJID_(jid), xmppRoster_(xmppRoster), mainWindowFactory_(mainWindowFactory), mainWindow_(mainWindowFactory_->createMainWindow(chattables, uiEventStream)), roster_(new Roster()), offlineFilter_(new OfflineRosterFilter()), vcardManager_(vcardManager), avatarManager_(avatarManager), nickManager_(nickManager), nickResolver_(nickResolver), presenceOracle_(presenceOracle), uiEventStream_(uiEventStream), entityCapsManager_(entityCapsManager), clientBlockListManager_(clientBlockListManager), chattables_(chattables) { + iqRouter_ = iqRouter; + subscriptionManager_ = subscriptionManager; + eventController_ = eventController; + settings_ = settings; + expandiness_ = new RosterGroupExpandinessPersister(roster_, settings); +#ifndef NOT_YET + mainWindow_->setRosterModel(roster_); +#endif + rosterVCardProvider_ = new RosterVCardProvider(roster_, vcardManager, JID::WithoutResource); + + changeStatusConnection_ = mainWindow_->onChangeStatusRequest.connect(boost::bind(&RosterController::handleChangeStatusRequest, this, _1, _2)); + signOutConnection_ = mainWindow_->onSignOutRequest.connect(boost::bind(boost::ref(onSignOutRequest))); + xmppRoster_->onJIDAdded.connect(boost::bind(&RosterController::handleOnJIDAdded, this, _1)); + xmppRoster_->onJIDUpdated.connect(boost::bind(&RosterController::handleOnJIDUpdated, this, _1, _2, _3)); + xmppRoster_->onJIDRemoved.connect(boost::bind(&RosterController::handleOnJIDRemoved, this, _1)); + xmppRoster_->onRosterCleared.connect(boost::bind(&RosterController::handleRosterCleared, this)); + subscriptionManager_->onPresenceSubscriptionRequest.connect(boost::bind(&RosterController::handleSubscriptionRequest, this, _1, _2)); + uiEventConnection_ = uiEventStream->onUIEvent.connect(boost::bind(&RosterController::handleUIEvent, this, _1)); + + featureOracle_ = std::make_unique<FeatureOracle>(entityCapsManager_, presenceOracle_); + + vcardManager_->onOwnVCardChanged.connect(boost::bind(&RosterController::handleOwnVCardChanged, this, _1)); + avatarManager_->onAvatarChanged.connect(boost::bind(&RosterController::handleAvatarChanged, this, _1)); + presenceOracle_->onPresenceChange.connect(boost::bind(&RosterController::handlePresenceChanged, this, _1)); + mainWindow_->setMyAvatarPath(pathToString(avatarManager_->getAvatarPath(myJID_.toBare()))); + + nickManager_->onOwnNickChanged.connect(boost::bind(&MainWindow::setMyNick, mainWindow_, _1)); + mainWindow_->setMyJID(jid); + mainWindow_->setMyNick(nickManager_->getOwnNick()); + + entityCapsManager_->onCapsChanged.connect(boost::bind(&RosterController::handleOnCapsChanged, this, _1)); + + settings_->onSettingChanged.connect(boost::bind(&RosterController::handleSettingChanged, this, _1)); + + handleShowOfflineToggled(settings_->getSetting(SettingConstants::SHOW_OFFLINE)); + + ownContact_ = std::make_shared<ContactRosterItem>(myJID_.toBare(), myJID_.toBare(), nickManager_->getOwnNick(), static_cast<GroupRosterItem*>(nullptr)); + ownContact_->setVCard(vcardManager_->getVCard(myJID_.toBare())); + ownContact_->setAvatarPath(pathToString(avatarManager_->getAvatarPath(myJID_.toBare()))); + mainWindow_->setMyContactRosterItem(ownContact_); } -RosterController::~RosterController() { - settings_->onSettingChanged.disconnect(boost::bind(&RosterController::handleSettingChanged, this, _1)); - nickManager_->onOwnNickChanged.disconnect(boost::bind(&MainWindow::setMyNick, mainWindow_, _1)); - - delete offlineFilter_; - delete expandiness_; +RosterController::~RosterController() { + settings_->onSettingChanged.disconnect(boost::bind(&RosterController::handleSettingChanged, this, _1)); + nickManager_->onOwnNickChanged.disconnect(boost::bind(&MainWindow::setMyNick, mainWindow_, _1)); - mainWindow_->setRosterModel(NULL); - if (mainWindow_->canDelete()) { - delete mainWindow_; - } - delete roster_; - + delete offlineFilter_; + delete expandiness_; + + mainWindow_->setRosterModel(nullptr); + if (mainWindow_->canDelete()) { + delete mainWindow_; + } + delete rosterVCardProvider_; + delete roster_; } void RosterController::setEnabled(bool enabled) { - if (!enabled) { - roster_->applyOnItems(AppearOffline()); - } + if (!enabled) { + roster_->applyOnItems(AppearOffline()); + } } void RosterController::handleShowOfflineToggled(bool state) { - if (state) { - roster_->removeFilter(offlineFilter_); - } else { - roster_->addFilter(offlineFilter_); - } + if (state) { + roster_->removeFilter(offlineFilter_); + } else { + roster_->addFilter(offlineFilter_); + } } void RosterController::handleChangeStatusRequest(StatusShow::Type show, const std::string &statusText) { - onChangeStatusRequest(show, statusText); + onChangeStatusRequest(show, statusText); } void RosterController::handleOnJIDAdded(const JID& jid) { - std::vector<std::string> groups = xmppRoster_->getGroupsForJID(jid); - std::string name = nickResolver_->jidToNick(jid); - if (!groups.empty()) { - foreach(const std::string& group, groups) { - roster_->addContact(jid, jid, name, group, avatarManager_->getAvatarPath(jid)); - } - } - else { - roster_->addContact(jid, jid, name, QT_TRANSLATE_NOOP("", "Contacts"), avatarManager_->getAvatarPath(jid)); - } - applyAllPresenceTo(jid); + std::vector<std::string> groups = xmppRoster_->getGroupsForJID(jid); + std::string name = nickResolver_->jidToNick(jid); + if (!groups.empty()) { + for (const auto& group : groups) { + roster_->addContact(jid, jid, name, group, avatarManager_->getAvatarPath(jid)); + } + } + else { + roster_->addContact(jid, jid, name, QT_TRANSLATE_NOOP("", "Contacts"), avatarManager_->getAvatarPath(jid)); + } + applyAllPresenceTo(jid); + + chattables_.addJID(jid, Chattables::State::Type::Person); + auto state = chattables_.getState(jid); + state.name = name; + chattables_.setState(jid, state); } void RosterController::applyAllPresenceTo(const JID& jid) { - foreach (Presence::ref presence, presenceOracle_->getAllPresence(jid)) { - roster_->applyOnItems(SetPresence(presence)); - } + for (auto&& presence : presenceOracle_->getAllPresence(jid)) { + roster_->applyOnItems(SetPresence(presence)); + } } void RosterController::handleRosterCleared() { - roster_->removeAll(); + roster_->removeAll(); } void RosterController::handleOnJIDRemoved(const JID& jid) { - roster_->removeContact(jid); + roster_->removeContact(jid); } void RosterController::handleOnJIDUpdated(const JID& jid, const std::string& oldName, const std::vector<std::string>& passedOldGroups) { - if (oldName != xmppRoster_->getNameForJID(jid)) { - roster_->applyOnItems(SetName(nickResolver_->jidToNick(jid), jid)); - } - std::vector<std::string> groups = xmppRoster_->getGroupsForJID(jid); - std::vector<std::string> oldGroups = passedOldGroups; - std::string name = nickResolver_->jidToNick(jid); - std::string contactsGroup = QT_TRANSLATE_NOOP("", "Contacts"); - if (oldGroups.empty()) { - oldGroups.push_back(contactsGroup); - } - if (groups.empty()) { - groups.push_back(contactsGroup); - } - foreach(const std::string& group, groups) { - if (std::find(oldGroups.begin(), oldGroups.end(), group) == oldGroups.end()) { - roster_->addContact(jid, jid, name, group, avatarManager_->getAvatarPath(jid)); - } - } - foreach(const std::string& group, oldGroups) { - if (std::find(groups.begin(), groups.end(), group) == groups.end()) { - roster_->removeContactFromGroup(jid, group); - if (roster_->getGroup(group)->getChildren().size() == 0) { - roster_->removeGroup(group); - } - } - } - applyAllPresenceTo(jid); + if (oldName != xmppRoster_->getNameForJID(jid)) { + roster_->applyOnItems(SetName(nickResolver_->jidToNick(jid), jid)); + } + std::vector<std::string> groups = xmppRoster_->getGroupsForJID(jid); + std::vector<std::string> oldGroups = passedOldGroups; + std::string name = nickResolver_->jidToNick(jid); + std::string contactsGroup = QT_TRANSLATE_NOOP("", "Contacts"); + if (oldGroups.empty()) { + oldGroups.push_back(contactsGroup); + } + if (groups.empty()) { + groups.push_back(contactsGroup); + } + for (const auto& group : groups) { + if (std::find(oldGroups.begin(), oldGroups.end(), group) == oldGroups.end()) { + roster_->addContact(jid, jid, name, group, avatarManager_->getAvatarPath(jid)); + } + } + for (const auto& group : oldGroups) { + if (std::find(groups.begin(), groups.end(), group) == groups.end()) { + roster_->removeContactFromGroup(jid, group); + if (roster_->getGroup(group)->getChildren().size() == 0) { + roster_->removeGroup(group); + } + } + } + applyAllPresenceTo(jid); } void RosterController::handleSettingChanged(const std::string& settingPath) { - if (settingPath == SettingConstants::SHOW_OFFLINE.getKey()) { - handleShowOfflineToggled(settings_->getSetting(SettingConstants::SHOW_OFFLINE)); - } + if (settingPath == SettingConstants::SHOW_OFFLINE.getKey()) { + handleShowOfflineToggled(settings_->getSetting(SettingConstants::SHOW_OFFLINE)); + } } void RosterController::handleBlockingStateChanged() { - if (clientBlockListManager_->getBlockList()->getState() == BlockList::Available) { - roster_->setBlockedState(clientBlockListManager_->getBlockList()->getItems(), ContactRosterItem::IsBlocked); - } + if (clientBlockListManager_->getBlockList()->getState() == BlockList::Available) { + for (const auto& jid : clientBlockListManager_->getBlockList()->getItems()) { + roster_->applyOnItems(SetBlockingState(jid, ContactRosterItem::IsBlocked)); + } + } } void RosterController::handleBlockingItemAdded(const JID& jid) { - roster_->setBlockedState(std::vector<JID>(1, jid), ContactRosterItem::IsBlocked); + roster_->applyOnItems(SetBlockingState(jid, ContactRosterItem::IsBlocked)); } void RosterController::handleBlockingItemRemoved(const JID& jid) { - roster_->setBlockedState(std::vector<JID>(1, jid), ContactRosterItem::IsUnblocked); + roster_->applyOnItems(SetBlockingState(jid, ContactRosterItem::IsUnblocked)); } -void RosterController::handleUIEvent(boost::shared_ptr<UIEvent> event) { - if (boost::shared_ptr<AddContactUIEvent> addContactEvent = boost::dynamic_pointer_cast<AddContactUIEvent>(event)) { - RosterItemPayload item; - item.setName(addContactEvent->getName()); - item.setJID(addContactEvent->getJID()); - item.setGroups(std::vector<std::string>(addContactEvent->getGroups().begin(), addContactEvent->getGroups().end())); - boost::shared_ptr<RosterPayload> roster(new RosterPayload()); - roster->addItem(item); - SetRosterRequest::ref request = SetRosterRequest::create(roster, iqRouter_); - request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster)); - request->send(); - subscriptionManager_->requestSubscription(addContactEvent->getJID()); - } - else if (boost::shared_ptr<RemoveRosterItemUIEvent> removeEvent = boost::dynamic_pointer_cast<RemoveRosterItemUIEvent>(event)) { - RosterItemPayload item(removeEvent->getJID(), "", RosterItemPayload::Remove); - boost::shared_ptr<RosterPayload> roster(new RosterPayload()); - roster->addItem(item); - SetRosterRequest::ref request = SetRosterRequest::create(roster, iqRouter_); - request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster)); - request->send(); - - } - else if (boost::shared_ptr<RenameRosterItemUIEvent> renameEvent = boost::dynamic_pointer_cast<RenameRosterItemUIEvent>(event)) { - JID contact(renameEvent->getJID()); - RosterItemPayload item(contact, renameEvent->getNewName(), xmppRoster_->getSubscriptionStateForJID(contact)); - item.setGroups(xmppRoster_->getGroupsForJID(contact)); - boost::shared_ptr<RosterPayload> roster(new RosterPayload()); - roster->addItem(item); - SetRosterRequest::ref request = SetRosterRequest::create(roster, iqRouter_); - request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster)); - request->send(); - } - else if (boost::shared_ptr<RenameGroupUIEvent> renameGroupEvent = boost::dynamic_pointer_cast<RenameGroupUIEvent>(event)) { - std::vector<XMPPRosterItem> items = xmppRoster_->getItems(); - std::string group = renameGroupEvent->getGroup(); - // FIXME: We should handle contacts groups specially to avoid clashes - if (group == QT_TRANSLATE_NOOP("", "Contacts")) { - group = ""; - } - foreach(XMPPRosterItem& item, items) { - std::vector<std::string> groups = item.getGroups(); - if ( (group.empty() && groups.empty()) || std::find(groups.begin(), groups.end(), group) != groups.end()) { - groups.erase(std::remove(groups.begin(), groups.end(), group), groups.end()); - if (std::find(groups.begin(), groups.end(), renameGroupEvent->getNewName()) == groups.end()) { - groups.push_back(renameGroupEvent->getNewName()); - } - item.setGroups(groups); - updateItem(item); - } - } - } - else if (boost::shared_ptr<SendFileUIEvent> sendFileEvent = boost::dynamic_pointer_cast<SendFileUIEvent>(event)) { - //TODO add send file dialog to ChatView of receipient jid - ftOverview_->sendFile(sendFileEvent->getJID(), sendFileEvent->getFilename()); - } +void RosterController::handleUIEvent(std::shared_ptr<UIEvent> event) { + if (std::shared_ptr<AddContactUIEvent> addContactEvent = std::dynamic_pointer_cast<AddContactUIEvent>(event)) { + RosterItemPayload item; + item.setName(addContactEvent->getName()); + item.setJID(addContactEvent->getJID()); + item.setGroups(std::vector<std::string>(addContactEvent->getGroups().begin(), addContactEvent->getGroups().end())); + std::shared_ptr<RosterPayload> roster(new RosterPayload()); + roster->addItem(item); + SetRosterRequest::ref request = SetRosterRequest::create(roster, iqRouter_); + request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster)); + request->send(); + subscriptionManager_->requestSubscription(addContactEvent->getJID()); + } + else if (std::shared_ptr<RemoveRosterItemUIEvent> removeEvent = std::dynamic_pointer_cast<RemoveRosterItemUIEvent>(event)) { + RosterItemPayload item(removeEvent->getJID(), "", RosterItemPayload::Remove); + std::shared_ptr<RosterPayload> roster(new RosterPayload()); + roster->addItem(item); + SetRosterRequest::ref request = SetRosterRequest::create(roster, iqRouter_); + request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster)); + request->send(); + + } + else if (std::shared_ptr<RenameRosterItemUIEvent> renameEvent = std::dynamic_pointer_cast<RenameRosterItemUIEvent>(event)) { + JID contact(renameEvent->getJID()); + RosterItemPayload item(contact, renameEvent->getNewName(), xmppRoster_->getSubscriptionStateForJID(contact)); + item.setGroups(xmppRoster_->getGroupsForJID(contact)); + std::shared_ptr<RosterPayload> roster(new RosterPayload()); + roster->addItem(item); + SetRosterRequest::ref request = SetRosterRequest::create(roster, iqRouter_); + request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster)); + request->send(); + } + else if (std::shared_ptr<RenameGroupUIEvent> renameGroupEvent = std::dynamic_pointer_cast<RenameGroupUIEvent>(event)) { + std::vector<XMPPRosterItem> items = xmppRoster_->getItems(); + std::string group = renameGroupEvent->getGroup(); + // FIXME: We should handle contacts groups specially to avoid clashes + if (group == QT_TRANSLATE_NOOP("", "Contacts")) { + group = ""; + } + for (auto& item : items) { + std::vector<std::string> groups = item.getGroups(); + if ( (group.empty() && groups.empty()) || std::find(groups.begin(), groups.end(), group) != groups.end()) { + groups.erase(std::remove(groups.begin(), groups.end(), group), groups.end()); + if (std::find(groups.begin(), groups.end(), renameGroupEvent->getNewName()) == groups.end()) { + groups.push_back(renameGroupEvent->getNewName()); + } + item.setGroups(groups); + updateItem(item); + } + } + } } void RosterController::setContactGroups(const JID& jid, const std::vector<std::string>& groups) { - updateItem(XMPPRosterItem(jid, xmppRoster_->getNameForJID(jid), groups, xmppRoster_->getSubscriptionStateForJID(jid))); + updateItem(XMPPRosterItem(jid, xmppRoster_->getNameForJID(jid), groups, xmppRoster_->getSubscriptionStateForJID(jid))); } void RosterController::updateItem(const XMPPRosterItem& item) { - RosterItemPayload itemPayload(item.getJID(), item.getName(), item.getSubscription()); - itemPayload.setGroups(item.getGroups()); + RosterItemPayload itemPayload(item.getJID(), item.getName(), item.getSubscription()); + itemPayload.setGroups(item.getGroups()); - RosterPayload::ref roster = boost::make_shared<RosterPayload>(); - roster->addItem(itemPayload); + RosterPayload::ref roster = std::make_shared<RosterPayload>(); + roster->addItem(itemPayload); - SetRosterRequest::ref request = SetRosterRequest::create(roster, iqRouter_); - request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster)); - request->send(); + SetRosterRequest::ref request = SetRosterRequest::create(roster, iqRouter_); + request->onResponse.connect(boost::bind(&RosterController::handleRosterItemUpdated, this, _1, roster)); + request->send(); } void RosterController::initBlockingCommand() { - boost::shared_ptr<BlockList> blockList = clientBlockListManager_->getBlockList(); - - blockingOnStateChangedConnection_ = blockList->onStateChanged.connect(boost::bind(&RosterController::handleBlockingStateChanged, this)); - blockingOnItemAddedConnection_ = blockList->onItemAdded.connect(boost::bind(&RosterController::handleBlockingItemAdded, this, _1)); - blockingOnItemRemovedConnection_ = blockList->onItemRemoved.connect(boost::bind(&RosterController::handleBlockingItemRemoved, this, _1)); + std::shared_ptr<BlockList> blockList = clientBlockListManager_->requestBlockList(); + + blockingOnStateChangedConnection_ = blockList->onStateChanged.connect(boost::bind(&RosterController::handleBlockingStateChanged, this)); + blockingOnItemAddedConnection_ = blockList->onItemAdded.connect(boost::bind(&RosterController::handleBlockingItemAdded, this, _1)); + blockingOnItemRemovedConnection_ = blockList->onItemRemoved.connect(boost::bind(&RosterController::handleBlockingItemRemoved, this, _1)); + roster_->setBlockingSupported(true); + if (blockList->getState() == BlockList::Available) { + for (const auto& jid : blockList->getItems()) { + roster_->applyOnItems(SetBlockingState(jid, ContactRosterItem::IsBlocked)); + } + } +} - if (blockList->getState() == BlockList::Available) { - roster_->setBlockedState(blockList->getItems(), ContactRosterItem::IsBlocked); - } +void RosterController::handleRosterItemUpdated(ErrorPayload::ref error, std::shared_ptr<RosterPayload> rosterPayload) { + if (!!error) { + handleRosterSetError(error, rosterPayload); + } + std::shared_ptr<BlockList> blockList = clientBlockListManager_->getBlockList(); + std::vector<RosterItemPayload> items = rosterPayload->getItems(); + if (blockList->getState() == BlockList::Available && items.size() > 0) { + std::vector<JID> jids = blockList->getItems(); + if (std::find(jids.begin(), jids.end(), items[0].getJID()) != jids.end()) { + roster_->applyOnItems(SetBlockingState(items[0].getJID(), ContactRosterItem::IsBlocked)); + } + } } -void RosterController::handleRosterSetError(ErrorPayload::ref error, boost::shared_ptr<RosterPayload> rosterPayload) { - if (!error) { - return; - } - std::string text = str(format(QT_TRANSLATE_NOOP("", "Server %1% rejected contact list change to item '%2%'")) % myJID_.getDomain() % rosterPayload->getItems()[0].getJID().toString()); - if (!error->getText().empty()) { - text += ": " + error->getText(); - } - boost::shared_ptr<ErrorEvent> errorEvent(new ErrorEvent(JID(myJID_.getDomain()), text)); - eventController_->handleIncomingEvent(errorEvent); +void RosterController::handleRosterSetError(ErrorPayload::ref error, std::shared_ptr<RosterPayload> rosterPayload) { + if (!error) { + return; + } + std::string text = str(format(QT_TRANSLATE_NOOP("", "Server %1% rejected contact list change to item '%2%'")) % myJID_.getDomain() % rosterPayload->getItems()[0].getJID().toString()); + if (!error->getText().empty()) { + text += ": " + error->getText(); + } + std::shared_ptr<ErrorEvent> errorEvent(new ErrorEvent(JID(myJID_.getDomain()), text)); + eventController_->handleIncomingEvent(errorEvent); } void RosterController::handleIncomingPresence(Presence::ref newPresence) { - if (newPresence->getType() == Presence::Error) { - return; - } - roster_->applyOnItems(SetPresence(newPresence)); + if (newPresence->getType() == Presence::Error) { + return; + } + auto bareFrom = newPresence->getFrom().toBare(); + Presence::ref accountPresence = presenceOracle_->getAccountPresence(bareFrom); + if (!accountPresence) { + accountPresence = Presence::create(); + accountPresence->setFrom(newPresence->getFrom()); + accountPresence->setType(Presence::Unavailable); + } + roster_->applyOnItems(SetPresence(accountPresence)); + auto state = chattables_.getState(bareFrom); + state.status = accountPresence->getShow(); + chattables_.setState(bareFrom, state); } void RosterController::handleSubscriptionRequest(const JID& jid, const std::string& message) { - if (xmppRoster_->containsJID(jid) && (xmppRoster_->getSubscriptionStateForJID(jid) == RosterItemPayload::To || xmppRoster_->getSubscriptionStateForJID(jid) == RosterItemPayload::Both)) { - subscriptionManager_->confirmSubscription(jid); - return; - } - SubscriptionRequestEvent* eventPointer = new SubscriptionRequestEvent(jid, message); - eventPointer->onAccept.connect(boost::bind(&RosterController::handleSubscriptionRequestAccepted, this, eventPointer)); - eventPointer->onDecline.connect(boost::bind(&RosterController::handleSubscriptionRequestDeclined, this, eventPointer)); - boost::shared_ptr<StanzaEvent> event(eventPointer); - eventController_->handleIncomingEvent(event); + if (xmppRoster_->containsJID(jid) && (xmppRoster_->getSubscriptionStateForJID(jid) == RosterItemPayload::To || xmppRoster_->getSubscriptionStateForJID(jid) == RosterItemPayload::Both)) { + subscriptionManager_->confirmSubscription(jid); + return; + } + SubscriptionRequestEvent* eventPointer = new SubscriptionRequestEvent(jid, message); + eventPointer->onAccept.connect(boost::bind(&RosterController::handleSubscriptionRequestAccepted, this, eventPointer)); + eventPointer->onDecline.connect(boost::bind(&RosterController::handleSubscriptionRequestDeclined, this, eventPointer)); + std::shared_ptr<StanzaEvent> event(eventPointer); + eventController_->handleIncomingEvent(event); } void RosterController::handleSubscriptionRequestAccepted(SubscriptionRequestEvent* event) { - subscriptionManager_->confirmSubscription(event->getJID()); - if (!xmppRoster_->containsJID(event->getJID()) || xmppRoster_->getSubscriptionStateForJID(event->getJID()) == RosterItemPayload::None || xmppRoster_->getSubscriptionStateForJID(event->getJID()) == RosterItemPayload::From) { - subscriptionManager_->requestSubscription(event->getJID()); - } + subscriptionManager_->confirmSubscription(event->getJID()); + if (!xmppRoster_->containsJID(event->getJID()) || xmppRoster_->getSubscriptionStateForJID(event->getJID()) == RosterItemPayload::None || xmppRoster_->getSubscriptionStateForJID(event->getJID()) == RosterItemPayload::From) { + subscriptionManager_->requestSubscription(event->getJID()); + } } void RosterController::handleSubscriptionRequestDeclined(SubscriptionRequestEvent* event) { - subscriptionManager_->cancelSubscription(event->getJID()); + subscriptionManager_->cancelSubscription(event->getJID()); +} + +void RosterController::handleOwnVCardChanged(VCard::ref vcard) { + ownContact_->setVCard(vcard); + mainWindow_->setMyContactRosterItem(ownContact_); } void RosterController::handleAvatarChanged(const JID& jid) { - boost::filesystem::path path = avatarManager_->getAvatarPath(jid); - roster_->applyOnItems(SetAvatar(jid, path)); - if (jid.equals(myJID_, JID::WithoutResource)) { - mainWindow_->setMyAvatarPath(pathToString(path)); - } + boost::filesystem::path path = avatarManager_->getAvatarPath(jid); + roster_->applyOnItems(SetAvatar(jid, path)); + if (jid.equals(myJID_, JID::WithoutResource)) { + mainWindow_->setMyAvatarPath(pathToString(path)); + ownContact_->setAvatarPath(pathToString(path)); + mainWindow_->setMyContactRosterItem(ownContact_); + } +} + +void RosterController::handlePresenceChanged(Presence::ref presence) { + if (presence->getFrom().equals(myJID_, JID::WithResource)) { + ownContact_->applyPresence(presence); + mainWindow_->setMyContactRosterItem(ownContact_); + } + handleIncomingPresence(presence); } boost::optional<XMPPRosterItem> RosterController::getItem(const JID& jid) const { - return xmppRoster_->getItem(jid); + return xmppRoster_->getItem(jid); } std::set<std::string> RosterController::getGroups() const { - return xmppRoster_->getGroups(); + return xmppRoster_->getGroups(); } void RosterController::handleOnCapsChanged(const JID& jid) { - DiscoInfo::ref info = entityCapsManager_->getCaps(jid); - if (info) { - std::set<ContactRosterItem::Feature> features; - if (info->hasFeature(DiscoInfo::JingleFeature) && info->hasFeature(DiscoInfo::JingleFTFeature) && info->hasFeature(DiscoInfo::JingleTransportsIBBFeature)) { - features.insert(ContactRosterItem::FileTransferFeature); - } - if (info->hasFeature(DiscoInfo::WhiteboardFeature)) { - features.insert(ContactRosterItem::WhiteboardFeature); - } - roster_->setAvailableFeatures(jid, features); - } + std::set<ContactRosterItem::Feature> features; + if (featureOracle_->isFileTransferSupported(jid.toBare()) == Tristate::Yes || featureOracle_->isFileTransferSupported(jid.toBare()) == Tristate::Maybe) { + features.insert(ContactRosterItem::FileTransferFeature); + } + if (featureOracle_->isWhiteboardSupported(jid.toBare()) == Tristate::Yes) { + features.insert(ContactRosterItem::WhiteboardFeature); + } + roster_->applyOnItems(SetAvailableFeatures(jid, features)); } } |