From d2ba1ab8a36333523bf794c23226bd044e1717c2 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Thu, 21 Apr 2016 11:02:07 +0200 Subject: 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 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 #include #include +#include #include #include #include @@ -59,6 +60,7 @@ #include #include #include +#include #include #include #include @@ -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 event) { mucBookmarkManager_->addBookmark(addMUCBookmarkEvent->getBookmark()); return; } + std::shared_ptr sendFileEvent = std::dynamic_pointer_cast(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 createImpromptuMUCEvent = std::dynamic_pointer_cast(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 #include #include -#include #include #include #include @@ -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 #include #include +#include #include #include #include @@ -30,7 +31,6 @@ #include #include -#include #include #include #include @@ -49,7 +49,6 @@ #include #include #include -#include #include #include #include @@ -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(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 event) { } } } - else if (std::shared_ptr sendFileEvent = std::dynamic_pointer_cast(event)) { - ftOverview_->sendFile(sendFileEvent->getJID(), sendFileEvent->getFilename()); - } } void RosterController::setContactGroups(const JID& jid, const std::vector& groups) { @@ -396,17 +393,14 @@ std::set RosterController::getGroups() const { } void RosterController::handleOnCapsChanged(const JID& jid) { - DiscoInfo::ref info = entityCapsManager_->getCaps(jid); - if (info) { - std::set 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 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 ownContact_; + std::unique_ptr 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 #include #include +#include #include #include #include +#include +#include #include +#include #include #include -#include #include #include #include @@ -29,7 +32,6 @@ #include #include -#include #include #include #include @@ -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(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(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(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(CHILDREN[i]); + return dynamic_cast(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(dynamic_cast(CHILDREN[0])->getChildren()[0]); + ContactRosterItem* item = dynamic_cast(dynamic_cast(getUIRosterChildren()[0])->getChildren()[0]); CPPUNIT_ASSERT(item); CPPUNIT_ASSERT_EQUAL(presence->getStatus(), item->getStatusText()); - ContactRosterItem* item2 = dynamic_cast(dynamic_cast(CHILDREN[1])->getChildren()[0]); + ContactRosterItem* item2 = dynamic_cast(dynamic_cast(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(dynamic_cast(CHILDREN[0])->getChildren()[0]); + ContactRosterItem* item = dynamic_cast(dynamic_cast(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(dynamic_cast(CHILDREN[0])->getChildren()[0]); + ContactRosterItem* item = dynamic_cast(dynamic_cast(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(dynamic_cast(CHILDREN[0])->getChildren()[0]); + ContactRosterItem* item = dynamic_cast(dynamic_cast(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(dynamic_cast(CHILDREN[0])->getChildren()[0]); + item = dynamic_cast(dynamic_cast(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(CHILDREN.size())); + CPPUNIT_ASSERT_EQUAL(2, static_cast(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(CHILDREN.size())); + CPPUNIT_ASSERT_EQUAL(1, static_cast(getUIRosterChildren().size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(groupChild(0)->getChildren().size())); xmppRoster_->addContact(jid, "name", groups, RosterItemPayload::To); - CPPUNIT_ASSERT_EQUAL(1, static_cast(CHILDREN.size())); + CPPUNIT_ASSERT_EQUAL(1, static_cast(getUIRosterChildren().size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(groupChild(0)->getChildren().size())); xmppRoster_->addContact(jid, "name", groups, RosterItemPayload::Both); - CPPUNIT_ASSERT_EQUAL(1, static_cast(CHILDREN.size())); + CPPUNIT_ASSERT_EQUAL(1, static_cast(getUIRosterChildren().size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(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(CHILDREN.size())); + CPPUNIT_ASSERT_EQUAL(1, static_cast(getUIRosterChildren().size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(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(CHILDREN.size())); + CPPUNIT_ASSERT_EQUAL(1, static_cast(getUIRosterChildren().size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(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(CHILDREN.size())); + CPPUNIT_ASSERT_EQUAL(1, static_cast(getUIRosterChildren().size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(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(CHILDREN.size())); + CPPUNIT_ASSERT_EQUAL(1, static_cast(getUIRosterChildren().size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(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(CHILDREN.size())); + CPPUNIT_ASSERT_EQUAL(1, static_cast(getUIRosterChildren().size())); CPPUNIT_ASSERT_EQUAL(1, static_cast(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(dynamic_cast(CHILDREN[0])->getChildren()[0]); + ContactRosterItem* item = dynamic_cast(dynamic_cast(getUIRosterChildren()[0])->getChildren()[0]); CPPUNIT_ASSERT(item); CPPUNIT_ASSERT_EQUAL(presence->getStatus(), item->getStatusText()); - ContactRosterItem* item2 = dynamic_cast(dynamic_cast(CHILDREN[1])->getChildren()[0]); + ContactRosterItem* item2 = dynamic_cast(dynamic_cast(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(); + ftClientPresence->setFrom(from); + ftClientPresence->setPriority(0); + ftClientPresence->setShow(StatusShow::Online); + ftClientPresence->addPayload(std::make_shared(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)); + 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(dynamic_cast(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(dynamic_cast(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(); + unavailablePresence->setFrom(contact.withResource("ft-unsupported")); + unavailablePresence->setPriority(0); + unavailablePresence->setType(Presence::Unavailable); + stanzaChannel_->onPresenceReceived(unavailablePresence); + + item = dynamic_cast(dynamic_cast(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(); + unavailablePresence->setFrom(contact.withResource("ft-supported")); + unavailablePresence->setPriority(0); + unavailablePresence->setType(Presence::Unavailable); + stanzaChannel_->onPresenceReceived(unavailablePresence); + + item = dynamic_cast(dynamic_cast(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& v1, const std::vector& 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& getUIRosterChildren() const { + return mainWindow_->roster->getRoot()->getChildren(); + } + private: JID jid_; + std::unique_ptr 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_; EntityCapsManager* entityCapsManager_; JingleSessionManager* jingleSessionManager_; - FileTransferManager* ftManager_; - FileTransferOverview* ftOverview_; ClientBlockListManager* clientBlockListManager_; CryptoProvider* crypto_; VCardStorage* vcardStorage_; VCardManager* vcardManager_; + std::unique_ptr capsInfoGenerator_; }; CPPUNIT_TEST_SUITE_REGISTRATION(RosterControllerTest); diff --git a/Swiften/Base/SConscript b/Swiften/Base/SConscript index 92e3da8..de502c2 100644 --- a/Swiften/Base/SConscript +++ b/Swiften/Base/SConscript @@ -1,23 +1,23 @@ Import("swiften_env") objects = swiften_env.SwiftenObject([ + "BoostRandomGenerator.cpp", "ByteArray.cpp", "DateTime.cpp", - "SafeByteArray.cpp", - "SafeAllocator.cpp", "Error.cpp", + "FileSize.cpp", + "IDGenerator.cpp", "Log.cpp", "LogSerializers.cpp", "Path.cpp", "Paths.cpp", - "String.cpp", - "IDGenerator.cpp", - "SimpleIDGenerator.cpp", "RandomGenerator.cpp", - "BoostRandomGenerator.cpp", - "sleep.cpp", - "URL.cpp", "Regex.cpp", - "FileSize.cpp" + "SafeAllocator.cpp", + "SafeByteArray.cpp", + "SimpleIDGenerator.cpp", + "String.cpp", + "URL.cpp", + "sleep.cpp", ]) swiften_env.Append(SWIFTEN_OBJECTS = [objects]) diff --git a/Swiften/Disco/FeatureOracle.cpp b/Swiften/Disco/FeatureOracle.cpp index 8328984..2baf87c 100644 --- a/Swiften/Disco/FeatureOracle.cpp +++ b/Swiften/Disco/FeatureOracle.cpp @@ -8,10 +8,12 @@ #include #include +#include #include -#include +#include #include +#include #include #include #include @@ -20,78 +22,178 @@ namespace Swift { FeatureOracle::FeatureOracle(EntityCapsProvider* capsProvider, PresenceOracle* presenceOracle) : capsProvider_(capsProvider), presenceOracle_(presenceOracle) { - } Tristate FeatureOracle::isFileTransferSupported(const JID& jid) { - DiscoInfo::ref discoInfo = getDiscoResultForJID(jid); - if (discoInfo) { - return FileTransferManager::isSupportedBy(discoInfo) ? Yes : No; + Tristate fileTransferSupported = No; + + auto isYesOrMaybe = [](Tristate tristate) { return tristate == Yes || tristate == Maybe; }; + auto isYes = [](Tristate tristate) { return tristate == Yes; }; + + auto supportedFeatures = getFeaturesForJID(jid); + + auto jingleSupported = isFeatureSupported(supportedFeatures, DiscoInfo::JingleFeature); + auto jingleFTSupported = isFeatureSupported(supportedFeatures, DiscoInfo::JingleFTFeature); + auto jingleTransportIBBSupported = isFeatureSupported(supportedFeatures, DiscoInfo::JingleTransportsIBBFeature); + auto jingleTransportS5BSupported = isFeatureSupported(supportedFeatures, DiscoInfo::JingleTransportsS5BFeature); + + if (isYes(jingleSupported) && isYes(jingleFTSupported) && (isYes(jingleTransportIBBSupported) || isYes(jingleTransportS5BSupported))) { + fileTransferSupported = Yes; } - else { - return Maybe; + else if (isYesOrMaybe(jingleSupported) && isYesOrMaybe(jingleFTSupported) && (isYesOrMaybe(jingleTransportIBBSupported) || isYesOrMaybe(jingleTransportS5BSupported))) { + fileTransferSupported = Maybe; } + + return fileTransferSupported; } Tristate FeatureOracle::isMessageReceiptsSupported(const JID& jid) { - return isFeatureSupported(jid, DiscoInfo::MessageDeliveryReceiptsFeature); + return isFeatureSupported(getFeaturesForJID(jid), DiscoInfo::MessageDeliveryReceiptsFeature); } Tristate FeatureOracle::isMessageCorrectionSupported(const JID& jid) { - return isFeatureSupported(jid, DiscoInfo::MessageCorrectionFeature); + return isFeatureSupported(getFeaturesForJID(jid), DiscoInfo::MessageCorrectionFeature); +} + +Tristate FeatureOracle::isWhiteboardSupported(const JID& jid) { + return isFeatureSupported(getFeaturesForJID(jid), DiscoInfo::WhiteboardFeature); } -DiscoInfo::ref FeatureOracle::getDiscoResultForJID(const JID& jid) { - DiscoInfo::ref discoInfo; +class PresenceFeatureAvailablityComparator { + public: + static int preferenceFromStatusShow(StatusShow::Type showType) { + switch (showType) { + case StatusShow::FFC: + return 5; + case StatusShow::Online: + return 4; + case StatusShow::DND: + return 3; + case StatusShow::Away: + return 2; + case StatusShow::XA: + return 1; + case StatusShow::None: + return 0; + } + assert(false); + return -1; + } + + static int compareWithoutResource(const Presence::ref& a, const Presence::ref& b) { + int aPreference = preferenceFromStatusShow(a->getShow()); + int bPreference = preferenceFromStatusShow(b->getShow()); + + if (aPreference != bPreference) { + return aPreference < bPreference ? 1 : -1; + } + + Idle::ref aIdle = a->getPayload(); + Idle::ref bIdle = b->getPayload(); + + if (aIdle && !bIdle) { + return -1; + } + else if (!aIdle && bIdle) { + return 1; + } + + if (a->getPriority() != b->getPriority()) { + return a->getPriority() < b->getPriority() ? 1 : -1; + } + + return 0; + } + + /* + * This method returns true, if \ref Presence \p b is more available than + * \ref Presence \p a. + * Going by \ref StatusShow::Type first, then by idle state, then by + * presence priority and finally by resource name. + */ + bool operator()(const Presence::ref& a, const Presence::ref& b) { + int aMoreAvailableThanB = compareWithoutResource(a, b); + if (aMoreAvailableThanB < 0) { + return true; + } + else if (aMoreAvailableThanB > 0) { + return false; + } + + return a->getFrom().getResource() < b->getFrom().getResource(); + } +}; + +JID FeatureOracle::getMostAvailableClientForFileTrasfer(const JID& bareJID) { + JID fullJID; + assert(bareJID.isBare()); + + std::vector allPresences = presenceOracle_->getAllPresence(bareJID); + std::sort(allPresences.begin(), allPresences.end(), PresenceFeatureAvailablityComparator()); + + for (const auto& presence : allPresences) { + if (presence->isAvailable()) { + if (isFileTransferSupported(presence->getFrom()) == Yes) { + fullJID = presence->getFrom(); + break; + } + } + } + + SWIFT_LOG_ASSERT(!fullJID.isBare(), error); + return fullJID; +} + +std::unordered_map FeatureOracle::getFeaturesForJID(const JID& jid) { + std::unordered_map supportedFeatures; if (jid.isBare()) { - // Calculate the common subset of disco features of all available results and return that. - std::vector availablePresences = presenceOracle_->getAllPresence(jid); - - bool commonFeaturesInitialized = false; - std::vector commonFeatures; - foreach(Presence::ref presence, availablePresences) { - DiscoInfo::ref presenceDiscoInfo = capsProvider_->getCaps(presence->getFrom()); - if (presenceDiscoInfo) { - std::vector features = presenceDiscoInfo->getFeatures(); - if (!commonFeaturesInitialized) { - commonFeatures = features; - commonFeaturesInitialized = true; - } - else { - std::vector featuresToRemove; - foreach(const std::string& feature, commonFeatures) { - if (std::find(features.begin(), features.end(), feature) == features.end()) { - featuresToRemove.push_back(feature); - } - } - foreach(const std::string& featureToRemove, featuresToRemove) { - commonFeatures.erase(std::remove(commonFeatures.begin(), commonFeatures.end(), featureToRemove), commonFeatures.end()); - } + // Calculate the union of disco features of all most available results and return that. + std::vector onlineDiscoInfos; + std::unordered_set features; + + // Collect relevant disco info results and the set of features. + for (auto&& presence : presenceOracle_->getAllPresence(jid)) { + if (presence->getType() == Presence::Available) { + DiscoInfo::ref presenceDiscoInfo = capsProvider_->getCaps(presence->getFrom()); + if (presenceDiscoInfo) { + onlineDiscoInfos.push_back(presenceDiscoInfo); + features.insert(presenceDiscoInfo->getFeatures().begin(), presenceDiscoInfo->getFeatures().end()); } } } - discoInfo = std::make_shared(); - foreach(const std::string& commonFeature, commonFeatures) { - discoInfo->addFeature(commonFeature); + // Calculate supportedFeaturesMap. + for (auto&& feature : features) { + Tristate supported = Yes; + for (auto&& discoInfo : onlineDiscoInfos) { + if (!discoInfo->hasFeature(feature)) { + supported = Maybe; + break; + } + } + supportedFeatures[feature] = supported; } } else { // Return the disco result of the full JID. - discoInfo = capsProvider_->getCaps(jid); + auto discoInfo = capsProvider_->getCaps(jid); + if (discoInfo) { + for (auto&& feature : discoInfo->getFeatures()) { + supportedFeatures[feature] = Yes; + } + } } - return discoInfo; + return supportedFeatures; } -Tristate FeatureOracle::isFeatureSupported(const JID& jid, const std::string& feature) { - DiscoInfo::ref discoInfo = getDiscoResultForJID(jid); - if (discoInfo) { - return discoInfo->hasFeature(feature) ? Yes : No; - } - else { - return Maybe; +Tristate FeatureOracle::isFeatureSupported(const std::unordered_map& supportedFeatures, const std::string& feature) { + Tristate supported = No; + auto lookupResult = supportedFeatures.find(feature); + if (lookupResult != supportedFeatures.end()) { + supported = lookupResult->second; } + return supported; } } diff --git a/Swiften/Disco/FeatureOracle.h b/Swiften/Disco/FeatureOracle.h index d434e86..be0cd6f 100644 --- a/Swiften/Disco/FeatureOracle.h +++ b/Swiften/Disco/FeatureOracle.h @@ -1,11 +1,14 @@ /* - * Copyright (c) 2015 Isode Limited. + * Copyright (c) 2015-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #pragma once +#include +#include + #include #include #include @@ -27,16 +30,19 @@ class SWIFTEN_API FeatureOracle { Tristate isFileTransferSupported(const JID& jid); Tristate isMessageReceiptsSupported(const JID& jid); Tristate isMessageCorrectionSupported(const JID& jid); + Tristate isWhiteboardSupported(const JID& jid); + + JID getMostAvailableClientForFileTrasfer(const JID& bareJID); private: /** * @brief getDiscoResultForJID returns a shared reference to a DiscoInfo representing features supported by the jid. - * @param jid The JID to return the DiscoInfo::ref for. - * @return DiscoResult::ref + * @param jid The JID to return an std::unordered_map for. + * @return std::unordered_map */ - DiscoInfo::ref getDiscoResultForJID(const JID& jid); + std::unordered_map getFeaturesForJID(const JID& jid); - Tristate isFeatureSupported(const JID& jid, const std::string& feature); + Tristate isFeatureSupported(const std::unordered_map& supportedFeatures, const std::string& feature); private: EntityCapsProvider* capsProvider_; diff --git a/Swiften/Disco/UnitTest/FeatureOracleTest.cpp b/Swiften/Disco/UnitTest/FeatureOracleTest.cpp new file mode 100644 index 0000000..e5ff09b --- /dev/null +++ b/Swiften/Disco/UnitTest/FeatureOracleTest.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Swift; + +class FeatureOracleTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(FeatureOracleTest); + CPPUNIT_TEST(testMergeAvailableResourcesForFeatures); + CPPUNIT_TEST(testMostAvailableFileTransferClient); + CPPUNIT_TEST_SUITE_END(); + + public: + void setUp() { + crypto_ = std::shared_ptr(PlatformCryptoProvider::create()); + dummyStanzaChannel_ = new DummyStanzaChannel(); + xmppRosterImpl_ = new XMPPRosterImpl(); + dummyCapsProvider_ = new DummyCapsProvider(); + entityCapsManager_ = new EntityCapsManager(dummyCapsProvider_, dummyStanzaChannel_); + presenceOracle_ = new PresenceOracle(dummyStanzaChannel_, xmppRosterImpl_); + featureOracle_ = new FeatureOracle(entityCapsManager_, presenceOracle_); + } + + void tearDown() { + delete featureOracle_; + delete presenceOracle_; + delete entityCapsManager_; + delete dummyCapsProvider_; + delete xmppRosterImpl_; + delete dummyStanzaChannel_; + } + + void simulateIncomingPresence(const JID& from, Presence::Type type, StatusShow::Type status, const DiscoInfo::ref& disco, const std::vector& additionalPayloads = {}) { + auto capsInfo = std::make_shared(CapsInfoGenerator("http://example.com", crypto_.get()).generateCapsInfo(*disco.get())); + dummyCapsProvider_->caps[capsInfo->getVersion()] = disco; + + Presence::ref capsNotifyPresence = std::make_shared(); + capsNotifyPresence->setType(type); + capsNotifyPresence->setFrom(from); + capsNotifyPresence->setShow(status); + capsNotifyPresence->addPayload(capsInfo); + + capsNotifyPresence->addPayloads(additionalPayloads); + + xmppRosterImpl_->addContact(from, "Foo", {}, RosterItemPayload::Both); + dummyStanzaChannel_->onPresenceReceived(capsNotifyPresence); + } + + DiscoInfo::ref fileTransferSupportingDisco() { + DiscoInfo::ref discoInfo = std::make_shared(); + discoInfo->addFeature(DiscoInfo::JingleFeature); + discoInfo->addFeature(DiscoInfo::JingleFTFeature); + discoInfo->addFeature(DiscoInfo::JingleTransportsS5BFeature); + discoInfo->addFeature(DiscoInfo::JingleTransportsIBBFeature); + return discoInfo; + } + + DiscoInfo::ref noFileTransferSupportingDisco() { + DiscoInfo::ref discoInfo = std::make_shared(); + discoInfo->addFeature(DiscoInfo::JingleFeature); + return discoInfo; + } + + void testMergeAvailableResourcesForFeatures() { + CPPUNIT_ASSERT_EQUAL(No, featureOracle_->isFileTransferSupported(baseJID)); + + simulateIncomingPresence(noFileTransferJID, Presence::Available, StatusShow::Online, noFileTransferSupportingDisco()); + + CPPUNIT_ASSERT_EQUAL(size_t(1), presenceOracle_->getAllPresence(baseJID).size()); + CPPUNIT_ASSERT_EQUAL(No, featureOracle_->isFileTransferSupported(baseJID)); + + simulateIncomingPresence(fileTransferJID, Presence::Available, StatusShow::Online, fileTransferSupportingDisco()); + + CPPUNIT_ASSERT_EQUAL(size_t(2), presenceOracle_->getAllPresence(baseJID).size()); + CPPUNIT_ASSERT_EQUAL(Maybe, featureOracle_->isFileTransferSupported(baseJID)); + + simulateIncomingPresence(noFileTransferJID, Presence::Unavailable, StatusShow::None, noFileTransferSupportingDisco()); + + CPPUNIT_ASSERT_EQUAL(size_t(1), presenceOracle_->getAllPresence(baseJID).size()); + CPPUNIT_ASSERT_EQUAL(Yes, featureOracle_->isFileTransferSupported(baseJID)); + + simulateIncomingPresence(fileTransferJID, Presence::Unavailable, StatusShow::None, fileTransferSupportingDisco()); + + CPPUNIT_ASSERT_EQUAL(size_t(1), presenceOracle_->getAllPresence(baseJID).size()); + CPPUNIT_ASSERT_EQUAL(No, featureOracle_->isFileTransferSupported(baseJID)); + } + + void testMostAvailableFileTransferClient() { + simulateIncomingPresence(fileTransferJID, Presence::Available, StatusShow::DND, fileTransferSupportingDisco()); + + CPPUNIT_ASSERT_EQUAL(fileTransferJID, featureOracle_->getMostAvailableClientForFileTrasfer(baseJID)); + + simulateIncomingPresence(noFileTransferJID, Presence::Available, StatusShow::Online, noFileTransferSupportingDisco()); + + CPPUNIT_ASSERT_EQUAL(fileTransferJID, featureOracle_->getMostAvailableClientForFileTrasfer(baseJID)); + + auto moreAvailableJID = baseJID.withResource("moreAvailableFt"); + simulateIncomingPresence(moreAvailableJID, Presence::Available, StatusShow::Online, fileTransferSupportingDisco()); + + CPPUNIT_ASSERT_EQUAL(moreAvailableJID, featureOracle_->getMostAvailableClientForFileTrasfer(baseJID)); + } + + private: + struct DummyCapsProvider : public CapsProvider { + virtual DiscoInfo::ref getCaps(const std::string& hash) const { + std::map::const_iterator i = caps.find(hash); + if (i != caps.end()) { + return i->second; + } + return DiscoInfo::ref(); + } + + std::map caps; + }; + + private: + JID baseJID = "test@example.com"; + JID fileTransferJID = baseJID.withResource("fileTransfer"); + JID noFileTransferJID = baseJID.withResource("noFileTransfer"); + + std::shared_ptr crypto_; + DummyCapsProvider* dummyCapsProvider_; + DummyStanzaChannel* dummyStanzaChannel_; + EntityCapsManager* entityCapsManager_; + FeatureOracle* featureOracle_; + PresenceOracle* presenceOracle_; + XMPPRosterImpl* xmppRosterImpl_; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(FeatureOracleTest); diff --git a/Swiften/Elements/Stanza.h b/Swiften/Elements/Stanza.h index 9a69696..9284bc3 100644 --- a/Swiften/Elements/Stanza.h +++ b/Swiften/Elements/Stanza.h @@ -68,6 +68,11 @@ namespace Swift { payloads_.insert(payloads_.end(), begin, end); } + template + void addPayloads(const Container& container) { + payloads_.insert(payloads_.end(), std::begin(container), std::end(container)); + } + void updatePayload(std::shared_ptr payload); void removePayloadOfSameType(std::shared_ptr); diff --git a/Swiften/FileTransfer/FileTransferManager.cpp b/Swiften/FileTransfer/FileTransferManager.cpp index 94f4ab7..a5d7313 100644 --- a/Swiften/FileTransfer/FileTransferManager.cpp +++ b/Swiften/FileTransfer/FileTransferManager.cpp @@ -4,6 +4,12 @@ * See Documentation/Licenses/BSD-simplified.txt for more information. */ +/* + * Copyright (c) 2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + #include namespace Swift { @@ -11,13 +17,4 @@ namespace Swift { FileTransferManager::~FileTransferManager() { } -bool FileTransferManager::isSupportedBy(const DiscoInfo::ref info) { - if (info) { - return info->hasFeature(DiscoInfo::JingleFeature) - && info->hasFeature(DiscoInfo::JingleFTFeature) - && (info->hasFeature(DiscoInfo::JingleTransportsIBBFeature) || info->hasFeature(DiscoInfo::JingleTransportsS5BFeature)); - } - return false; -} - } diff --git a/Swiften/FileTransfer/FileTransferManager.h b/Swiften/FileTransfer/FileTransferManager.h index e315c67..07cfc90 100644 --- a/Swiften/FileTransfer/FileTransferManager.h +++ b/Swiften/FileTransfer/FileTransferManager.h @@ -47,8 +47,6 @@ namespace Swift { std::shared_ptr bytestream, const FileTransferOptions& = FileTransferOptions()) = 0; - static bool isSupportedBy(const DiscoInfo::ref info); - boost::signals2::signal onIncomingFileTransfer; }; } diff --git a/Swiften/FileTransfer/FileTransferManagerImpl.cpp b/Swiften/FileTransfer/FileTransferManagerImpl.cpp index d449179..05dd3bb 100644 --- a/Swiften/FileTransfer/FileTransferManagerImpl.cpp +++ b/Swiften/FileTransfer/FileTransferManagerImpl.cpp @@ -19,8 +19,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -99,28 +99,6 @@ void FileTransferManagerImpl::stop() { s5bServerManager->stop(); } -boost::optional FileTransferManagerImpl::highestPriorityJIDSupportingFileTransfer(const JID& bareJID) { - JID fullReceipientJID; - int priority = INT_MIN; - - //getAllPresence(bareJID) gives you all presences for the bare JID (i.e. all resources) Isode Limited. @ 11:11 - std::vector presences = presenceOracle->getAllPresence(bareJID); - - //iterate over them - foreach(Presence::ref pres, presences) { - if (pres->getPriority() > priority) { - // look up caps from the jid - DiscoInfo::ref info = capsProvider->getCaps(pres->getFrom()); - if (isSupportedBy(info)) { - priority = pres->getPriority(); - fullReceipientJID = pres->getFrom(); - } - } - } - - return fullReceipientJID.isValid() ? boost::optional(fullReceipientJID) : boost::optional(); -} - OutgoingFileTransfer::ref FileTransferManagerImpl::createOutgoingFileTransfer( const JID& to, const boost::filesystem::path& filepath, @@ -155,9 +133,10 @@ OutgoingFileTransfer::ref FileTransferManagerImpl::createOutgoingFileTransfer( JID receipient = to; if(receipient.isBare()) { - boost::optional fullJID = highestPriorityJIDSupportingFileTransfer(receipient); - if (fullJID.is_initialized()) { - receipient = fullJID.get(); + auto featureOracle = FeatureOracle(capsProvider, presenceOracle); + JID fullJID = featureOracle.getMostAvailableClientForFileTrasfer(receipient); + if (!fullJID.toString().empty()) { + receipient = fullJID; } else { return OutgoingFileTransfer::ref(); } diff --git a/Swiften/Presence/PresenceOracle.cpp b/Swiften/Presence/PresenceOracle.cpp index c2c1152..1c9d0ea 100644 --- a/Swiften/Presence/PresenceOracle.cpp +++ b/Swiften/Presence/PresenceOracle.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2015 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -10,7 +10,6 @@ #include -#include #include #include #include @@ -101,11 +100,10 @@ std::vector PresenceOracle::getAllPresence(const JID& bareJID) co if (i == entries_.end()) { return results; } - PresenceMap presenceMap = i->second; - PresenceMap::const_iterator j = presenceMap.begin(); - for (; j != presenceMap.end(); ++j) { - Presence::ref current = j->second; - results.push_back(current); + for (const auto& jidPresence : i->second) { + if (jidPresence.second) { + results.push_back(jidPresence.second); + } } return results; } @@ -153,7 +151,7 @@ Presence::ref PresenceOracle::getActivePresence(const std::vector PresenceAccountPriorityQueue away; PresenceAccountPriorityQueue offline; - foreach(Presence::ref presence, presences) { + for (auto&& presence : presences) { switch (presence->getShow()) { case StatusShow::Online: online.push(presence); @@ -200,18 +198,15 @@ Presence::ref PresenceOracle::getHighestPriorityPresence(const JID& bareJID) con if (i == entries_.end()) { return Presence::ref(); } - PresenceMap presenceMap = i->second; - PresenceMap::const_iterator j = presenceMap.begin(); Presence::ref highest; - for (; j != presenceMap.end(); ++j) { - Presence::ref current = j->second; + for (const auto& jidPresence : i->second) { + Presence::ref current = jidPresence.second; if (!highest || current->getPriority() > highest->getPriority() || (current->getPriority() == highest->getPriority() && StatusShow::typeToAvailabilityOrdering(current->getShow()) > StatusShow::typeToAvailabilityOrdering(highest->getShow()))) { highest = current; } - } return highest; } diff --git a/Swiften/SConscript b/Swiften/SConscript index eb7ae19..a8fb88a 100644 --- a/Swiften/SConscript +++ b/Swiften/SConscript @@ -387,9 +387,10 @@ if env["SCONS_STAGE"] == "build" : File("Component/UnitTest/ComponentSessionTest.cpp"), File("Disco/UnitTest/CapsInfoGeneratorTest.cpp"), File("Disco/UnitTest/CapsManagerTest.cpp"), + File("Disco/UnitTest/DiscoInfoResponderTest.cpp"), File("Disco/UnitTest/EntityCapsManagerTest.cpp"), + File("Disco/UnitTest/FeatureOracleTest.cpp"), File("Disco/UnitTest/JIDDiscoInfoResponderTest.cpp"), - File("Disco/UnitTest/DiscoInfoResponderTest.cpp"), File("Elements/UnitTest/IQTest.cpp"), File("Elements/UnitTest/StanzaTest.cpp"), File("Elements/UnitTest/FormTest.cpp"), -- cgit v0.10.2-6-g49f6