diff options
Diffstat (limited to 'Swiften/Disco/FeatureOracle.cpp')
-rw-r--r-- | Swiften/Disco/FeatureOracle.cpp | 224 |
1 files changed, 163 insertions, 61 deletions
diff --git a/Swiften/Disco/FeatureOracle.cpp b/Swiften/Disco/FeatureOracle.cpp index 4e61aa9..2baf87c 100644 --- a/Swiften/Disco/FeatureOracle.cpp +++ b/Swiften/Disco/FeatureOracle.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Isode Limited. + * Copyright (c) 2015-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -8,10 +8,12 @@ #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> @@ -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; - } - else { - return Maybe; - } + 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 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); +} + +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; } -DiscoInfo::ref FeatureOracle::getDiscoResultForJID(const JID& jid) { - DiscoInfo::ref discoInfo; - 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()); - } - } - } - } - discoInfo = boost::make_shared<DiscoInfo>(); - - foreach(const std::string& commonFeature, commonFeatures) { - discoInfo->addFeature(commonFeature); - } - } - else { - // Return the disco result of the full JID. - discoInfo = capsProvider_->getCaps(jid); - } - - return discoInfo; +std::unordered_map<std::string, Tristate> FeatureOracle::getFeaturesForJID(const JID& jid) { + std::unordered_map<std::string, Tristate> supportedFeatures; + if (jid.isBare()) { + // 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()); + } + } + } + + // 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. + auto discoInfo = capsProvider_->getCaps(jid); + if (discoInfo) { + for (auto&& feature : discoInfo->getFeatures()) { + supportedFeatures[feature] = Yes; + } + } + } + + 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; } } |