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 /Swiften | |
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 'Swiften')
-rw-r--r-- | Swiften/Base/SConscript | 18 | ||||
-rw-r--r-- | Swiften/Disco/FeatureOracle.cpp | 194 | ||||
-rw-r--r-- | Swiften/Disco/FeatureOracle.h | 16 | ||||
-rw-r--r-- | Swiften/Disco/UnitTest/FeatureOracleTest.cpp | 151 | ||||
-rw-r--r-- | Swiften/Elements/Stanza.h | 5 | ||||
-rw-r--r-- | Swiften/FileTransfer/FileTransferManager.cpp | 15 | ||||
-rw-r--r-- | Swiften/FileTransfer/FileTransferManager.h | 2 | ||||
-rw-r--r-- | Swiften/FileTransfer/FileTransferManagerImpl.cpp | 31 | ||||
-rw-r--r-- | Swiften/Presence/PresenceOracle.cpp | 21 | ||||
-rw-r--r-- | Swiften/SConscript | 3 |
10 files changed, 345 insertions, 111 deletions
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 @@ -1,99 +1,201 @@ /* * Copyright (c) 2015-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include <Swiften/Disco/FeatureOracle.h> #include <algorithm> #include <iterator> +#include <unordered_set> #include <vector> -#include <Swiften/Base/foreach.h> +#include <Swiften/Base/Log.h> #include <Swiften/Disco/EntityCapsProvider.h> +#include <Swiften/Elements/Idle.h> #include <Swiften/Elements/Presence.h> #include <Swiften/FileTransfer/FileTransferManager.h> #include <Swiften/JID/JID.h> #include <Swiften/Presence/PresenceOracle.h> 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>(); + Idle::ref bIdle = b->getPayload<Idle>(); + + 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<Presence::ref> 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<std::string, Tristate> FeatureOracle::getFeaturesForJID(const JID& jid) { + std::unordered_map<std::string, Tristate> supportedFeatures; if (jid.isBare()) { - // Calculate the common subset of disco features of all available results and return that. - std::vector<Presence::ref> availablePresences = presenceOracle_->getAllPresence(jid); - - bool commonFeaturesInitialized = false; - std::vector<std::string> commonFeatures; - foreach(Presence::ref presence, availablePresences) { - DiscoInfo::ref presenceDiscoInfo = capsProvider_->getCaps(presence->getFrom()); - if (presenceDiscoInfo) { - std::vector<std::string> features = presenceDiscoInfo->getFeatures(); - if (!commonFeaturesInitialized) { - commonFeatures = features; - commonFeaturesInitialized = true; - } - else { - std::vector<std::string> 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<DiscoInfo::ref> onlineDiscoInfos; + std::unordered_set<std::string> 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<DiscoInfo>(); - 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<std::string, Tristate>& 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,47 +1,53 @@ /* - * Copyright (c) 2015 Isode Limited. + * Copyright (c) 2015-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #pragma once +#include <unordered_map> +#include <unordered_set> + #include <Swiften/Base/API.h> #include <Swiften/Base/Tristate.h> #include <Swiften/Elements/DiscoInfo.h> namespace Swift { class EntityCapsProvider; class JID; class PresenceOracle; /** * @brief The FeatureOracle class enables direct feature support lookup for client features supported by Swiften. */ class SWIFTEN_API FeatureOracle { public: FeatureOracle(EntityCapsProvider* capsProvider, PresenceOracle* presenceOracle); public: 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<std::string, Tristate> for. + * @return std::unordered_map<std::string, Tristate> */ - DiscoInfo::ref getDiscoResultForJID(const JID& jid); + std::unordered_map<std::string, Tristate> getFeaturesForJID(const JID& jid); - Tristate isFeatureSupported(const JID& jid, const std::string& feature); + Tristate isFeatureSupported(const std::unordered_map<std::string, Tristate>& supportedFeatures, const std::string& feature); private: EntityCapsProvider* capsProvider_; PresenceOracle* presenceOracle_; }; } 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 <vector> + +#include <boost/bind.hpp> + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include <Swiften/Base/Tristate.h> +#include <Swiften/Client/DummyStanzaChannel.h> +#include <Swiften/Crypto/CryptoProvider.h> +#include <Swiften/Crypto/PlatformCryptoProvider.h> +#include <Swiften/Disco/CapsInfoGenerator.h> +#include <Swiften/Disco/CapsProvider.h> +#include <Swiften/Disco/EntityCapsManager.h> +#include <Swiften/Disco/FeatureOracle.h> +#include <Swiften/Elements/CapsInfo.h> +#include <Swiften/Presence/PresenceOracle.h> +#include <Swiften/Roster/XMPPRosterImpl.h> + +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<CryptoProvider>(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<Payload::ref>& additionalPayloads = {}) { + auto capsInfo = std::make_shared<CapsInfo>(CapsInfoGenerator("http://example.com", crypto_.get()).generateCapsInfo(*disco.get())); + dummyCapsProvider_->caps[capsInfo->getVersion()] = disco; + + Presence::ref capsNotifyPresence = std::make_shared<Presence>(); + 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>(); + 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>(); + 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<std::string, DiscoInfo::ref>::const_iterator i = caps.find(hash); + if (i != caps.end()) { + return i->second; + } + return DiscoInfo::ref(); + } + + std::map<std::string, DiscoInfo::ref> caps; + }; + + private: + JID baseJID = "test@example.com"; + JID fileTransferJID = baseJID.withResource("fileTransfer"); + JID noFileTransferJID = baseJID.withResource("noFileTransfer"); + + std::shared_ptr<CryptoProvider> 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 @@ -41,56 +41,61 @@ namespace Swift { } return std::shared_ptr<T>(); } template<typename T> std::vector< std::shared_ptr<T> > getPayloads() const { std::vector< std::shared_ptr<T> > results; for (const auto& payload : payloads_) { std::shared_ptr<T> result(std::dynamic_pointer_cast<T>(payload)); if (result) { results.push_back(result); } } return results; } const std::vector< std::shared_ptr<Payload> >& getPayloads() const { return payloads_; } void addPayload(std::shared_ptr<Payload> payload) { payloads_.push_back(payload); } template<typename InputIterator> void addPayloads(InputIterator begin, InputIterator end) { payloads_.insert(payloads_.end(), begin, end); } + template<typename Container> + void addPayloads(const Container& container) { + payloads_.insert(payloads_.end(), std::begin(container), std::end(container)); + } + void updatePayload(std::shared_ptr<Payload> payload); void removePayloadOfSameType(std::shared_ptr<Payload>); std::shared_ptr<Payload> getPayloadOfSameType(std::shared_ptr<Payload>) const; const JID& getFrom() const { return from_; } void setFrom(const JID& from) { from_ = from; } const JID& getTo() const { return to_; } void setTo(const JID& to) { to_ = to; } const std::string& getID() const { return id_; } void setID(const std::string& id) { id_ = id; } boost::optional<boost::posix_time::ptime> getTimestamp() const; // Falls back to any timestamp if no specific timestamp for the given JID is found. boost::optional<boost::posix_time::ptime> getTimestampFrom(const JID& jid) const; private: std::string id_; JID from_; JID to_; std::vector< std::shared_ptr<Payload> > payloads_; }; } 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 @@ -1,23 +1,20 @@ /* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * 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 <Swiften/FileTransfer/FileTransferManager.h> 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 @@ -20,35 +20,33 @@ #include <Swiften/Base/API.h> #include <Swiften/Elements/DiscoInfo.h> #include <Swiften/FileTransfer/FileTransferOptions.h> #include <Swiften/FileTransfer/IncomingFileTransfer.h> #include <Swiften/FileTransfer/OutgoingFileTransfer.h> #include <Swiften/JID/JID.h> namespace Swift { class ReadBytestream; class SWIFTEN_API FileTransferManager { public: virtual ~FileTransferManager(); virtual OutgoingFileTransfer::ref createOutgoingFileTransfer( const JID& to, const boost::filesystem::path& filepath, const std::string& description, std::shared_ptr<ReadBytestream> bytestream, const FileTransferOptions& = FileTransferOptions()) = 0; virtual OutgoingFileTransfer::ref createOutgoingFileTransfer( const JID& to, const std::string& filename, const std::string& description, const boost::uintmax_t sizeInBytes, const boost::posix_time::ptime& lastModified, std::shared_ptr<ReadBytestream> bytestream, const FileTransferOptions& = FileTransferOptions()) = 0; - static bool isSupportedBy(const DiscoInfo::ref info); - boost::signals2::signal<void (IncomingFileTransfer::ref)> 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 @@ -1,53 +1,53 @@ /* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ /* * Copyright (c) 2013-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include <Swiften/FileTransfer/FileTransferManagerImpl.h> #include <boost/bind.hpp> #include <boost/cstdint.hpp> #include <boost/filesystem.hpp> #include <Swiften/Base/BoostFilesystemVersion.h> #include <Swiften/Base/Log.h> #include <Swiften/Base/Path.h> -#include <Swiften/Base/foreach.h> #include <Swiften/Disco/EntityCapsProvider.h> +#include <Swiften/Disco/FeatureOracle.h> #include <Swiften/Elements/JingleFileTransferFileInfo.h> #include <Swiften/Elements/Presence.h> #include <Swiften/FileTransfer/DefaultFileTransferTransporterFactory.h> #include <Swiften/FileTransfer/IncomingFileTransferManager.h> #include <Swiften/FileTransfer/OutgoingFileTransferManager.h> #include <Swiften/FileTransfer/SOCKS5BytestreamProxiesManager.h> #include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h> #include <Swiften/FileTransfer/SOCKS5BytestreamServerManager.h> #include <Swiften/JID/JID.h> #include <Swiften/Network/ConnectionFactory.h> #include <Swiften/Network/ConnectionServerFactory.h> #include <Swiften/Network/HostAddress.h> #include <Swiften/Network/NATTraverser.h> #include <Swiften/Presence/PresenceOracle.h> #include <Swiften/Queries/IQRouter.h> namespace Swift { FileTransferManagerImpl::FileTransferManagerImpl( const JID& ownJID, JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, PresenceOracle* presOracle, ConnectionFactory* connectionFactory, ConnectionServerFactory* connectionServerFactory, TimerFactory* timerFactory, DomainNameResolver* domainNameResolver, NetworkEnvironment* networkEnvironment, NATTraverser* natTraverser, @@ -72,115 +72,94 @@ FileTransferManagerImpl::FileTransferManagerImpl( outgoingFTManager = new OutgoingFileTransferManager( jingleSessionManager, iqRouter, transporterFactory, timerFactory, crypto); incomingFTManager = new IncomingFileTransferManager( jingleSessionManager, transporterFactory, timerFactory, crypto); incomingFTManager->onIncomingFileTransfer.connect(onIncomingFileTransfer); } FileTransferManagerImpl::~FileTransferManagerImpl() { delete incomingFTManager; delete outgoingFTManager; delete transporterFactory; delete bytestreamProxy; delete s5bServerManager; delete bytestreamRegistry; } void FileTransferManagerImpl::start() { } void FileTransferManagerImpl::stop() { s5bServerManager->stop(); } -boost::optional<JID> 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<Presence::ref> 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<JID>(fullReceipientJID) : boost::optional<JID>(); -} - OutgoingFileTransfer::ref FileTransferManagerImpl::createOutgoingFileTransfer( const JID& to, const boost::filesystem::path& filepath, const std::string& description, std::shared_ptr<ReadBytestream> bytestream, const FileTransferOptions& config) { #if BOOST_FILESYSTEM_VERSION == 2 // TODO: Delete this when boost 1.44 becomes a minimum requirement, and we no longer need v2 std::string filename = filepath.filename(); #else std::string filename = pathToString(filepath.filename()); #endif boost::uintmax_t sizeInBytes = boost::filesystem::file_size(filepath); boost::posix_time::ptime lastModified = boost::posix_time::from_time_t(boost::filesystem::last_write_time(filepath)); return createOutgoingFileTransfer(to, filename, description, sizeInBytes, lastModified, bytestream, config); } OutgoingFileTransfer::ref FileTransferManagerImpl::createOutgoingFileTransfer( const JID& to, const std::string& filename, const std::string& description, const boost::uintmax_t sizeInBytes, const boost::posix_time::ptime& lastModified, std::shared_ptr<ReadBytestream> bytestream, const FileTransferOptions& config) { JingleFileTransferFileInfo fileInfo; fileInfo.setDate(lastModified); fileInfo.setSize(sizeInBytes); fileInfo.setName(filename); fileInfo.setDescription(description); JID receipient = to; if(receipient.isBare()) { - boost::optional<JID> 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(); } } assert(!iqRouter->getJID().isBare()); DiscoInfo::ref capabilities = capsProvider->getCaps(receipient); FileTransferOptions options = config; if (capabilities) { if (!capabilities->hasFeature(DiscoInfo::JingleTransportsS5BFeature)) { options = options.withAssistedAllowed(false).withDirectAllowed(false).withProxiedAllowed(false); } if (!capabilities->hasFeature(DiscoInfo::JingleTransportsIBBFeature)) { options = options.withInBandAllowed(false); } } else { SWIFT_LOG(warning) << "No entity capabilities information for " << receipient.toString() << std::endl; } return outgoingFTManager->createOutgoingFileTransfer(iqRouter->getJID(), receipient, bytestream, fileInfo, options); } } 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,43 +1,42 @@ /* - * Copyright (c) 2010-2015 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include <Swiften/Presence/PresenceOracle.h> #include <queue> #include <boost/bind.hpp> -#include <Swiften/Base/foreach.h> #include <Swiften/Client/StanzaChannel.h> #include <Swiften/Elements/StatusShow.h> #include <Swiften/Roster/XMPPRoster.h> namespace Swift { PresenceOracle::PresenceOracle(StanzaChannel* stanzaChannel, XMPPRoster* roster) : stanzaChannel_(stanzaChannel), xmppRoster_(roster) { stanzaChannel_->onPresenceReceived.connect(boost::bind(&PresenceOracle::handleIncomingPresence, this, _1)); stanzaChannel_->onAvailableChanged.connect(boost::bind(&PresenceOracle::handleStanzaChannelAvailableChanged, this, _1)); xmppRoster_->onJIDRemoved.connect(boost::bind(&PresenceOracle::handleJIDRemoved, this, _1)); } PresenceOracle::~PresenceOracle() { stanzaChannel_->onPresenceReceived.disconnect(boost::bind(&PresenceOracle::handleIncomingPresence, this, _1)); stanzaChannel_->onAvailableChanged.disconnect(boost::bind(&PresenceOracle::handleStanzaChannelAvailableChanged, this, _1)); xmppRoster_->onJIDRemoved.disconnect(boost::bind(&PresenceOracle::handleJIDRemoved, this, _1)); } void PresenceOracle::handleStanzaChannelAvailableChanged(bool available) { if (available) { entries_.clear(); } } void PresenceOracle::handleIncomingPresence(Presence::ref presence) { JID bareJID(presence->getFrom().toBare()); if (presence->getType() == Presence::Subscribe) { } else { Presence::ref passedPresence = presence; @@ -74,146 +73,142 @@ void PresenceOracle::handleJIDRemoved(const JID& removedJID) { if (entries_.find(removedJID) != entries_.end()) { entries_[removedJID].clear(); entries_[removedJID][removedJID] = unavailablePresence; } onPresenceChange(unavailablePresence); } Presence::ref PresenceOracle::getLastPresence(const JID& jid) const { PresencesMap::const_iterator i = entries_.find(jid.toBare()); if (i == entries_.end()) { return Presence::ref(); } PresenceMap presenceMap = i->second; PresenceMap::const_iterator j = presenceMap.find(jid); if (j != presenceMap.end()) { return j->second; } else { return Presence::ref(); } } std::vector<Presence::ref> PresenceOracle::getAllPresence(const JID& bareJID) const { std::vector<Presence::ref> results; PresencesMap::const_iterator i = entries_.find(bareJID); 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; } struct PresenceAccountCmp { 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; } bool operator()(const Presence::ref& a, const Presence::ref& b) { int aPreference = preferenceFromStatusShow(a->getShow()); int bPreference = preferenceFromStatusShow(b->getShow()); if (aPreference != bPreference) { return aPreference < bPreference; } if (a->getPriority() != b->getPriority()) { return a->getPriority() < b->getPriority(); } return a->getFrom().getResource() < b->getFrom().getResource(); } }; typedef std::priority_queue<Presence::ref, std::vector<Presence::ref>, PresenceAccountCmp> PresenceAccountPriorityQueue; Presence::ref PresenceOracle::getActivePresence(const std::vector<Presence::ref> presences) { Presence::ref accountPresence; PresenceAccountPriorityQueue online; PresenceAccountPriorityQueue away; PresenceAccountPriorityQueue offline; - foreach(Presence::ref presence, presences) { + for (auto&& presence : presences) { switch (presence->getShow()) { case StatusShow::Online: online.push(presence); break; case StatusShow::Away: away.push(presence); break; case StatusShow::FFC: online.push(presence); break; case StatusShow::XA: away.push(presence); break; case StatusShow::DND: away.push(presence); break; case StatusShow::None: offline.push(presence); break; } } if (!online.empty()) { accountPresence = online.top(); } else if (!away.empty()) { accountPresence = away.top(); } else if (!offline.empty()) { accountPresence = offline.top(); } return accountPresence; } Presence::ref PresenceOracle::getAccountPresence(const JID& jid) const { Presence::ref accountPresence; std::vector<Presence::ref> allPresences = getAllPresence(jid.toBare()); accountPresence = getActivePresence(allPresences); return accountPresence; } Presence::ref PresenceOracle::getHighestPriorityPresence(const JID& bareJID) const { PresencesMap::const_iterator i = entries_.find(bareJID); 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 @@ -360,63 +360,64 @@ if env["SCONS_STAGE"] == "build" : os.unlink(str(target[0])) os.symlink(source[0].get_contents(), str(target[0])) for alias in myenv["SWIFTEN_LIBRARY_ALIASES"] : myenv.Command(myenv.File(alias), [myenv.Value(swiften_lib[0].name), swiften_lib[0]], symlink) env.Append(UNITTEST_SOURCES = [ File("Avatars/UnitTest/VCardUpdateAvatarManagerTest.cpp"), File("Avatars/UnitTest/VCardAvatarManagerTest.cpp"), File("Avatars/UnitTest/CombinedAvatarProviderTest.cpp"), File("Avatars/UnitTest/AvatarManagerImplTest.cpp"), File("Base/UnitTest/IDGeneratorTest.cpp"), File("Base/UnitTest/SimpleIDGeneratorTest.cpp"), File("Base/UnitTest/StringTest.cpp"), File("Base/UnitTest/DateTimeTest.cpp"), File("Base/UnitTest/ByteArrayTest.cpp"), File("Base/UnitTest/URLTest.cpp"), File("Base/UnitTest/PathTest.cpp"), File("Chat/UnitTest/ChatStateNotifierTest.cpp"), # File("Chat/UnitTest/ChatStateTrackerTest.cpp"), File("Client/UnitTest/ClientSessionTest.cpp"), File("Client/UnitTest/NickResolverTest.cpp"), File("Client/UnitTest/ClientBlockListManagerTest.cpp"), File("Client/UnitTest/BlockListImplTest.cpp"), File("Compress/UnitTest/ZLibCompressorTest.cpp"), File("Compress/UnitTest/ZLibDecompressorTest.cpp"), File("Component/UnitTest/ComponentHandshakeGeneratorTest.cpp"), File("Component/UnitTest/ComponentConnectorTest.cpp"), 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"), File("EventLoop/UnitTest/EventLoopTest.cpp"), File("EventLoop/UnitTest/SimpleEventLoopTest.cpp"), # File("History/UnitTest/SQLiteHistoryManagerTest.cpp"), File("JID/UnitTest/JIDTest.cpp"), File("LinkLocal/UnitTest/LinkLocalConnectorTest.cpp"), File("LinkLocal/UnitTest/LinkLocalServiceBrowserTest.cpp"), File("LinkLocal/UnitTest/LinkLocalServiceInfoTest.cpp"), File("LinkLocal/UnitTest/LinkLocalServiceTest.cpp"), File("MUC/UnitTest/MUCTest.cpp"), File("MUC/UnitTest/MockMUC.cpp"), File("Network/UnitTest/HostAddressTest.cpp"), File("Network/UnitTest/ConnectorTest.cpp"), File("Network/UnitTest/ChainedConnectorTest.cpp"), File("Network/UnitTest/DomainNameServiceQueryTest.cpp"), File("Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp"), File("Network/UnitTest/BOSHConnectionTest.cpp"), File("Network/UnitTest/BOSHConnectionPoolTest.cpp"), File("Parser/PayloadParsers/UnitTest/BlockParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/BodyParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/DiscoInfoParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/DiscoItemsParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/ErrorParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/FormParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/CommandParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/PriorityParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/RawXMLPayloadParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/ResourceBindParserTest.cpp"), |