diff options
| author | Tobias Markmann <tm@ayena.de> | 2016-04-21 09:02:07 (GMT) | 
|---|---|---|
| committer | Kevin Smith <kevin.smith@isode.com> | 2016-11-10 11:09:16 (GMT) | 
| commit | d2ba1ab8a36333523bf794c23226bd044e1717c2 (patch) | |
| tree | 737ac14bc1dfcec4b80192ea23e7b0e860a2f8d3 /Swift/Controllers | |
| parent | 41c771ec02682c2b344263d29f68eb6452c42dbe (diff) | |
| download | swift-d2ba1ab8a36333523bf794c23226bd044e1717c2.zip swift-d2ba1ab8a36333523bf794c23226bd044e1717c2.tar.bz2 | |
Use FeatureOracle to detect file-transfer support in roster
The FeatureOracle provides tri-state feature lookup
functionality for bare JIDs. It returns Yes if a feature is
supported by all resources of the bare JID, Maybe if some
support it, and No if none of the resources support it.
If passed a full JID, it returns the specific features supported
by that end-point.
Sending a file to a bare JID, will send a file to the resource
of the bare JID with the highest availability by presence, show
status and priority and which supports the features required
for a Jingle file-transfer.
Test-Information:
Added unit test verifying new behavior.
All tests pass on OS X 10.11.6. Added new unit tests for
FeatureOracle.
Manually verified that the roster and chat window both use
the same mechanism to detect support for file-transfers.
Manually verified that file-transfers via the contact list
goes to already bound full JIDs if there is an existing
ChatController.
Change-Id: I0175ac42ecb73f1d54f9c96ffbba773eb5e24296
Diffstat (limited to 'Swift/Controllers')
| -rw-r--r-- | Swift/Controllers/Chat/ChatsManager.cpp | 21 | ||||
| -rw-r--r-- | Swift/Controllers/MainController.cpp | 5 | ||||
| -rw-r--r-- | Swift/Controllers/Roster/RosterController.cpp | 30 | ||||
| -rw-r--r-- | Swift/Controllers/Roster/RosterController.h | 6 | ||||
| -rw-r--r-- | Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp | 135 | 
5 files changed, 139 insertions, 58 deletions
| diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp index 232d903..b9e2cf4 100644 --- a/Swift/Controllers/Chat/ChatsManager.cpp +++ b/Swift/Controllers/Chat/ChatsManager.cpp @@ -24,6 +24,7 @@  #include <Swiften/Client/NickResolver.h>  #include <Swiften/Client/StanzaChannel.h>  #include <Swiften/Disco/DiscoServiceWalker.h> +#include <Swiften/Disco/FeatureOracle.h>  #include <Swiften/Elements/CarbonsReceived.h>  #include <Swiften/Elements/CarbonsSent.h>  #include <Swiften/Elements/ChatState.h> @@ -59,6 +60,7 @@  #include <Swift/Controllers/UIEvents/RemoveMUCBookmarkUIEvent.h>  #include <Swift/Controllers/UIEvents/RequestChatUIEvent.h>  #include <Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h> +#include <Swift/Controllers/UIEvents/SendFileUIEvent.h>  #include <Swift/Controllers/UIInterfaces/ChatListWindowFactory.h>  #include <Swift/Controllers/UIInterfaces/JoinMUCWindow.h>  #include <Swift/Controllers/UIInterfaces/JoinMUCWindowFactory.h> @@ -199,6 +201,7 @@ ChatsManager::~ChatsManager() {      roster_->onJIDRemoved.disconnect(boost::bind(&ChatsManager::handleJIDRemovedFromRoster, this, _1));      roster_->onJIDUpdated.disconnect(boost::bind(&ChatsManager::handleJIDUpdatedInRoster, this, _1));      roster_->onRosterCleared.disconnect(boost::bind(&ChatsManager::handleRosterCleared, this)); +    ftOverview_->onNewFileTransferController.disconnect(boost::bind(&ChatsManager::handleNewFileTransferController, this, _1));      delete joinMUCWindow_;      for (JIDChatControllerPair controllerPair : chatControllers_) {          delete controllerPair.second; @@ -562,6 +565,24 @@ void ChatsManager::handleUIEvent(std::shared_ptr<UIEvent> event) {          mucBookmarkManager_->addBookmark(addMUCBookmarkEvent->getBookmark());          return;      } +    std::shared_ptr<SendFileUIEvent> sendFileEvent = std::dynamic_pointer_cast<SendFileUIEvent>(event); +    if (sendFileEvent) { +        JID fileReceiver = sendFileEvent->getJID(); +        if (fileReceiver.isBare()) { +            // See if there is a chat controller for a conversation with a bound +            // full JID. Check if this JID supports file transfer and use it instead +            // of the bare JID. +            ChatController* controller = getChatControllerIfExists(fileReceiver, false); +            if (controller) { +                JID controllerJID = controller->getToJID(); +                if (!controllerJID.isBare() && (FeatureOracle(entityCapsProvider_, presenceOracle_).isFileTransferSupported(controllerJID) == Yes)) { +                    fileReceiver = controllerJID; +                } +            } +        } +        ftOverview_->sendFile(fileReceiver, sendFileEvent->getFilename()); +        return; +    }      std::shared_ptr<CreateImpromptuMUCUIEvent> createImpromptuMUCEvent = std::dynamic_pointer_cast<CreateImpromptuMUCUIEvent>(event);      if (createImpromptuMUCEvent) { diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp index a9d3f5c..0d9f1b8 100644 --- a/Swift/Controllers/MainController.cpp +++ b/Swift/Controllers/MainController.cpp @@ -15,7 +15,6 @@  #include <Swiften/Base/Algorithm.h>  #include <Swiften/Base/Log.h>  #include <Swiften/Base/String.h> -#include <Swiften/Base/foreach.h>  #include <Swiften/Base/format.h>  #include <Swiften/Client/Client.h>  #include <Swiften/Client/ClientBlockListManager.h> @@ -180,7 +179,7 @@ MainController::MainController(      ClientOptions cachedOptions;      bool eagle = settings_->getSetting(SettingConstants::FORGET_PASSWORDS);      if (!eagle) { -        foreach (std::string profile, settings->getAvailableProfiles()) { +        for (auto&& profile : settings->getAvailableProfiles()) {              ProfileSettingsProvider profileSettings(profile, settings);              std::string password = profileSettings.getStringSetting("pass");              std::string certificate = profileSettings.getStringSetting("certificate"); @@ -349,7 +348,7 @@ void MainController::handleConnected() {          showProfileController_ = new ShowProfileController(client_->getVCardManager(), uiFactory_, uiEventStream_);          ftOverview_ = new FileTransferOverview(client_->getFileTransferManager());          fileTransferListController_->setFileTransferOverview(ftOverview_); -        rosterController_ = new RosterController(boundJID_, client_->getRoster(), client_->getAvatarManager(), uiFactory_, client_->getNickManager(), client_->getNickResolver(), client_->getPresenceOracle(), client_->getSubscriptionManager(), eventController_, uiEventStream_, client_->getIQRouter(), settings_, client_->getEntityCapsProvider(), ftOverview_, client_->getClientBlockListManager(), client_->getVCardManager()); +        rosterController_ = new RosterController(boundJID_, client_->getRoster(), client_->getAvatarManager(), uiFactory_, client_->getNickManager(), client_->getNickResolver(), client_->getPresenceOracle(), client_->getSubscriptionManager(), eventController_, uiEventStream_, client_->getIQRouter(), settings_, client_->getEntityCapsProvider(), client_->getClientBlockListManager(), client_->getVCardManager());          rosterController_->onChangeStatusRequest.connect(boost::bind(&MainController::handleChangeStatusRequest, this, _1, _2));          rosterController_->onSignOutRequest.connect(boost::bind(&MainController::signOut, this));          rosterController_->getWindow()->onShowCertificateRequest.connect(boost::bind(&MainController::handleShowCertificateRequest, this)); diff --git a/Swift/Controllers/Roster/RosterController.cpp b/Swift/Controllers/Roster/RosterController.cpp index 116ef2e..1d20c4a 100644 --- a/Swift/Controllers/Roster/RosterController.cpp +++ b/Swift/Controllers/Roster/RosterController.cpp @@ -17,6 +17,7 @@  #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> @@ -30,7 +31,6 @@  #include <Swiften/Roster/XMPPRosterItem.h>  #include <Swiften/VCards/VCardManager.h> -#include <Swift/Controllers/FileTransfer/FileTransferOverview.h>  #include <Swift/Controllers/Intl.h>  #include <Swift/Controllers/Roster/GroupRosterItem.h>  #include <Swift/Controllers/Roster/ItemOperations/AppearOffline.h> @@ -49,7 +49,6 @@  #include <Swift/Controllers/UIEvents/RemoveRosterItemUIEvent.h>  #include <Swift/Controllers/UIEvents/RenameGroupUIEvent.h>  #include <Swift/Controllers/UIEvents/RenameRosterItemUIEvent.h> -#include <Swift/Controllers/UIEvents/SendFileUIEvent.h>  #include <Swift/Controllers/UIInterfaces/MainWindow.h>  #include <Swift/Controllers/UIInterfaces/MainWindowFactory.h>  #include <Swift/Controllers/XMPPEvents/ErrorEvent.h> @@ -61,9 +60,8 @@ 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, VCardManager* vcardManager) -    : myJID_(jid), xmppRoster_(xmppRoster), mainWindowFactory_(mainWindowFactory), mainWindow_(mainWindowFactory_->createMainWindow(uiEventStream)), roster_(new Roster()), offlineFilter_(new OfflineRosterFilter()), vcardManager_(vcardManager), avatarManager_(avatarManager), nickManager_(nickManager), nickResolver_(nickResolver), presenceOracle_(presenceOracle), uiEventStream_(uiEventStream), entityCapsManager_(entityCapsManager), ftOverview_(fileTransferOverview), clientBlockListManager_(clientBlockListManager) { -    assert(fileTransferOverview); +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) +    : myJID_(jid), xmppRoster_(xmppRoster), mainWindowFactory_(mainWindowFactory), mainWindow_(mainWindowFactory_->createMainWindow(uiEventStream)), roster_(new Roster()), offlineFilter_(new OfflineRosterFilter()), vcardManager_(vcardManager), avatarManager_(avatarManager), nickManager_(nickManager), nickResolver_(nickResolver), presenceOracle_(presenceOracle), uiEventStream_(uiEventStream), entityCapsManager_(entityCapsManager), clientBlockListManager_(clientBlockListManager) {      iqRouter_ = iqRouter;      subscriptionManager_ = subscriptionManager;      eventController_ = eventController; @@ -81,6 +79,8 @@ RosterController::RosterController(const JID& jid, XMPPRoster* xmppRoster, Avata      subscriptionManager_->onPresenceSubscriptionRequest.connect(boost::bind(&RosterController::handleSubscriptionRequest, this, _1, _2));      uiEventConnection_ = uiEventStream->onUIEvent.connect(boost::bind(&RosterController::handleUIEvent, this, _1)); +    featureOracle_ = std::unique_ptr<FeatureOracle>(new 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)); @@ -267,9 +267,6 @@ void RosterController::handleUIEvent(std::shared_ptr<UIEvent> event) {              }          }      } -    else if (std::shared_ptr<SendFileUIEvent> sendFileEvent = std::dynamic_pointer_cast<SendFileUIEvent>(event)) { -        ftOverview_->sendFile(sendFileEvent->getJID(), sendFileEvent->getFilename()); -    }  }  void RosterController::setContactGroups(const JID& jid, const std::vector<std::string>& groups) { @@ -396,17 +393,14 @@ std::set<std::string> RosterController::getGroups() const {  }  void RosterController::handleOnCapsChanged(const JID& jid) { -    DiscoInfo::ref info = entityCapsManager_->getCaps(jid); -    if (info) { -        std::set<ContactRosterItem::Feature> features; -        if (FileTransferManager::isSupportedBy(info)) { -            features.insert(ContactRosterItem::FileTransferFeature); -        } -        if (info->hasFeature(DiscoInfo::WhiteboardFeature)) { -            features.insert(ContactRosterItem::WhiteboardFeature); -        } -        roster_->applyOnItems(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));  }  } diff --git a/Swift/Controllers/Roster/RosterController.h b/Swift/Controllers/Roster/RosterController.h index 980c545..ca2ecdc 100644 --- a/Swift/Controllers/Roster/RosterController.h +++ b/Swift/Controllers/Roster/RosterController.h @@ -27,8 +27,8 @@ namespace Swift {      class ClientBlockListManager;      class EntityCapsProvider;      class EventController; +    class FeatureOracle;      class FileTransferManager; -    class FileTransferOverview;      class IQRouter;      class MainWindow;      class MainWindowFactory; @@ -49,7 +49,7 @@ namespace Swift {      class RosterController {          public: -            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* entityCapsProvider, FileTransferOverview* fileTransferOverview, ClientBlockListManager* clientBlockListManager, VCardManager* vcardManager); +            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* entityCapsProvider, ClientBlockListManager* clientBlockListManager, VCardManager* vcardManager);              ~RosterController();              void showRosterWindow();              void setJID(const JID& jid) { myJID_ = jid; } @@ -111,10 +111,10 @@ namespace Swift {              SettingsProvider* settings_;              UIEventStream* uiEventStream_;              EntityCapsProvider* entityCapsManager_; -            FileTransferOverview* ftOverview_;              ClientBlockListManager* clientBlockListManager_;              RosterVCardProvider* rosterVCardProvider_;              std::shared_ptr<ContactRosterItem> ownContact_; +            std::unique_ptr<FeatureOracle> featureOracle_;              boost::signals2::scoped_connection blockingOnStateChangedConnection_;              boost::signals2::scoped_connection blockingOnItemAddedConnection_; diff --git a/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp b/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp index 0cd4080..ddbd7d3 100644 --- a/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp +++ b/Swift/Controllers/Roster/UnitTest/RosterControllerTest.cpp @@ -12,13 +12,16 @@  #include <Swiften/Client/ClientBlockListManager.h>  #include <Swiften/Client/DummyNickManager.h>  #include <Swiften/Client/DummyStanzaChannel.h> +#include <Swiften/Client/MemoryStorages.h>  #include <Swiften/Client/NickResolver.h>  #include <Swiften/Crypto/CryptoProvider.h>  #include <Swiften/Crypto/PlatformCryptoProvider.h> +#include <Swiften/Disco/CapsInfoGenerator.h> +#include <Swiften/Disco/CapsManager.h>  #include <Swiften/Disco/CapsProvider.h> +#include <Swiften/Disco/ClientDiscoManager.h>  #include <Swiften/Disco/EntityCapsManager.h>  #include <Swiften/EventLoop/DummyEventLoop.h> -#include <Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h>  #include <Swiften/Jingle/JingleSessionManager.h>  #include <Swiften/MUC/MUCRegistry.h>  #include <Swiften/Presence/PresenceOracle.h> @@ -29,7 +32,6 @@  #include <Swiften/VCards/VCardManager.h>  #include <Swiften/VCards/VCardMemoryStorage.h> -#include <Swift/Controllers/FileTransfer/FileTransferOverview.h>  #include <Swift/Controllers/Roster/ContactRosterItem.h>  #include <Swift/Controllers/Roster/GroupRosterItem.h>  #include <Swift/Controllers/Roster/Roster.h> @@ -42,8 +44,6 @@  using namespace Swift; -#define CHILDREN mainWindow_->roster->getRoot()->getChildren() -  class DummyCapsProvider : public CapsProvider {          DiscoInfo::ref getCaps(const std::string&) const {return DiscoInfo::ref(new DiscoInfo());}  }; @@ -61,6 +61,7 @@ class RosterControllerTest : public CppUnit::TestFixture {          CPPUNIT_TEST(testUnavailablePresence);          CPPUNIT_TEST(testRemoveResultsInUnavailablePresence);          CPPUNIT_TEST(testOwnContactInRosterPresence); +		CPPUNIT_TEST(testMultiResourceFileTransferFeature);          CPPUNIT_TEST_SUITE_END();      public: @@ -70,6 +71,8 @@ class RosterControllerTest : public CppUnit::TestFixture {              avatarManager_ = new NullAvatarManager();              mainWindowFactory_ = new MockMainWindowFactory();              mucRegistry_ = new MUCRegistry(); +            crypto_ = PlatformCryptoProvider::create(); +            storages_ = std::unique_ptr<MemoryStorages>(new MemoryStorages(crypto_));              nickResolver_ = new NickResolver(jid_.toBare(), xmppRoster_, nullptr, mucRegistry_);              channel_ = new DummyIQChannel();              router_ = new IQRouter(channel_); @@ -80,18 +83,16 @@ class RosterControllerTest : public CppUnit::TestFixture {              uiEventStream_ = new UIEventStream();              settings_ = new DummySettingsProvider();              nickManager_ = new DummyNickManager(); -            capsProvider_ = new DummyCapsProvider(); -            entityCapsManager_ = new EntityCapsManager(capsProvider_, stanzaChannel_); +            capsManager_ = std::unique_ptr<CapsManager>(new CapsManager(storages_->getCapsStorage(), stanzaChannel_, router_, crypto_)); +            entityCapsManager_ = new EntityCapsManager(capsManager_.get(), stanzaChannel_);              jingleSessionManager_ = new JingleSessionManager(router_); -            ftManager_ = new DummyFileTransferManager(); -            ftOverview_ = new FileTransferOverview(ftManager_);              clientBlockListManager_ = new ClientBlockListManager(router_); -            crypto_ = PlatformCryptoProvider::create();              vcardStorage_ = new VCardMemoryStorage(crypto_);              vcardManager_ = new VCardManager(jid_, router_, vcardStorage_); -            rosterController_ = new RosterController(jid_, xmppRoster_, avatarManager_, mainWindowFactory_, nickManager_, nickResolver_, presenceOracle_, subscriptionManager_, eventController_, uiEventStream_, router_, settings_, entityCapsManager_, ftOverview_, clientBlockListManager_, vcardManager_); +            rosterController_ = new RosterController(jid_, xmppRoster_, avatarManager_, mainWindowFactory_, nickManager_, nickResolver_, presenceOracle_, subscriptionManager_, eventController_, uiEventStream_, router_, settings_, entityCapsManager_, clientBlockListManager_, vcardManager_);              mainWindow_ = mainWindowFactory_->last; +            capsInfoGenerator_ = std::unique_ptr<CapsInfoGenerator>(new CapsInfoGenerator("", crypto_));          }          void tearDown() { @@ -100,11 +101,8 @@ class RosterControllerTest : public CppUnit::TestFixture {              delete vcardStorage_;              delete crypto_;              delete clientBlockListManager_; -            delete ftOverview_; -            delete ftManager_;              delete jingleSessionManager_;              delete entityCapsManager_; -            delete capsProvider_;              delete nickManager_;              delete nickResolver_;              delete mucRegistry_; @@ -122,7 +120,7 @@ class RosterControllerTest : public CppUnit::TestFixture {          }      GroupRosterItem* groupChild(size_t i) { -        return dynamic_cast<GroupRosterItem*>(CHILDREN[i]); +        return dynamic_cast<GroupRosterItem*>(getUIRosterChildren()[i]);      }      JID withResource(const JID& jid, const std::string& resource) { @@ -140,10 +138,10 @@ class RosterControllerTest : public CppUnit::TestFixture {          presence->setPriority(2);          presence->setStatus("So totally here");          stanzaChannel_->onPresenceReceived(presence); -        ContactRosterItem* item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(CHILDREN[0])->getChildren()[0]); +        ContactRosterItem* item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(getUIRosterChildren()[0])->getChildren()[0]);          CPPUNIT_ASSERT(item);          CPPUNIT_ASSERT_EQUAL(presence->getStatus(), item->getStatusText()); -        ContactRosterItem* item2 = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(CHILDREN[1])->getChildren()[0]); +        ContactRosterItem* item2 = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(getUIRosterChildren()[1])->getChildren()[0]);          CPPUNIT_ASSERT(item2);          CPPUNIT_ASSERT_EQUAL(presence->getStatus(), item2->getStatusText());      } @@ -163,7 +161,7 @@ class RosterControllerTest : public CppUnit::TestFixture {          highPresence->setStatus("So totally here");          stanzaChannel_->onPresenceReceived(lowPresence);          stanzaChannel_->onPresenceReceived(highPresence); -        ContactRosterItem* item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(CHILDREN[0])->getChildren()[0]); +        ContactRosterItem* item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(getUIRosterChildren()[0])->getChildren()[0]);          CPPUNIT_ASSERT(item);          CPPUNIT_ASSERT_EQUAL(highPresence->getStatus(), item->getStatusText());      } @@ -183,7 +181,7 @@ class RosterControllerTest : public CppUnit::TestFixture {          highPresence->setStatus("So totally here");          stanzaChannel_->onPresenceReceived(highPresence);          stanzaChannel_->onPresenceReceived(lowPresence); -        ContactRosterItem* item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(CHILDREN[0])->getChildren()[0]); +        ContactRosterItem* item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(getUIRosterChildren()[0])->getChildren()[0]);          CPPUNIT_ASSERT(item);          CPPUNIT_ASSERT_EQUAL(highPresence->getStatus(), item->getStatusText());      } @@ -223,7 +221,7 @@ class RosterControllerTest : public CppUnit::TestFixture {          stanzaChannel_->onPresenceReceived(highPresenceOffline);          // After this, the roster should show the low presence. -        ContactRosterItem* item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(CHILDREN[0])->getChildren()[0]); +        ContactRosterItem* item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(getUIRosterChildren()[0])->getChildren()[0]);          CPPUNIT_ASSERT(item);          Presence::ref low = presenceOracle_->getAccountPresence(from); @@ -233,7 +231,7 @@ class RosterControllerTest : public CppUnit::TestFixture {          CPPUNIT_ASSERT_EQUAL(lowPresence->getShow(), item->getStatusShow());          CPPUNIT_ASSERT_EQUAL(lowPresence->getStatus(), item->getStatusText());          stanzaChannel_->onPresenceReceived(lowPresenceOffline); -        item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(CHILDREN[0])->getChildren()[0]); +        item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(getUIRosterChildren()[0])->getChildren()[0]);          CPPUNIT_ASSERT(item);          /* A verification that if the test fails, it's the RosterController, not the PresenceOracle. */          low = presenceOracle_->getHighestPriorityPresence(from); @@ -249,7 +247,7 @@ class RosterControllerTest : public CppUnit::TestFixture {              groups.push_back("testGroup2");              xmppRoster_->addContact(JID("test@testdomain.com/bob"), "name", groups, RosterItemPayload::Both); -            CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(CHILDREN.size())); +            CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(getUIRosterChildren().size()));              //CPPUNIT_ASSERT_EQUAL(std::string("Bob"), xmppRoster_->getNameForJID(JID("foo@bar.com")));          } @@ -258,14 +256,14 @@ class RosterControllerTest : public CppUnit::TestFixture {              JID jid("test@testdomain.com");              xmppRoster_->addContact(jid, "name", groups, RosterItemPayload::None); -            CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(CHILDREN.size())); +            CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(getUIRosterChildren().size()));              CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(groupChild(0)->getChildren().size()));              xmppRoster_->addContact(jid, "name", groups, RosterItemPayload::To); -            CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(CHILDREN.size())); +            CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(getUIRosterChildren().size()));              CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(groupChild(0)->getChildren().size()));              xmppRoster_->addContact(jid, "name", groups, RosterItemPayload::Both); -            CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(CHILDREN.size())); +            CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(getUIRosterChildren().size()));              CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(groupChild(0)->getChildren().size()));          } @@ -275,11 +273,11 @@ class RosterControllerTest : public CppUnit::TestFixture {              JID jid("test@testdomain.com");              xmppRoster_->addContact(jid, "name", groups, RosterItemPayload::Both); -            CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(CHILDREN.size())); +            CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(getUIRosterChildren().size()));              CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(groupChild(0)->getChildren().size()));              CPPUNIT_ASSERT_EQUAL(std::string("name"), groupChild(0)->getChildren()[0]->getDisplayName());              xmppRoster_->addContact(jid, "NewName", groups, RosterItemPayload::Both); -            CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(CHILDREN.size())); +            CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(getUIRosterChildren().size()));              CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(groupChild(0)->getChildren().size()));              CPPUNIT_ASSERT_EQUAL(std::string("NewName"), groupChild(0)->getChildren()[0]->getDisplayName());          } @@ -293,18 +291,18 @@ class RosterControllerTest : public CppUnit::TestFixture {          JID jid("test@testdomain.com");          xmppRoster_->addContact(jid, "", oldGroups, RosterItemPayload::Both); -        CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(CHILDREN.size())); +        CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(getUIRosterChildren().size()));          CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(groupChild(0)->getChildren().size()));          CPPUNIT_ASSERT_EQUAL(jid.toString(), groupChild(0)->getChildren()[0]->getDisplayName());          xmppRoster_->addContact(jid, "new name", newGroups, RosterItemPayload::Both); -        CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(CHILDREN.size())); +        CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(getUIRosterChildren().size()));          CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(groupChild(0)->getChildren().size()));          CPPUNIT_ASSERT_EQUAL(std::string("new name"), groupChild(0)->getChildren()[0]->getDisplayName());          CPPUNIT_ASSERT_EQUAL(std::string("A Group"), groupChild(0)->getDisplayName());          xmppRoster_->addContact(jid, "new name", newestGroups, RosterItemPayload::Both); -        CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(CHILDREN.size())); +        CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(getUIRosterChildren().size()));          CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(groupChild(0)->getChildren().size()));          CPPUNIT_ASSERT_EQUAL(std::string("new name"), groupChild(0)->getChildren()[0]->getDisplayName());          CPPUNIT_ASSERT_EQUAL(std::string("Best Group"), groupChild(0)->getDisplayName()); @@ -364,14 +362,79 @@ class RosterControllerTest : public CppUnit::TestFixture {              presence->setPriority(2);              presence->setStatus("So totally here");              stanzaChannel_->onPresenceReceived(presence); -            ContactRosterItem* item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(CHILDREN[0])->getChildren()[0]); +            ContactRosterItem* item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(getUIRosterChildren()[0])->getChildren()[0]);              CPPUNIT_ASSERT(item);              CPPUNIT_ASSERT_EQUAL(presence->getStatus(), item->getStatusText()); -            ContactRosterItem* item2 = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(CHILDREN[1])->getChildren()[0]); +            ContactRosterItem* item2 = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(getUIRosterChildren()[1])->getChildren()[0]);              CPPUNIT_ASSERT(item2);              CPPUNIT_ASSERT_EQUAL(presence->getStatus(), item2->getStatusText());          } +         // This tests a scenario of a contact having a resource supporting Jingle File Transfer and +        // one resource not supporting it, and the contact features being set correctly. +        void testMultiResourceFileTransferFeature() { +            JID contact("test@testdomain.com"); +            xmppRoster_->addContact(contact, "Name", {}, RosterItemPayload::Both); + +            auto sendPresenceAndAnswerCaps = [=](const JID& from, const DiscoInfo& discoInfo) { +                auto capsInfo = capsInfoGenerator_->generateCapsInfo(discoInfo); + +                auto ftClientPresence = std::make_shared<Presence>(); +                ftClientPresence->setFrom(from); +                ftClientPresence->setPriority(0); +                ftClientPresence->setShow(StatusShow::Online); +                ftClientPresence->addPayload(std::make_shared<CapsInfo>(capsInfo)); +                stanzaChannel_->onPresenceReceived(ftClientPresence); + +                // disco reply +                auto discoRequest = channel_->iqs_.back(); +                CPPUNIT_ASSERT(discoRequest); +                auto discoReply = IQ::createResult(discoRequest->getFrom(), ftClientPresence->getFrom(), discoRequest->getID(), std::make_shared<DiscoInfo>(discoInfo)); +                channel_->onIQReceived(discoReply); +            }; + +            auto ftDiscoInfo = DiscoInfo(); +            ftDiscoInfo.addFeature(DiscoInfo::JingleFeature); +            ftDiscoInfo.addFeature(DiscoInfo::JingleFTFeature); +            ftDiscoInfo.addFeature(DiscoInfo::JingleTransportsIBBFeature); + +            sendPresenceAndAnswerCaps(contact.withResource("ft-supported"), ftDiscoInfo); + +            auto* item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(getUIRosterChildren()[0])->getChildren()[0]); +            CPPUNIT_ASSERT(item); +            CPPUNIT_ASSERT_EQUAL(contact, item->getJID()); +            CPPUNIT_ASSERT_EQUAL(true, item->supportsFeature(ContactRosterItem::FileTransferFeature)); + +            sendPresenceAndAnswerCaps(contact.withResource("ft-unsupported"), DiscoInfo()); + +            item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(getUIRosterChildren()[0])->getChildren()[0]); +            CPPUNIT_ASSERT(item); +            CPPUNIT_ASSERT_EQUAL(contact, item->getJID()); +            CPPUNIT_ASSERT_EQUAL(true, item->supportsFeature(ContactRosterItem::FileTransferFeature)); + +            auto unavailablePresence = std::make_shared<Presence>(); +            unavailablePresence->setFrom(contact.withResource("ft-unsupported")); +            unavailablePresence->setPriority(0); +            unavailablePresence->setType(Presence::Unavailable); +            stanzaChannel_->onPresenceReceived(unavailablePresence); + +            item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(getUIRosterChildren()[0])->getChildren()[0]); +            CPPUNIT_ASSERT(item); +            CPPUNIT_ASSERT_EQUAL(contact, item->getJID()); +            CPPUNIT_ASSERT_EQUAL(true, item->supportsFeature(ContactRosterItem::FileTransferFeature)); + +            unavailablePresence = std::make_shared<Presence>(); +            unavailablePresence->setFrom(contact.withResource("ft-supported")); +            unavailablePresence->setPriority(0); +            unavailablePresence->setType(Presence::Unavailable); +            stanzaChannel_->onPresenceReceived(unavailablePresence); + +            item = dynamic_cast<ContactRosterItem*>(dynamic_cast<GroupRosterItem*>(getUIRosterChildren()[0])->getChildren()[0]); +            CPPUNIT_ASSERT(item); +            CPPUNIT_ASSERT_EQUAL(contact, item->getJID()); +            CPPUNIT_ASSERT_EQUAL(false, item->supportsFeature(ContactRosterItem::FileTransferFeature)); +        } +          void assertVectorsEqual(const std::vector<std::string>& v1, const std::vector<std::string>& v2, int line) {              for (const auto& entry : v1) {                  if (std::find(v2.begin(), v2.end(), entry) == v2.end()) { @@ -382,8 +445,13 @@ class RosterControllerTest : public CppUnit::TestFixture {              }          } +        const std::vector<RosterItem*>& getUIRosterChildren() const { +            return mainWindow_->roster->getRoot()->getChildren(); +        } +      private:          JID jid_; +        std::unique_ptr<MemoryStorages> storages_;          XMPPRosterImpl* xmppRoster_;          MUCRegistry* mucRegistry_;          AvatarManager* avatarManager_; @@ -400,15 +468,14 @@ class RosterControllerTest : public CppUnit::TestFixture {          UIEventStream* uiEventStream_;          MockMainWindow* mainWindow_;          DummySettingsProvider* settings_; -        DummyCapsProvider* capsProvider_; +        std::unique_ptr<CapsManager> capsManager_;          EntityCapsManager* entityCapsManager_;          JingleSessionManager* jingleSessionManager_; -        FileTransferManager* ftManager_; -        FileTransferOverview* ftOverview_;          ClientBlockListManager* clientBlockListManager_;          CryptoProvider* crypto_;          VCardStorage* vcardStorage_;          VCardManager* vcardManager_; +        std::unique_ptr<CapsInfoGenerator> capsInfoGenerator_;  };  CPPUNIT_TEST_SUITE_REGISTRATION(RosterControllerTest); | 
 Swift
 Swift