summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTobias Markmann <tm@ayena.de>2016-04-21 09:02:07 (GMT)
committerKevin Smith <kevin.smith@isode.com>2016-11-10 11:09:16 (GMT)
commitd2ba1ab8a36333523bf794c23226bd044e1717c2 (patch)
tree737ac14bc1dfcec4b80192ea23e7b0e860a2f8d3 /Swiften
parent41c771ec02682c2b344263d29f68eb6452c42dbe (diff)
downloadswift-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/SConscript18
-rw-r--r--Swiften/Disco/FeatureOracle.cpp194
-rw-r--r--Swiften/Disco/FeatureOracle.h16
-rw-r--r--Swiften/Disco/UnitTest/FeatureOracleTest.cpp151
-rw-r--r--Swiften/Elements/Stanza.h5
-rw-r--r--Swiften/FileTransfer/FileTransferManager.cpp15
-rw-r--r--Swiften/FileTransfer/FileTransferManager.h2
-rw-r--r--Swiften/FileTransfer/FileTransferManagerImpl.cpp31
-rw-r--r--Swiften/Presence/PresenceOracle.cpp21
-rw-r--r--Swiften/SConscript3
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"),