diff options
Diffstat (limited to 'Swiften/Disco')
31 files changed, 1707 insertions, 1442 deletions
diff --git a/Swiften/Disco/CapsInfoGenerator.cpp b/Swiften/Disco/CapsInfoGenerator.cpp index d2354ea..961ef43 100644 --- a/Swiften/Disco/CapsInfoGenerator.cpp +++ b/Swiften/Disco/CapsInfoGenerator.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2013 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -8,16 +8,15 @@ #include <algorithm> -#include <Swiften/Base/foreach.h> +#include <Swiften/Crypto/CryptoProvider.h> #include <Swiften/Elements/DiscoInfo.h> #include <Swiften/Elements/FormField.h> -#include <Swiften/Crypto/CryptoProvider.h> #include <Swiften/StringCodecs/Base64.h> namespace { - bool compareFields(Swift::FormField::ref f1, Swift::FormField::ref f2) { - return f1->getName() < f2->getName(); - } + bool compareFields(Swift::FormField::ref f1, Swift::FormField::ref f2) { + return f1->getName() < f2->getName(); + } } namespace Swift { @@ -26,39 +25,39 @@ CapsInfoGenerator::CapsInfoGenerator(const std::string& node, CryptoProvider* cr } CapsInfo CapsInfoGenerator::generateCapsInfo(const DiscoInfo& discoInfo) const { - std::string serializedCaps; - - std::vector<DiscoInfo::Identity> identities(discoInfo.getIdentities()); - std::sort(identities.begin(), identities.end()); - foreach (const DiscoInfo::Identity& identity, identities) { - serializedCaps += identity.getCategory() + "/" + identity.getType() + "/" + identity.getLanguage() + "/" + identity.getName() + "<"; - } - - std::vector<std::string> features(discoInfo.getFeatures()); - std::sort(features.begin(), features.end()); - foreach (const std::string& feature, features) { - serializedCaps += feature + "<"; - } - - foreach(Form::ref extension, discoInfo.getExtensions()) { - serializedCaps += extension->getFormType() + "<"; - std::vector<FormField::ref> fields(extension->getFields()); - std::sort(fields.begin(), fields.end(), &compareFields); - foreach(FormField::ref field, fields) { - if (field->getName() == "FORM_TYPE") { - continue; - } - serializedCaps += field->getName() + "<"; - std::vector<std::string> values(field->getValues()); - std::sort(values.begin(), values.end()); - foreach(const std::string& value, values) { - serializedCaps += value + "<"; - } - } - } - - std::string version(Base64::encode(crypto_->getSHA1Hash(createByteArray(serializedCaps)))); - return CapsInfo(node_, version, "sha-1"); + std::string serializedCaps; + + std::vector<DiscoInfo::Identity> identities(discoInfo.getIdentities()); + std::sort(identities.begin(), identities.end()); + for (const auto& identity : identities) { + serializedCaps += identity.getCategory() + "/" + identity.getType() + "/" + identity.getLanguage() + "/" + identity.getName() + "<"; + } + + std::vector<std::string> features(discoInfo.getFeatures()); + std::sort(features.begin(), features.end()); + for (const auto& feature : features) { + serializedCaps += feature + "<"; + } + + for (const auto& extension : discoInfo.getExtensions()) { + serializedCaps += extension->getFormType() + "<"; + std::vector<FormField::ref> fields(extension->getFields()); + std::sort(fields.begin(), fields.end(), &compareFields); + for (const auto& field : fields) { + if (field->getName() == "FORM_TYPE") { + continue; + } + serializedCaps += field->getName() + "<"; + std::vector<std::string> values(field->getValues()); + std::sort(values.begin(), values.end()); + for (const auto& value : values) { + serializedCaps += value + "<"; + } + } + } + + std::string version(Base64::encode(crypto_->getSHA1Hash(createByteArray(serializedCaps)))); + return CapsInfo(node_, version, "sha-1"); } } diff --git a/Swiften/Disco/CapsInfoGenerator.h b/Swiften/Disco/CapsInfoGenerator.h index ebcb8fa..ed1b1bd 100644 --- a/Swiften/Disco/CapsInfoGenerator.h +++ b/Swiften/Disco/CapsInfoGenerator.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -7,21 +7,22 @@ #pragma once #include <string> + #include <Swiften/Base/API.h> #include <Swiften/Elements/CapsInfo.h> namespace Swift { - class DiscoInfo; - class CryptoProvider; + class DiscoInfo; + class CryptoProvider; - class SWIFTEN_API CapsInfoGenerator { - public: - CapsInfoGenerator(const std::string& node, CryptoProvider* crypto); + class SWIFTEN_API CapsInfoGenerator { + public: + CapsInfoGenerator(const std::string& node, CryptoProvider* crypto); - CapsInfo generateCapsInfo(const DiscoInfo& discoInfo) const; + CapsInfo generateCapsInfo(const DiscoInfo& discoInfo) const; - private: - std::string node_; - CryptoProvider* crypto_; - }; + private: + std::string node_; + CryptoProvider* crypto_; + }; } diff --git a/Swiften/Disco/CapsManager.cpp b/Swiften/Disco/CapsManager.cpp index 2f2f909..794cf74 100644 --- a/Swiften/Disco/CapsManager.cpp +++ b/Swiften/Disco/CapsManager.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2013 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -7,77 +7,77 @@ #include <Swiften/Disco/CapsManager.h> #include <boost/bind.hpp> -#include <iostream> +#include <Swiften/Base/Log.h> #include <Swiften/Client/StanzaChannel.h> -#include <Swiften/Disco/CapsStorage.h> #include <Swiften/Disco/CapsInfoGenerator.h> -#include <Swiften/Elements/CapsInfo.h> +#include <Swiften/Disco/CapsStorage.h> #include <Swiften/Disco/GetDiscoInfoRequest.h> +#include <Swiften/Elements/CapsInfo.h> namespace Swift { CapsManager::CapsManager(CapsStorage* capsStorage, StanzaChannel* stanzaChannel, IQRouter* iqRouter, CryptoProvider* crypto) : iqRouter(iqRouter), crypto(crypto), capsStorage(capsStorage), warnOnInvalidHash(true) { - stanzaChannel->onPresenceReceived.connect(boost::bind(&CapsManager::handlePresenceReceived, this, _1)); - stanzaChannel->onAvailableChanged.connect(boost::bind(&CapsManager::handleStanzaChannelAvailableChanged, this, _1)); + stanzaChannel->onPresenceReceived.connect(boost::bind(&CapsManager::handlePresenceReceived, this, _1)); + stanzaChannel->onAvailableChanged.connect(boost::bind(&CapsManager::handleStanzaChannelAvailableChanged, this, _1)); } -void CapsManager::handlePresenceReceived(boost::shared_ptr<Presence> presence) { - boost::shared_ptr<CapsInfo> capsInfo = presence->getPayload<CapsInfo>(); - if (!capsInfo || capsInfo->getHash() != "sha-1" || presence->getPayload<ErrorPayload>()) { - return; - } - std::string hash = capsInfo->getVersion(); - if (capsStorage->getDiscoInfo(hash)) { - return; - } - if (failingCaps.find(std::make_pair(presence->getFrom(), hash)) != failingCaps.end()) { - return; - } - if (requestedDiscoInfos.find(hash) != requestedDiscoInfos.end()) { - fallbacks[hash].insert(std::make_pair(presence->getFrom(), capsInfo->getNode())); - return; - } - requestDiscoInfo(presence->getFrom(), capsInfo->getNode(), hash); +void CapsManager::handlePresenceReceived(std::shared_ptr<Presence> presence) { + std::shared_ptr<CapsInfo> capsInfo = presence->getPayload<CapsInfo>(); + if (!capsInfo || capsInfo->getHash() != "sha-1" || presence->getPayload<ErrorPayload>()) { + return; + } + std::string hash = capsInfo->getVersion(); + if (capsStorage->getDiscoInfo(hash)) { + return; + } + if (failingCaps.find(std::make_pair(presence->getFrom(), hash)) != failingCaps.end()) { + return; + } + if (requestedDiscoInfos.find(hash) != requestedDiscoInfos.end()) { + fallbacks[hash].insert(std::make_pair(presence->getFrom(), capsInfo->getNode())); + return; + } + requestDiscoInfo(presence->getFrom(), capsInfo->getNode(), hash); } void CapsManager::handleStanzaChannelAvailableChanged(bool available) { - if (available) { - failingCaps.clear(); - fallbacks.clear(); - requestedDiscoInfos.clear(); - } + if (available) { + failingCaps.clear(); + fallbacks.clear(); + requestedDiscoInfos.clear(); + } } void CapsManager::handleDiscoInfoReceived(const JID& from, const std::string& hash, DiscoInfo::ref discoInfo, ErrorPayload::ref error) { - requestedDiscoInfos.erase(hash); - if (error || !discoInfo || CapsInfoGenerator("", crypto).generateCapsInfo(*discoInfo.get()).getVersion() != hash) { - if (warnOnInvalidHash && !error && discoInfo) { - std::cerr << "Warning: Caps from " << from.toString() << " do not verify" << std::endl; - } - failingCaps.insert(std::make_pair(from, hash)); - std::map<std::string, std::set< std::pair<JID, std::string> > >::iterator i = fallbacks.find(hash); - if (i != fallbacks.end() && !i->second.empty()) { - std::pair<JID,std::string> fallbackAndNode = *i->second.begin(); - i->second.erase(i->second.begin()); - requestDiscoInfo(fallbackAndNode.first, fallbackAndNode.second, hash); - } - return; - } - fallbacks.erase(hash); - capsStorage->setDiscoInfo(hash, discoInfo); - onCapsAvailable(hash); + requestedDiscoInfos.erase(hash); + if (error || !discoInfo || CapsInfoGenerator("", crypto).generateCapsInfo(*discoInfo.get()).getVersion() != hash) { + if (warnOnInvalidHash && !error && discoInfo) { + SWIFT_LOG(warning) << "Caps from " << from.toString() << " do not verify" << std::endl; + } + failingCaps.insert(std::make_pair(from, hash)); + std::map<std::string, std::set< std::pair<JID, std::string> > >::iterator i = fallbacks.find(hash); + if (i != fallbacks.end() && !i->second.empty()) { + std::pair<JID,std::string> fallbackAndNode = *i->second.begin(); + i->second.erase(i->second.begin()); + requestDiscoInfo(fallbackAndNode.first, fallbackAndNode.second, hash); + } + return; + } + fallbacks.erase(hash); + capsStorage->setDiscoInfo(hash, discoInfo); + onCapsAvailable(hash); } void CapsManager::requestDiscoInfo(const JID& jid, const std::string& node, const std::string& hash) { - GetDiscoInfoRequest::ref request = GetDiscoInfoRequest::create(jid, node + "#" + hash, iqRouter); - request->onResponse.connect(boost::bind(&CapsManager::handleDiscoInfoReceived, this, jid, hash, _1, _2)); - requestedDiscoInfos.insert(hash); - request->send(); + GetDiscoInfoRequest::ref request = GetDiscoInfoRequest::create(jid, node + "#" + hash, iqRouter); + request->onResponse.connect(boost::bind(&CapsManager::handleDiscoInfoReceived, this, jid, hash, _1, _2)); + requestedDiscoInfos.insert(hash); + request->send(); } DiscoInfo::ref CapsManager::getCaps(const std::string& hash) const { - return capsStorage->getDiscoInfo(hash); + return capsStorage->getDiscoInfo(hash); } diff --git a/Swiften/Disco/CapsManager.h b/Swiften/Disco/CapsManager.h index 095fe1f..e57730e 100644 --- a/Swiften/Disco/CapsManager.h +++ b/Swiften/Disco/CapsManager.h @@ -1,53 +1,54 @@ /* - * Copyright (c) 2010-2013 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #pragma once -#include <set> #include <map> +#include <set> + +#include <boost/signals2.hpp> #include <Swiften/Base/API.h> -#include <Swiften/Base/boost_bsignals.h> -#include <Swiften/Elements/Presence.h> -#include <Swiften/Elements/DiscoInfo.h> +#include <Swiften/Disco/CapsProvider.h> #include <Swiften/Elements/CapsInfo.h> +#include <Swiften/Elements/DiscoInfo.h> #include <Swiften/Elements/ErrorPayload.h> -#include <Swiften/Disco/CapsProvider.h> +#include <Swiften/Elements/Presence.h> namespace Swift { - class StanzaChannel; - class IQRouter; - class JID; - class CapsStorage; - class CryptoProvider; - - class SWIFTEN_API CapsManager : public CapsProvider, public boost::bsignals::trackable { - public: - CapsManager(CapsStorage*, StanzaChannel*, IQRouter*, CryptoProvider*); - - DiscoInfo::ref getCaps(const std::string&) const; - - // Mainly for testing purposes - void setWarnOnInvalidHash(bool b) { - warnOnInvalidHash = b; - } - - private: - void handlePresenceReceived(boost::shared_ptr<Presence>); - void handleStanzaChannelAvailableChanged(bool); - void handleDiscoInfoReceived(const JID&, const std::string& hash, DiscoInfo::ref, ErrorPayload::ref); - void requestDiscoInfo(const JID& jid, const std::string& node, const std::string& hash); - - private: - IQRouter* iqRouter; - CryptoProvider* crypto; - CapsStorage* capsStorage; - bool warnOnInvalidHash; - std::set<std::string> requestedDiscoInfos; - std::set< std::pair<JID, std::string> > failingCaps; - std::map<std::string, std::set< std::pair<JID, std::string> > > fallbacks; - }; + class StanzaChannel; + class IQRouter; + class JID; + class CapsStorage; + class CryptoProvider; + + class SWIFTEN_API CapsManager : public CapsProvider, public boost::signals2::trackable { + public: + CapsManager(CapsStorage*, StanzaChannel*, IQRouter*, CryptoProvider*); + + DiscoInfo::ref getCaps(const std::string&) const; + + // Mainly for testing purposes + void setWarnOnInvalidHash(bool b) { + warnOnInvalidHash = b; + } + + private: + void handlePresenceReceived(std::shared_ptr<Presence>); + void handleStanzaChannelAvailableChanged(bool); + void handleDiscoInfoReceived(const JID&, const std::string& hash, DiscoInfo::ref, ErrorPayload::ref); + void requestDiscoInfo(const JID& jid, const std::string& node, const std::string& hash); + + private: + IQRouter* iqRouter; + CryptoProvider* crypto; + CapsStorage* capsStorage; + bool warnOnInvalidHash; + std::set<std::string> requestedDiscoInfos; + std::set< std::pair<JID, std::string> > failingCaps; + std::map<std::string, std::set< std::pair<JID, std::string> > > fallbacks; + }; } diff --git a/Swiften/Disco/CapsMemoryStorage.h b/Swiften/Disco/CapsMemoryStorage.h index 2a81554..15a1fd3 100644 --- a/Swiften/Disco/CapsMemoryStorage.h +++ b/Swiften/Disco/CapsMemoryStorage.h @@ -1,39 +1,39 @@ /* - * Copyright (c) 2010-2015 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #pragma once -#include <boost/shared_ptr.hpp> #include <map> - +#include <memory> #include <string> + #include <Swiften/Base/API.h> #include <Swiften/Disco/CapsStorage.h> namespace Swift { - class SWIFTEN_API CapsMemoryStorage : public CapsStorage { - public: - CapsMemoryStorage() {} + class SWIFTEN_API CapsMemoryStorage : public CapsStorage { + public: + CapsMemoryStorage() {} - virtual DiscoInfo::ref getDiscoInfo(const std::string& hash) const { - CapsMap::const_iterator i = caps.find(hash); - if (i != caps.end()) { - return i->second; - } - else { - return DiscoInfo::ref(); - } - } + virtual DiscoInfo::ref getDiscoInfo(const std::string& hash) const { + CapsMap::const_iterator i = caps.find(hash); + if (i != caps.end()) { + return i->second; + } + else { + return DiscoInfo::ref(); + } + } - virtual void setDiscoInfo(const std::string& hash, DiscoInfo::ref discoInfo) { - caps[hash] = discoInfo; - } + virtual void setDiscoInfo(const std::string& hash, DiscoInfo::ref discoInfo) { + caps[hash] = discoInfo; + } - private: - typedef std::map<std::string, DiscoInfo::ref> CapsMap; - CapsMap caps; - }; + private: + typedef std::map<std::string, DiscoInfo::ref> CapsMap; + CapsMap caps; + }; } diff --git a/Swiften/Disco/CapsProvider.h b/Swiften/Disco/CapsProvider.h index 08202a7..b9e2d21 100644 --- a/Swiften/Disco/CapsProvider.h +++ b/Swiften/Disco/CapsProvider.h @@ -1,25 +1,26 @@ /* - * Copyright (c) 2010-2015 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #pragma once -#include <Swiften/Base/boost_bsignals.h> +#include <boost/signals2.hpp> + #include <Swiften/Base/API.h> -#include <Swiften/Elements/DiscoInfo.h> #include <Swiften/Elements/CapsInfo.h> +#include <Swiften/Elements/DiscoInfo.h> namespace Swift { - - class SWIFTEN_API CapsProvider { - public: - virtual ~CapsProvider() {} - virtual DiscoInfo::ref getCaps(const std::string&) const = 0; + class SWIFTEN_API CapsProvider { + public: + virtual ~CapsProvider() {} + + virtual DiscoInfo::ref getCaps(const std::string&) const = 0; - boost::signal<void (const std::string&)> onCapsAvailable; - }; + boost::signals2::signal<void (const std::string&)> onCapsAvailable; + }; } diff --git a/Swiften/Disco/CapsStorage.h b/Swiften/Disco/CapsStorage.h index 85b8d5d..ebfd3f3 100644 --- a/Swiften/Disco/CapsStorage.h +++ b/Swiften/Disco/CapsStorage.h @@ -1,22 +1,22 @@ /* - * Copyright (c) 2010 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #pragma once -#include <boost/shared_ptr.hpp> +#include <memory> -#include <Swiften/Elements/DiscoInfo.h> #include <Swiften/Base/API.h> +#include <Swiften/Elements/DiscoInfo.h> namespace Swift { - class SWIFTEN_API CapsStorage { - public: - virtual ~CapsStorage(); + class SWIFTEN_API CapsStorage { + public: + virtual ~CapsStorage(); - virtual DiscoInfo::ref getDiscoInfo(const std::string&) const = 0; - virtual void setDiscoInfo(const std::string&, DiscoInfo::ref) = 0; - }; + virtual DiscoInfo::ref getDiscoInfo(const std::string&) const = 0; + virtual void setDiscoInfo(const std::string&, DiscoInfo::ref) = 0; + }; } diff --git a/Swiften/Disco/ClientDiscoManager.cpp b/Swiften/Disco/ClientDiscoManager.cpp index 74336a5..8c90d2d 100644 --- a/Swiften/Disco/ClientDiscoManager.cpp +++ b/Swiften/Disco/ClientDiscoManager.cpp @@ -1,43 +1,43 @@ /* - * Copyright (c) 2010-2013 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include <Swiften/Disco/ClientDiscoManager.h> -#include <Swiften/Disco/DiscoInfoResponder.h> #include <Swiften/Disco/CapsInfoGenerator.h> +#include <Swiften/Disco/DiscoInfoResponder.h> #include <Swiften/Presence/PayloadAddingPresenceSender.h> namespace Swift { ClientDiscoManager::ClientDiscoManager(IQRouter* iqRouter, PresenceSender* presenceSender, CryptoProvider* crypto) : crypto(crypto) { - discoInfoResponder = new DiscoInfoResponder(iqRouter); - discoInfoResponder->start(); - this->presenceSender = new PayloadAddingPresenceSender(presenceSender); + discoInfoResponder = new DiscoInfoResponder(iqRouter); + discoInfoResponder->start(); + this->presenceSender = new PayloadAddingPresenceSender(presenceSender); } ClientDiscoManager::~ClientDiscoManager() { - delete presenceSender; - discoInfoResponder->stop(); - delete discoInfoResponder; + delete presenceSender; + discoInfoResponder->stop(); + delete discoInfoResponder; } void ClientDiscoManager::setCapsNode(const std::string& node) { - capsNode = node; + capsNode = node; } void ClientDiscoManager::setDiscoInfo(const DiscoInfo& discoInfo) { - capsInfo = CapsInfo::ref(new CapsInfo(CapsInfoGenerator(capsNode, crypto).generateCapsInfo(discoInfo))); - discoInfoResponder->clearDiscoInfo(); - discoInfoResponder->setDiscoInfo(discoInfo); - discoInfoResponder->setDiscoInfo(capsInfo->getNode() + "#" + capsInfo->getVersion(), discoInfo); - presenceSender->setPayload(capsInfo); + capsInfo = CapsInfo::ref(new CapsInfo(CapsInfoGenerator(capsNode, crypto).generateCapsInfo(discoInfo))); + discoInfoResponder->clearDiscoInfo(); + discoInfoResponder->setDiscoInfo(discoInfo); + discoInfoResponder->setDiscoInfo(capsInfo->getNode() + "#" + capsInfo->getVersion(), discoInfo); + presenceSender->setPayload(capsInfo); } void ClientDiscoManager::handleConnected() { - presenceSender->reset(); + presenceSender->reset(); } } diff --git a/Swiften/Disco/ClientDiscoManager.h b/Swiften/Disco/ClientDiscoManager.h index 560c69a..595beeb 100644 --- a/Swiften/Disco/ClientDiscoManager.h +++ b/Swiften/Disco/ClientDiscoManager.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2013 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -12,66 +12,67 @@ #include <Swiften/Presence/PayloadAddingPresenceSender.h> namespace Swift { - class IQRouter; - class DiscoInfoResponder; - class PayloadAddingPresenceSender; - class PresenceSender; - class CryptoProvider; + class IQRouter; + class DiscoInfoResponder; + class PayloadAddingPresenceSender; + class PresenceSender; + class CryptoProvider; - /** - * Class responsible for managing outgoing disco information for a client. - * - * The manager will respond to disco#info requests, and add entity capabilities information - * to outgoing presence. - * - * To use this class, call setCapsNode() once with the caps URI of the client. After this, - * call setDiscoInfo() with the capabilities for the client. This can be - * called whenever the capabilities change. - */ - class SWIFTEN_API ClientDiscoManager { - public: - /** - * Constructs the manager - * - * \param iqRouter the router on which requests will be answered - * \param presenceSender the presence sender to which all outgoing presence - * (with caps information) will be sent. - */ - ClientDiscoManager(IQRouter* iqRouter, PresenceSender* presenceSender, CryptoProvider* crypto); - ~ClientDiscoManager(); + /** + * Class responsible for managing outgoing disco information for a client. + * + * The manager will respond to disco#info requests, and add entity capabilities information + * to outgoing presence. + * + * To use this class, call setCapsNode() once with the caps URI of the client. After this, + * call setDiscoInfo() with the capabilities for the client. This can be + * called whenever the capabilities change. + */ + class SWIFTEN_API ClientDiscoManager { + public: + /** + * Constructs the manager + * + * \param iqRouter the router on which requests will be answered + * \param presenceSender the presence sender to which all outgoing presence + * (with caps information) will be sent. + * \param crypto the crypto provider used for cryptographic hash computations. + */ + ClientDiscoManager(IQRouter* iqRouter, PresenceSender* presenceSender, CryptoProvider* crypto); + ~ClientDiscoManager(); - /** - * Needs to be called before calling setDiscoInfo(). - */ - void setCapsNode(const std::string& node); + /** + * Needs to be called before calling setDiscoInfo(). + */ + void setCapsNode(const std::string& node); - /** - * Sets the capabilities of the client. - */ - void setDiscoInfo(const DiscoInfo& info); + /** + * Sets the capabilities of the client. + */ + void setDiscoInfo(const DiscoInfo& info); - /** - * Returns the presence sender through which all outgoing presence - * should be sent. - * The manager will add the necessary caps information, and forward it to - * the presence sender passed at construction time. - */ - PresenceSender* getPresenceSender() const { - return presenceSender; - } + /** + * Returns the presence sender through which all outgoing presence + * should be sent. + * The manager will add the necessary caps information, and forward it to + * the presence sender passed at construction time. + */ + PresenceSender* getPresenceSender() const { + return presenceSender; + } - /** - * Called when the client is connected. - * This resets the presence sender, such that it assumes initial presence - * hasn't been sent yet. - */ - void handleConnected(); + /** + * Called when the client is connected. + * This resets the presence sender, such that it assumes initial presence + * hasn't been sent yet. + */ + void handleConnected(); - private: - PayloadAddingPresenceSender* presenceSender; - CryptoProvider* crypto; - DiscoInfoResponder* discoInfoResponder; - std::string capsNode; - CapsInfo::ref capsInfo; - }; + private: + PayloadAddingPresenceSender* presenceSender; + CryptoProvider* crypto; + DiscoInfoResponder* discoInfoResponder; + std::string capsNode; + CapsInfo::ref capsInfo; + }; } diff --git a/Swiften/Disco/DiscoInfoResponder.cpp b/Swiften/Disco/DiscoInfoResponder.cpp index 3c477c3..c94d299 100644 --- a/Swiften/Disco/DiscoInfoResponder.cpp +++ b/Swiften/Disco/DiscoInfoResponder.cpp @@ -1,14 +1,15 @@ /* - * Copyright (c) 2010 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ -#include <boost/smart_ptr/make_shared.hpp> - #include <Swiften/Disco/DiscoInfoResponder.h> -#include <Swiften/Queries/IQRouter.h> + +#include <memory> + #include <Swiften/Elements/DiscoInfo.h> +#include <Swiften/Queries/IQRouter.h> namespace Swift { @@ -16,34 +17,34 @@ DiscoInfoResponder::DiscoInfoResponder(IQRouter* router) : GetResponder<DiscoInf } void DiscoInfoResponder::clearDiscoInfo() { - info_ = DiscoInfo(); - nodeInfo_.clear(); + info_ = DiscoInfo(); + nodeInfo_.clear(); } void DiscoInfoResponder::setDiscoInfo(const DiscoInfo& info) { - info_ = info; + info_ = info; } void DiscoInfoResponder::setDiscoInfo(const std::string& node, const DiscoInfo& info) { - DiscoInfo newInfo(info); - newInfo.setNode(node); - nodeInfo_[node] = newInfo; + DiscoInfo newInfo(info); + newInfo.setNode(node); + nodeInfo_[node] = newInfo; } -bool DiscoInfoResponder::handleGetRequest(const JID& from, const JID&, const std::string& id, boost::shared_ptr<DiscoInfo> info) { - if (info->getNode().empty()) { - sendResponse(from, id, boost::make_shared<DiscoInfo>(info_)); - } - else { - std::map<std::string,DiscoInfo>::const_iterator i = nodeInfo_.find(info->getNode()); - if (i != nodeInfo_.end()) { - sendResponse(from, id, boost::make_shared<DiscoInfo>((*i).second)); - } - else { - sendError(from, id, ErrorPayload::ItemNotFound, ErrorPayload::Cancel); - } - } - return true; +bool DiscoInfoResponder::handleGetRequest(const JID& from, const JID&, const std::string& id, std::shared_ptr<DiscoInfo> info) { + if (info->getNode().empty()) { + sendResponse(from, id, std::make_shared<DiscoInfo>(info_)); + } + else { + std::map<std::string,DiscoInfo>::const_iterator i = nodeInfo_.find(info->getNode()); + if (i != nodeInfo_.end()) { + sendResponse(from, id, std::make_shared<DiscoInfo>((*i).second)); + } + else { + sendError(from, id, ErrorPayload::ItemNotFound, ErrorPayload::Cancel); + } + } + return true; } } diff --git a/Swiften/Disco/DiscoInfoResponder.h b/Swiften/Disco/DiscoInfoResponder.h index 0ca6336..9995695 100644 --- a/Swiften/Disco/DiscoInfoResponder.h +++ b/Swiften/Disco/DiscoInfoResponder.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -9,25 +9,25 @@ #include <map> #include <Swiften/Base/API.h> -#include <Swiften/Queries/GetResponder.h> #include <Swiften/Elements/DiscoInfo.h> +#include <Swiften/Queries/GetResponder.h> namespace Swift { - class IQRouter; + class IQRouter; - class SWIFTEN_API DiscoInfoResponder : public GetResponder<DiscoInfo> { - public: - DiscoInfoResponder(IQRouter* router); + class SWIFTEN_API DiscoInfoResponder : public GetResponder<DiscoInfo> { + public: + DiscoInfoResponder(IQRouter* router); - void clearDiscoInfo(); - void setDiscoInfo(const DiscoInfo& info); - void setDiscoInfo(const std::string& node, const DiscoInfo& info); + void clearDiscoInfo(); + void setDiscoInfo(const DiscoInfo& info); + void setDiscoInfo(const std::string& node, const DiscoInfo& info); - private: - virtual bool handleGetRequest(const JID& from, const JID& to, const std::string& id, boost::shared_ptr<DiscoInfo> payload); + private: + virtual bool handleGetRequest(const JID& from, const JID& to, const std::string& id, std::shared_ptr<DiscoInfo> payload); - private: - DiscoInfo info_; - std::map<std::string, DiscoInfo> nodeInfo_; - }; + private: + DiscoInfo info_; + std::map<std::string, DiscoInfo> nodeInfo_; + }; } diff --git a/Swiften/Disco/DiscoServiceWalker.cpp b/Swiften/Disco/DiscoServiceWalker.cpp index 5803602..a3f95d2 100644 --- a/Swiften/Disco/DiscoServiceWalker.cpp +++ b/Swiften/Disco/DiscoServiceWalker.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. */ @@ -9,7 +9,6 @@ #include <boost/bind.hpp> #include <Swiften/Base/Log.h> -#include <Swiften/Base/foreach.h> namespace Swift { @@ -18,118 +17,118 @@ DiscoServiceWalker::DiscoServiceWalker(const JID& service, IQRouter* iqRouter, s } void DiscoServiceWalker::beginWalk() { - SWIFT_LOG(debug) << "Starting walk to " << service_ << std::endl; - assert(!active_); - assert(servicesBeingSearched_.empty()); - active_ = true; - walkNode(service_); + SWIFT_LOG(debug) << "Starting walk to " << service_ << std::endl; + assert(!active_); + assert(servicesBeingSearched_.empty()); + active_ = true; + walkNode(service_); } void DiscoServiceWalker::endWalk() { - if (active_) { - SWIFT_LOG(debug) << "Ending walk to " << service_ << std::endl; - foreach (GetDiscoInfoRequest::ref request, pendingDiscoInfoRequests_) { - request->onResponse.disconnect(boost::bind(&DiscoServiceWalker::handleDiscoInfoResponse, this, _1, _2, request)); - } - foreach (GetDiscoItemsRequest::ref request, pendingDiscoItemsRequests_) { - request->onResponse.disconnect(boost::bind(&DiscoServiceWalker::handleDiscoItemsResponse, this, _1, _2, request)); - } - active_ = false; - onWalkAborted(); - } + if (active_) { + SWIFT_LOG(debug) << "Ending walk to " << service_ << std::endl; + for (auto&& request : pendingDiscoInfoRequests_) { + request->onResponse.disconnect(boost::bind(&DiscoServiceWalker::handleDiscoInfoResponse, this, _1, _2, request)); + } + for (auto&& request : pendingDiscoItemsRequests_) { + request->onResponse.disconnect(boost::bind(&DiscoServiceWalker::handleDiscoItemsResponse, this, _1, _2, request)); + } + active_ = false; + onWalkAborted(); + } } void DiscoServiceWalker::walkNode(const JID& jid) { - SWIFT_LOG(debug) << "Walking node " << jid << std::endl; - servicesBeingSearched_.insert(jid); - searchedServices_.insert(jid); - GetDiscoInfoRequest::ref discoInfoRequest = GetDiscoInfoRequest::create(jid, iqRouter_); - discoInfoRequest->onResponse.connect(boost::bind(&DiscoServiceWalker::handleDiscoInfoResponse, this, _1, _2, discoInfoRequest)); - pendingDiscoInfoRequests_.insert(discoInfoRequest); - discoInfoRequest->send(); + SWIFT_LOG(debug) << "Walking node " << jid << std::endl; + servicesBeingSearched_.insert(jid); + searchedServices_.insert(jid); + GetDiscoInfoRequest::ref discoInfoRequest = GetDiscoInfoRequest::create(jid, iqRouter_); + discoInfoRequest->onResponse.connect(boost::bind(&DiscoServiceWalker::handleDiscoInfoResponse, this, _1, _2, discoInfoRequest)); + pendingDiscoInfoRequests_.insert(discoInfoRequest); + discoInfoRequest->send(); } -void DiscoServiceWalker::handleDiscoInfoResponse(boost::shared_ptr<DiscoInfo> info, ErrorPayload::ref error, GetDiscoInfoRequest::ref request) { - /* If we got canceled, don't do anything */ - if (!active_) { - return; - } - - SWIFT_LOG(debug) << "Disco info response from " << request->getReceiver() << std::endl; - - request->onResponse.disconnect(boost::bind(&DiscoServiceWalker::handleDiscoInfoResponse, this, _1, _2, request)); - pendingDiscoInfoRequests_.erase(request); - if (error) { - handleDiscoError(request->getReceiver(), error); - return; - } - - bool couldContainServices = false; - foreach (DiscoInfo::Identity identity, info->getIdentities()) { - if (identity.getCategory() == "server") { - couldContainServices = true; - } - } - bool completed = false; - if (couldContainServices) { - GetDiscoItemsRequest::ref discoItemsRequest = GetDiscoItemsRequest::create(request->getReceiver(), iqRouter_); - discoItemsRequest->onResponse.connect(boost::bind(&DiscoServiceWalker::handleDiscoItemsResponse, this, _1, _2, discoItemsRequest)); - pendingDiscoItemsRequests_.insert(discoItemsRequest); - discoItemsRequest->send(); - } else { - completed = true; - } - onServiceFound(request->getReceiver(), info); - if (completed) { - markNodeCompleted(request->getReceiver()); - } +void DiscoServiceWalker::handleDiscoInfoResponse(std::shared_ptr<DiscoInfo> info, ErrorPayload::ref error, GetDiscoInfoRequest::ref request) { + /* If we got canceled, don't do anything */ + if (!active_) { + return; + } + + SWIFT_LOG(debug) << "Disco info response from " << request->getReceiver() << std::endl; + + request->onResponse.disconnect(boost::bind(&DiscoServiceWalker::handleDiscoInfoResponse, this, _1, _2, request)); + pendingDiscoInfoRequests_.erase(request); + if (error) { + handleDiscoError(request->getReceiver(), error); + return; + } + + bool couldContainServices = false; + for (const auto& identity : info->getIdentities()) { + if (identity.getCategory() == "server") { + couldContainServices = true; + } + } + bool completed = false; + if (couldContainServices) { + GetDiscoItemsRequest::ref discoItemsRequest = GetDiscoItemsRequest::create(request->getReceiver(), iqRouter_); + discoItemsRequest->onResponse.connect(boost::bind(&DiscoServiceWalker::handleDiscoItemsResponse, this, _1, _2, discoItemsRequest)); + pendingDiscoItemsRequests_.insert(discoItemsRequest); + discoItemsRequest->send(); + } else { + completed = true; + } + onServiceFound(request->getReceiver(), info); + if (completed) { + markNodeCompleted(request->getReceiver()); + } } -void DiscoServiceWalker::handleDiscoItemsResponse(boost::shared_ptr<DiscoItems> items, ErrorPayload::ref error, GetDiscoItemsRequest::ref request) { - /* If we got canceled, don't do anything */ - if (!active_) { - return; - } - - SWIFT_LOG(debug) << "Received disco items from " << request->getReceiver() << std::endl; - request->onResponse.disconnect(boost::bind(&DiscoServiceWalker::handleDiscoItemsResponse, this, _1, _2, request)); - pendingDiscoItemsRequests_.erase(request); - if (error) { - handleDiscoError(request->getReceiver(), error); - return; - } - foreach (DiscoItems::Item item, items->getItems()) { - if (item.getNode().empty()) { - /* Don't look at noded items. It's possible that this will exclude some services, - * but I've never seen one in the wild, and it's an easy fix for not looping. - */ - if (std::find(searchedServices_.begin(), searchedServices_.end(), item.getJID()) == searchedServices_.end()) { /* Don't recurse infinitely */ - SWIFT_LOG(debug) << "Received disco item " << item.getJID() << std::endl; - walkNode(item.getJID()); - } - } - } - markNodeCompleted(request->getReceiver()); +void DiscoServiceWalker::handleDiscoItemsResponse(std::shared_ptr<DiscoItems> items, ErrorPayload::ref error, GetDiscoItemsRequest::ref request) { + /* If we got canceled, don't do anything */ + if (!active_) { + return; + } + + SWIFT_LOG(debug) << "Received disco items from " << request->getReceiver() << std::endl; + request->onResponse.disconnect(boost::bind(&DiscoServiceWalker::handleDiscoItemsResponse, this, _1, _2, request)); + pendingDiscoItemsRequests_.erase(request); + if (error) { + handleDiscoError(request->getReceiver(), error); + return; + } + for (auto&& item : items->getItems()) { + if (item.getNode().empty()) { + /* Don't look at noded items. It's possible that this will exclude some services, + * but I've never seen one in the wild, and it's an easy fix for not looping. + */ + if (std::find(searchedServices_.begin(), searchedServices_.end(), item.getJID()) == searchedServices_.end()) { /* Don't recurse infinitely */ + SWIFT_LOG(debug) << "Received disco item " << item.getJID() << std::endl; + walkNode(item.getJID()); + } + } + } + markNodeCompleted(request->getReceiver()); } void DiscoServiceWalker::handleDiscoError(const JID& jid, ErrorPayload::ref /*error*/) { - SWIFT_LOG(debug) << "Disco error from " << jid << std::endl; - markNodeCompleted(jid); + SWIFT_LOG(debug) << "Disco error from " << jid << std::endl; + markNodeCompleted(jid); } void DiscoServiceWalker::markNodeCompleted(const JID& jid) { - SWIFT_LOG(debug) << "Node completed " << jid << std::endl; - servicesBeingSearched_.erase(jid); - /* All results are in */ - if (servicesBeingSearched_.empty()) { - active_ = false; - onWalkComplete(); - } - /* Check if we're on a rampage */ - else if (searchedServices_.size() >= maxSteps_) { - active_ = false; - onWalkComplete(); - } + SWIFT_LOG(debug) << "Node completed " << jid << std::endl; + servicesBeingSearched_.erase(jid); + /* All results are in */ + if (servicesBeingSearched_.empty()) { + active_ = false; + onWalkComplete(); + } + /* Check if we're on a rampage */ + else if (searchedServices_.size() >= maxSteps_) { + active_ = false; + onWalkComplete(); + } } } diff --git a/Swiften/Disco/DiscoServiceWalker.h b/Swiften/Disco/DiscoServiceWalker.h index 643096d..f7e1e6d 100644 --- a/Swiften/Disco/DiscoServiceWalker.h +++ b/Swiften/Disco/DiscoServiceWalker.h @@ -1,75 +1,76 @@ /* - * Copyright (c) 2010 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #pragma once -#include <vector> +#include <memory> #include <set> +#include <string> +#include <vector> + +#include <boost/signals2.hpp> #include <Swiften/Base/API.h> -#include <boost/shared_ptr.hpp> -#include <Swiften/Base/boost_bsignals.h> -#include <string> -#include <Swiften/JID/JID.h> +#include <Swiften/Disco/GetDiscoInfoRequest.h> +#include <Swiften/Disco/GetDiscoItemsRequest.h> #include <Swiften/Elements/DiscoInfo.h> #include <Swiften/Elements/DiscoItems.h> #include <Swiften/Elements/ErrorPayload.h> -#include <Swiften/Disco/GetDiscoInfoRequest.h> -#include <Swiften/Disco/GetDiscoItemsRequest.h> +#include <Swiften/JID/JID.h> namespace Swift { - class IQRouter; - /** - * Recursively walk service discovery trees to find all services offered. - * This stops on any disco item that's not reporting itself as a server. - */ - class SWIFTEN_API DiscoServiceWalker { - public: - DiscoServiceWalker(const JID& service, IQRouter* iqRouter, size_t maxSteps = 200); + class IQRouter; + /** + * Recursively walk service discovery trees to find all services offered. + * This stops on any disco item that's not reporting itself as a server. + */ + class SWIFTEN_API DiscoServiceWalker { + public: + DiscoServiceWalker(const JID& service, IQRouter* iqRouter, size_t maxSteps = 200); - /** - * Start the walk. - * - * Call this exactly once. - */ - void beginWalk(); + /** + * Start the walk. + * + * Call this exactly once. + */ + void beginWalk(); - /** - * End the walk. - */ - void endWalk(); + /** + * End the walk. + */ + void endWalk(); - bool isActive() const { - return active_; - } + bool isActive() const { + return active_; + } - /** Emitted for each service found. */ - boost::signal<void(const JID&, boost::shared_ptr<DiscoInfo>)> onServiceFound; + /** Emitted for each service found. */ + boost::signals2::signal<void(const JID&, std::shared_ptr<DiscoInfo>)> onServiceFound; - /** Emitted when walking is aborted. */ - boost::signal<void()> onWalkAborted; + /** Emitted when walking is aborted. */ + boost::signals2::signal<void()> onWalkAborted; - /** Emitted when walking is complete.*/ - boost::signal<void()> onWalkComplete; + /** Emitted when walking is complete.*/ + boost::signals2::signal<void()> onWalkComplete; - private: - void walkNode(const JID& jid); - void markNodeCompleted(const JID& jid); - void handleDiscoInfoResponse(boost::shared_ptr<DiscoInfo> info, ErrorPayload::ref error, GetDiscoInfoRequest::ref request); - void handleDiscoItemsResponse(boost::shared_ptr<DiscoItems> items, ErrorPayload::ref error, GetDiscoItemsRequest::ref request); - void handleDiscoError(const JID& jid, ErrorPayload::ref error); + private: + void walkNode(const JID& jid); + void markNodeCompleted(const JID& jid); + void handleDiscoInfoResponse(std::shared_ptr<DiscoInfo> info, ErrorPayload::ref error, GetDiscoInfoRequest::ref request); + void handleDiscoItemsResponse(std::shared_ptr<DiscoItems> items, ErrorPayload::ref error, GetDiscoItemsRequest::ref request); + void handleDiscoError(const JID& jid, ErrorPayload::ref error); - private: - JID service_; - IQRouter* iqRouter_; - size_t maxSteps_; - bool active_; - std::set<JID> servicesBeingSearched_; - std::set<JID> searchedServices_; - std::set<GetDiscoInfoRequest::ref> pendingDiscoInfoRequests_; - std::set<GetDiscoItemsRequest::ref> pendingDiscoItemsRequests_; - }; + private: + JID service_; + IQRouter* iqRouter_; + size_t maxSteps_; + bool active_; + std::set<JID> servicesBeingSearched_; + std::set<JID> searchedServices_; + std::set<GetDiscoInfoRequest::ref> pendingDiscoInfoRequests_; + std::set<GetDiscoItemsRequest::ref> pendingDiscoItemsRequests_; + }; } diff --git a/Swiften/Disco/DummyEntityCapsProvider.cpp b/Swiften/Disco/DummyEntityCapsProvider.cpp index 65baf7a..fce38fe 100644 --- a/Swiften/Disco/DummyEntityCapsProvider.cpp +++ b/Swiften/Disco/DummyEntityCapsProvider.cpp @@ -1,21 +1,19 @@ /* - * Copyright (c) 2010 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include <Swiften/Disco/DummyEntityCapsProvider.h> -#include <iostream> - namespace Swift { DiscoInfo::ref DummyEntityCapsProvider::getCaps(const JID& jid) const { - std::map<JID, DiscoInfo::ref>::const_iterator i = caps.find(jid); - if (i != caps.end()) { - return i->second; - } - return DiscoInfo::ref(); + std::map<JID, DiscoInfo::ref>::const_iterator i = caps.find(jid); + if (i != caps.end()) { + return i->second; + } + return DiscoInfo::ref(); } } diff --git a/Swiften/Disco/DummyEntityCapsProvider.h b/Swiften/Disco/DummyEntityCapsProvider.h index d6b2c82..5171c91 100644 --- a/Swiften/Disco/DummyEntityCapsProvider.h +++ b/Swiften/Disco/DummyEntityCapsProvider.h @@ -12,13 +12,13 @@ #include <Swiften/Disco/EntityCapsProvider.h> namespace Swift { - class SWIFTEN_API DummyEntityCapsProvider : public EntityCapsProvider { - public: - DummyEntityCapsProvider() { - } + class SWIFTEN_API DummyEntityCapsProvider : public EntityCapsProvider { + public: + DummyEntityCapsProvider() { + } - DiscoInfo::ref getCaps(const JID& jid) const; + DiscoInfo::ref getCaps(const JID& jid) const; - std::map<JID, DiscoInfo::ref> caps; - }; + std::map<JID, DiscoInfo::ref> caps; + }; } diff --git a/Swiften/Disco/EntityCapsManager.cpp b/Swiften/Disco/EntityCapsManager.cpp index 09519ba..64d90be 100644 --- a/Swiften/Disco/EntityCapsManager.cpp +++ b/Swiften/Disco/EntityCapsManager.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -8,72 +8,72 @@ #include <boost/bind.hpp> -#include <Swiften/Disco/CapsProvider.h> #include <Swiften/Client/StanzaChannel.h> +#include <Swiften/Disco/CapsProvider.h> namespace Swift { EntityCapsManager::EntityCapsManager(CapsProvider* capsProvider, StanzaChannel* stanzaChannel) : capsProvider(capsProvider) { - stanzaChannel->onPresenceReceived.connect(boost::bind(&EntityCapsManager::handlePresenceReceived, this, _1)); - stanzaChannel->onAvailableChanged.connect(boost::bind(&EntityCapsManager::handleStanzaChannelAvailableChanged, this, _1)); - capsProvider->onCapsAvailable.connect(boost::bind(&EntityCapsManager::handleCapsAvailable, this, _1)); + stanzaChannel->onPresenceReceived.connect(boost::bind(&EntityCapsManager::handlePresenceReceived, this, _1)); + stanzaChannel->onAvailableChanged.connect(boost::bind(&EntityCapsManager::handleStanzaChannelAvailableChanged, this, _1)); + capsProvider->onCapsAvailable.connect(boost::bind(&EntityCapsManager::handleCapsAvailable, this, _1)); } -void EntityCapsManager::handlePresenceReceived(boost::shared_ptr<Presence> presence) { - JID from = presence->getFrom(); - if (presence->isAvailable()) { - boost::shared_ptr<CapsInfo> capsInfo = presence->getPayload<CapsInfo>(); - if (!capsInfo || capsInfo->getHash() != "sha-1" || presence->getPayload<ErrorPayload>()) { - return; - } - std::string hash = capsInfo->getVersion(); - std::map<JID, std::string>::iterator i = caps.find(from); - if (i == caps.end() || i->second != hash) { - caps.insert(std::make_pair(from, hash)); - DiscoInfo::ref disco = capsProvider->getCaps(hash); - if (disco) { - onCapsChanged(from); - } - else if (i != caps.end()) { - caps.erase(i); - onCapsChanged(from); - } - } - } - else { - std::map<JID, std::string>::iterator i = caps.find(from); - if (i != caps.end()) { - caps.erase(i); - onCapsChanged(from); - } - } +void EntityCapsManager::handlePresenceReceived(std::shared_ptr<Presence> presence) { + JID from = presence->getFrom(); + if (presence->isAvailable()) { + std::shared_ptr<CapsInfo> capsInfo = presence->getPayload<CapsInfo>(); + if (!capsInfo || capsInfo->getHash() != "sha-1" || presence->getPayload<ErrorPayload>()) { + return; + } + std::string hash = capsInfo->getVersion(); + std::map<JID, std::string>::iterator i = caps.find(from); + if (i == caps.end() || i->second != hash) { + caps.insert(std::make_pair(from, hash)); + DiscoInfo::ref disco = capsProvider->getCaps(hash); + if (disco) { + onCapsChanged(from); + } + else if (i != caps.end()) { + caps.erase(i); + onCapsChanged(from); + } + } + } + else { + std::map<JID, std::string>::iterator i = caps.find(from); + if (i != caps.end()) { + caps.erase(i); + onCapsChanged(from); + } + } } void EntityCapsManager::handleStanzaChannelAvailableChanged(bool available) { - if (available) { - std::map<JID, std::string> capsCopy; - capsCopy.swap(caps); - for (std::map<JID,std::string>::const_iterator i = capsCopy.begin(); i != capsCopy.end(); ++i) { - onCapsChanged(i->first); - } - } + if (available) { + std::map<JID, std::string> capsCopy; + capsCopy.swap(caps); + for (std::map<JID,std::string>::const_iterator i = capsCopy.begin(); i != capsCopy.end(); ++i) { + onCapsChanged(i->first); + } + } } void EntityCapsManager::handleCapsAvailable(const std::string& hash) { - // TODO: Use Boost.Bimap ? - for (std::map<JID,std::string>::const_iterator i = caps.begin(); i != caps.end(); ++i) { - if (i->second == hash) { - onCapsChanged(i->first); - } - } + // TODO: Use Boost.Bimap ? + for (std::map<JID,std::string>::const_iterator i = caps.begin(); i != caps.end(); ++i) { + if (i->second == hash) { + onCapsChanged(i->first); + } + } } DiscoInfo::ref EntityCapsManager::getCaps(const JID& jid) const { - std::map<JID, std::string>::const_iterator i = caps.find(jid); - if (i != caps.end()) { - return capsProvider->getCaps(i->second); - } - return DiscoInfo::ref(); + std::map<JID, std::string>::const_iterator i = caps.find(jid); + if (i != caps.end()) { + return capsProvider->getCaps(i->second); + } + return DiscoInfo::ref(); } } diff --git a/Swiften/Disco/EntityCapsManager.h b/Swiften/Disco/EntityCapsManager.h index 2f6982f..4236326 100644 --- a/Swiften/Disco/EntityCapsManager.h +++ b/Swiften/Disco/EntityCapsManager.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -8,39 +8,40 @@ #include <map> +#include <boost/signals2.hpp> + #include <Swiften/Base/API.h> -#include <Swiften/Base/boost_bsignals.h> -#include <Swiften/Elements/Presence.h> +#include <Swiften/Disco/EntityCapsProvider.h> #include <Swiften/Elements/DiscoInfo.h> #include <Swiften/Elements/ErrorPayload.h> -#include <Swiften/Disco/EntityCapsProvider.h> +#include <Swiften/Elements/Presence.h> namespace Swift { - class StanzaChannel; - class CapsProvider; - - /** - * This class is responsible for gathering and providing - * information about capabilities of entities on the network. - * This information is provided in the form of service discovery - * information. - */ - class SWIFTEN_API EntityCapsManager : public EntityCapsProvider, public boost::bsignals::trackable { - public: - EntityCapsManager(CapsProvider*, StanzaChannel*); - - /** - * Returns the service discovery information of the given JID. - */ - DiscoInfo::ref getCaps(const JID&) const; - - private: - void handlePresenceReceived(boost::shared_ptr<Presence>); - void handleStanzaChannelAvailableChanged(bool); - void handleCapsAvailable(const std::string&); - - private: - CapsProvider* capsProvider; - std::map<JID, std::string> caps; - }; + class StanzaChannel; + class CapsProvider; + + /** + * This class is responsible for gathering and providing + * information about capabilities of entities on the network. + * This information is provided in the form of service discovery + * information. + */ + class SWIFTEN_API EntityCapsManager : public EntityCapsProvider, public boost::signals2::trackable { + public: + EntityCapsManager(CapsProvider*, StanzaChannel*); + + /** + * Returns the service discovery information of the given JID. + */ + DiscoInfo::ref getCaps(const JID&) const; + + private: + void handlePresenceReceived(std::shared_ptr<Presence>); + void handleStanzaChannelAvailableChanged(bool); + void handleCapsAvailable(const std::string&); + + private: + CapsProvider* capsProvider; + std::map<JID, std::string> caps; + }; } diff --git a/Swiften/Disco/EntityCapsProvider.h b/Swiften/Disco/EntityCapsProvider.h index 0e34f02..5f4af18 100644 --- a/Swiften/Disco/EntityCapsProvider.h +++ b/Swiften/Disco/EntityCapsProvider.h @@ -1,34 +1,35 @@ /* - * Copyright (c) 2010 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #pragma once +#include <boost/signals2.hpp> + #include <Swiften/Base/API.h> -#include <Swiften/Base/boost_bsignals.h> -#include <Swiften/JID/JID.h> #include <Swiften/Elements/DiscoInfo.h> +#include <Swiften/JID/JID.h> namespace Swift { - /** - * This class provides information about capabilities of entities on the network. - * This information is provided in the form of service discovery - * information. - */ - class SWIFTEN_API EntityCapsProvider { - public: - virtual ~EntityCapsProvider(); + /** + * This class provides information about capabilities of entities on the network. + * This information is provided in the form of service discovery + * information. + */ + class SWIFTEN_API EntityCapsProvider { + public: + virtual ~EntityCapsProvider(); - /** - * Returns the service discovery information of the given JID. - */ - virtual DiscoInfo::ref getCaps(const JID&) const = 0; + /** + * Returns the service discovery information of the given JID. + */ + virtual DiscoInfo::ref getCaps(const JID&) const = 0; - /** - * Emitted when the capabilities of a JID changes. - */ - boost::signal<void (const JID&)> onCapsChanged; - }; + /** + * Emitted when the capabilities of a JID changes. + */ + boost::signals2::signal<void (const JID&)> onCapsChanged; + }; } 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; } } diff --git a/Swiften/Disco/FeatureOracle.h b/Swiften/Disco/FeatureOracle.h index d579e5a..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 <unordered_map> +#include <unordered_set> + #include <Swiften/Base/API.h> #include <Swiften/Base/Tristate.h> #include <Swiften/Elements/DiscoInfo.h> @@ -20,27 +23,30 @@ 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); - - 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 - */ - DiscoInfo::ref getDiscoResultForJID(const JID& jid); - - Tristate isFeatureSupported(const JID& jid, const std::string& feature); - - private: - EntityCapsProvider* capsProvider_; - PresenceOracle* presenceOracle_; + 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 an std::unordered_map<std::string, Tristate> for. + * @return std::unordered_map<std::string, Tristate> + */ + std::unordered_map<std::string, Tristate> getFeaturesForJID(const JID& jid); + + Tristate isFeatureSupported(const std::unordered_map<std::string, Tristate>& supportedFeatures, const std::string& feature); + + private: + EntityCapsProvider* capsProvider_; + PresenceOracle* presenceOracle_; }; } diff --git a/Swiften/Disco/GetDiscoInfoRequest.h b/Swiften/Disco/GetDiscoInfoRequest.h index a3c1415..1d86c14 100644 --- a/Swiften/Disco/GetDiscoInfoRequest.h +++ b/Swiften/Disco/GetDiscoInfoRequest.h @@ -1,38 +1,38 @@ /* - * Copyright (c) 2010-2015 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #pragma once -#include <boost/smart_ptr/make_shared.hpp> +#include <memory> #include <Swiften/Base/API.h> -#include <Swiften/Queries/GenericRequest.h> #include <Swiften/Elements/DiscoInfo.h> +#include <Swiften/Queries/GenericRequest.h> namespace Swift { - class SWIFTEN_API GetDiscoInfoRequest : public GenericRequest<DiscoInfo> { - public: - typedef boost::shared_ptr<GetDiscoInfoRequest> ref; - - static ref create(const JID& jid, IQRouter* router) { - return ref(new GetDiscoInfoRequest(jid, router)); - } - - static ref create(const JID& jid, const std::string& node, IQRouter* router) { - return ref(new GetDiscoInfoRequest(jid, node, router)); - } - - private: - GetDiscoInfoRequest(const JID& jid, IQRouter* router) : - GenericRequest<DiscoInfo>(IQ::Get, jid, boost::make_shared<DiscoInfo>(), router) { - } - - GetDiscoInfoRequest(const JID& jid, const std::string& node, IQRouter* router) : - GenericRequest<DiscoInfo>(IQ::Get, jid, boost::make_shared<DiscoInfo>(), router) { - getPayloadGeneric()->setNode(node); - } - }; + class SWIFTEN_API GetDiscoInfoRequest : public GenericRequest<DiscoInfo> { + public: + typedef std::shared_ptr<GetDiscoInfoRequest> ref; + + static ref create(const JID& jid, IQRouter* router) { + return ref(new GetDiscoInfoRequest(jid, router)); + } + + static ref create(const JID& jid, const std::string& node, IQRouter* router) { + return ref(new GetDiscoInfoRequest(jid, node, router)); + } + + private: + GetDiscoInfoRequest(const JID& jid, IQRouter* router) : + GenericRequest<DiscoInfo>(IQ::Get, jid, std::make_shared<DiscoInfo>(), router) { + } + + GetDiscoInfoRequest(const JID& jid, const std::string& node, IQRouter* router) : + GenericRequest<DiscoInfo>(IQ::Get, jid, std::make_shared<DiscoInfo>(), router) { + getPayloadGeneric()->setNode(node); + } + }; } diff --git a/Swiften/Disco/GetDiscoItemsRequest.h b/Swiften/Disco/GetDiscoItemsRequest.h index a9cf38b..5b1ccf2 100644 --- a/Swiften/Disco/GetDiscoItemsRequest.h +++ b/Swiften/Disco/GetDiscoItemsRequest.h @@ -1,38 +1,38 @@ /* - * Copyright (c) 2010-2015 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #pragma once -#include <boost/smart_ptr/make_shared.hpp> +#include <memory> #include <Swiften/Base/API.h> -#include <Swiften/Queries/GenericRequest.h> #include <Swiften/Elements/DiscoItems.h> +#include <Swiften/Queries/GenericRequest.h> namespace Swift { - class SWIFTEN_API GetDiscoItemsRequest : public GenericRequest<DiscoItems> { - public: - typedef boost::shared_ptr<GetDiscoItemsRequest> ref; - - static ref create(const JID& jid, IQRouter* router) { - return ref(new GetDiscoItemsRequest(jid, router)); - } - - static ref create(const JID& jid, const std::string& node, IQRouter* router) { - return ref(new GetDiscoItemsRequest(jid, node, router)); - } - - private: - GetDiscoItemsRequest(const JID& jid, IQRouter* router) : - GenericRequest<DiscoItems>(IQ::Get, jid, boost::make_shared<DiscoItems>(), router) { - } - - GetDiscoItemsRequest(const JID& jid, const std::string& node, IQRouter* router) : - GenericRequest<DiscoItems>(IQ::Get, jid, boost::make_shared<DiscoItems>(), router) { - getPayloadGeneric()->setNode(node); - } - }; + class SWIFTEN_API GetDiscoItemsRequest : public GenericRequest<DiscoItems> { + public: + typedef std::shared_ptr<GetDiscoItemsRequest> ref; + + static ref create(const JID& jid, IQRouter* router) { + return ref(new GetDiscoItemsRequest(jid, router)); + } + + static ref create(const JID& jid, const std::string& node, IQRouter* router) { + return ref(new GetDiscoItemsRequest(jid, node, router)); + } + + private: + GetDiscoItemsRequest(const JID& jid, IQRouter* router) : + GenericRequest<DiscoItems>(IQ::Get, jid, std::make_shared<DiscoItems>(), router) { + } + + GetDiscoItemsRequest(const JID& jid, const std::string& node, IQRouter* router) : + GenericRequest<DiscoItems>(IQ::Get, jid, std::make_shared<DiscoItems>(), router) { + getPayloadGeneric()->setNode(node); + } + }; } diff --git a/Swiften/Disco/JIDDiscoInfoResponder.cpp b/Swiften/Disco/JIDDiscoInfoResponder.cpp index 6a632cf..8802bce 100644 --- a/Swiften/Disco/JIDDiscoInfoResponder.cpp +++ b/Swiften/Disco/JIDDiscoInfoResponder.cpp @@ -1,14 +1,15 @@ /* - * Copyright (c) 2010 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ -#include <boost/smart_ptr/make_shared.hpp> - #include <Swiften/Disco/JIDDiscoInfoResponder.h> -#include <Swiften/Queries/IQRouter.h> + +#include <memory> + #include <Swiften/Elements/DiscoInfo.h> +#include <Swiften/Queries/IQRouter.h> namespace Swift { @@ -16,41 +17,41 @@ JIDDiscoInfoResponder::JIDDiscoInfoResponder(IQRouter* router) : GetResponder<Di } void JIDDiscoInfoResponder::clearDiscoInfo(const JID& jid) { - info.erase(jid); + info.erase(jid); } void JIDDiscoInfoResponder::setDiscoInfo(const JID& jid, const DiscoInfo& discoInfo) { - JIDDiscoInfoMap::iterator i = info.insert(std::make_pair(jid, JIDDiscoInfo())).first; - i->second.discoInfo = discoInfo; + JIDDiscoInfoMap::iterator i = info.insert(std::make_pair(jid, JIDDiscoInfo())).first; + i->second.discoInfo = discoInfo; } void JIDDiscoInfoResponder::setDiscoInfo(const JID& jid, const std::string& node, const DiscoInfo& discoInfo) { - JIDDiscoInfoMap::iterator i = info.insert(std::make_pair(jid, JIDDiscoInfo())).first; - DiscoInfo newInfo(discoInfo); - newInfo.setNode(node); - i->second.nodeDiscoInfo[node] = newInfo; + JIDDiscoInfoMap::iterator i = info.insert(std::make_pair(jid, JIDDiscoInfo())).first; + DiscoInfo newInfo(discoInfo); + newInfo.setNode(node); + i->second.nodeDiscoInfo[node] = newInfo; } -bool JIDDiscoInfoResponder::handleGetRequest(const JID& from, const JID& to, const std::string& id, boost::shared_ptr<DiscoInfo> discoInfo) { - JIDDiscoInfoMap::const_iterator i = info.find(to); - if (i != info.end()) { - if (discoInfo->getNode().empty()) { - sendResponse(from, to, id, boost::make_shared<DiscoInfo>(i->second.discoInfo)); - } - else { - std::map<std::string,DiscoInfo>::const_iterator j = i->second.nodeDiscoInfo.find(discoInfo->getNode()); - if (j != i->second.nodeDiscoInfo.end()) { - sendResponse(from, to, id, boost::make_shared<DiscoInfo>(j->second)); - } - else { - sendError(from, to, id, ErrorPayload::ItemNotFound, ErrorPayload::Cancel); - } - } - } - else { - sendError(from, to, id, ErrorPayload::ItemNotFound, ErrorPayload::Cancel); - } - return true; +bool JIDDiscoInfoResponder::handleGetRequest(const JID& from, const JID& to, const std::string& id, std::shared_ptr<DiscoInfo> discoInfo) { + JIDDiscoInfoMap::const_iterator i = info.find(to); + if (i != info.end()) { + if (discoInfo->getNode().empty()) { + sendResponse(from, to, id, std::make_shared<DiscoInfo>(i->second.discoInfo)); + } + else { + std::map<std::string,DiscoInfo>::const_iterator j = i->second.nodeDiscoInfo.find(discoInfo->getNode()); + if (j != i->second.nodeDiscoInfo.end()) { + sendResponse(from, to, id, std::make_shared<DiscoInfo>(j->second)); + } + else { + sendError(from, to, id, ErrorPayload::ItemNotFound, ErrorPayload::Cancel); + } + } + } + else { + sendError(from, to, id, ErrorPayload::ItemNotFound, ErrorPayload::Cancel); + } + return true; } } diff --git a/Swiften/Disco/JIDDiscoInfoResponder.h b/Swiften/Disco/JIDDiscoInfoResponder.h index 2f184bf..1eb6228 100644 --- a/Swiften/Disco/JIDDiscoInfoResponder.h +++ b/Swiften/Disco/JIDDiscoInfoResponder.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -9,30 +9,30 @@ #include <map> #include <Swiften/Base/API.h> -#include <Swiften/Queries/GetResponder.h> #include <Swiften/Elements/DiscoInfo.h> #include <Swiften/JID/JID.h> +#include <Swiften/Queries/GetResponder.h> namespace Swift { - class IQRouter; - - class SWIFTEN_API JIDDiscoInfoResponder : public GetResponder<DiscoInfo> { - public: - JIDDiscoInfoResponder(IQRouter* router); - - void clearDiscoInfo(const JID& jid); - void setDiscoInfo(const JID& jid, const DiscoInfo& info); - void setDiscoInfo(const JID& jid, const std::string& node, const DiscoInfo& info); - - private: - virtual bool handleGetRequest(const JID& from, const JID& to, const std::string& id, boost::shared_ptr<DiscoInfo> payload); - - private: - struct JIDDiscoInfo { - DiscoInfo discoInfo; - std::map<std::string, DiscoInfo> nodeDiscoInfo; - }; - typedef std::map<JID, JIDDiscoInfo> JIDDiscoInfoMap; - JIDDiscoInfoMap info; - }; + class IQRouter; + + class SWIFTEN_API JIDDiscoInfoResponder : public GetResponder<DiscoInfo> { + public: + JIDDiscoInfoResponder(IQRouter* router); + + void clearDiscoInfo(const JID& jid); + void setDiscoInfo(const JID& jid, const DiscoInfo& info); + void setDiscoInfo(const JID& jid, const std::string& node, const DiscoInfo& info); + + private: + virtual bool handleGetRequest(const JID& from, const JID& to, const std::string& id, std::shared_ptr<DiscoInfo> payload); + + private: + struct JIDDiscoInfo { + DiscoInfo discoInfo; + std::map<std::string, DiscoInfo> nodeDiscoInfo; + }; + typedef std::map<JID, JIDDiscoInfo> JIDDiscoInfoMap; + JIDDiscoInfoMap info; + }; } diff --git a/Swiften/Disco/SConscript b/Swiften/Disco/SConscript index 1779e26..15137ae 100644 --- a/Swiften/Disco/SConscript +++ b/Swiften/Disco/SConscript @@ -1,16 +1,16 @@ Import("swiften_env") objects = swiften_env.SwiftenObject([ - "CapsInfoGenerator.cpp", - "CapsManager.cpp", - "EntityCapsManager.cpp", - "EntityCapsProvider.cpp", - "DummyEntityCapsProvider.cpp", - "CapsStorage.cpp", - "ClientDiscoManager.cpp", - "DiscoInfoResponder.cpp", - "JIDDiscoInfoResponder.cpp", - "DiscoServiceWalker.cpp", - "FeatureOracle.cpp", - ]) + "CapsInfoGenerator.cpp", + "CapsManager.cpp", + "EntityCapsManager.cpp", + "EntityCapsProvider.cpp", + "DummyEntityCapsProvider.cpp", + "CapsStorage.cpp", + "ClientDiscoManager.cpp", + "DiscoInfoResponder.cpp", + "JIDDiscoInfoResponder.cpp", + "DiscoServiceWalker.cpp", + "FeatureOracle.cpp", + ]) swiften_env.Append(SWIFTEN_OBJECTS = [objects]) diff --git a/Swiften/Disco/UnitTest/CapsInfoGeneratorTest.cpp b/Swiften/Disco/UnitTest/CapsInfoGeneratorTest.cpp index 328c367..8d27ec5 100644 --- a/Swiften/Disco/UnitTest/CapsInfoGeneratorTest.cpp +++ b/Swiften/Disco/UnitTest/CapsInfoGeneratorTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2013 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -7,80 +7,80 @@ #include <cppunit/extensions/HelperMacros.h> #include <cppunit/extensions/TestFactoryRegistry.h> -#include <Swiften/Elements/DiscoInfo.h> -#include <Swiften/Disco/CapsInfoGenerator.h> #include <Swiften/Crypto/CryptoProvider.h> #include <Swiften/Crypto/PlatformCryptoProvider.h> +#include <Swiften/Disco/CapsInfoGenerator.h> +#include <Swiften/Elements/DiscoInfo.h> using namespace Swift; class CapsInfoGeneratorTest : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(CapsInfoGeneratorTest); - CPPUNIT_TEST(testGenerate_XEP0115SimpleExample); - CPPUNIT_TEST(testGenerate_XEP0115ComplexExample); - CPPUNIT_TEST_SUITE_END(); + CPPUNIT_TEST_SUITE(CapsInfoGeneratorTest); + CPPUNIT_TEST(testGenerate_XEP0115SimpleExample); + CPPUNIT_TEST(testGenerate_XEP0115ComplexExample); + CPPUNIT_TEST_SUITE_END(); + + public: + void setUp() { + crypto = std::shared_ptr<CryptoProvider>(PlatformCryptoProvider::create()); + } - public: - void setUp() { - crypto = boost::shared_ptr<CryptoProvider>(PlatformCryptoProvider::create()); - } + void testGenerate_XEP0115SimpleExample() { + DiscoInfo discoInfo; + discoInfo.addIdentity(DiscoInfo::Identity("Exodus 0.9.1", "client", "pc")); + discoInfo.addFeature("http://jabber.org/protocol/disco#items"); + discoInfo.addFeature("http://jabber.org/protocol/caps"); + discoInfo.addFeature("http://jabber.org/protocol/disco#info"); + discoInfo.addFeature("http://jabber.org/protocol/muc"); - void testGenerate_XEP0115SimpleExample() { - DiscoInfo discoInfo; - discoInfo.addIdentity(DiscoInfo::Identity("Exodus 0.9.1", "client", "pc")); - discoInfo.addFeature("http://jabber.org/protocol/disco#items"); - discoInfo.addFeature("http://jabber.org/protocol/caps"); - discoInfo.addFeature("http://jabber.org/protocol/disco#info"); - discoInfo.addFeature("http://jabber.org/protocol/muc"); + CapsInfoGenerator testling("http://code.google.com/p/exodus", crypto.get()); + CapsInfo result = testling.generateCapsInfo(discoInfo); - CapsInfoGenerator testling("http://code.google.com/p/exodus", crypto.get()); - CapsInfo result = testling.generateCapsInfo(discoInfo); + CPPUNIT_ASSERT_EQUAL(std::string("http://code.google.com/p/exodus"), result.getNode()); + CPPUNIT_ASSERT_EQUAL(std::string("sha-1"), result.getHash()); + CPPUNIT_ASSERT_EQUAL(std::string("QgayPKawpkPSDYmwT/WM94uAlu0="), result.getVersion()); + } - CPPUNIT_ASSERT_EQUAL(std::string("http://code.google.com/p/exodus"), result.getNode()); - CPPUNIT_ASSERT_EQUAL(std::string("sha-1"), result.getHash()); - CPPUNIT_ASSERT_EQUAL(std::string("QgayPKawpkPSDYmwT/WM94uAlu0="), result.getVersion()); - } + void testGenerate_XEP0115ComplexExample() { + DiscoInfo discoInfo; + discoInfo.addIdentity(DiscoInfo::Identity("Psi 0.11", "client", "pc", "en")); + discoInfo.addIdentity(DiscoInfo::Identity("\xce\xa8 0.11", "client", "pc", "el")); + discoInfo.addFeature("http://jabber.org/protocol/disco#items"); + discoInfo.addFeature("http://jabber.org/protocol/caps"); + discoInfo.addFeature("http://jabber.org/protocol/disco#info"); + discoInfo.addFeature("http://jabber.org/protocol/muc"); - void testGenerate_XEP0115ComplexExample() { - DiscoInfo discoInfo; - discoInfo.addIdentity(DiscoInfo::Identity("Psi 0.11", "client", "pc", "en")); - discoInfo.addIdentity(DiscoInfo::Identity("\xce\xa8 0.11", "client", "pc", "el")); - discoInfo.addFeature("http://jabber.org/protocol/disco#items"); - discoInfo.addFeature("http://jabber.org/protocol/caps"); - discoInfo.addFeature("http://jabber.org/protocol/disco#info"); - discoInfo.addFeature("http://jabber.org/protocol/muc"); + Form::ref extension(new Form(Form::ResultType)); + FormField::ref field = std::make_shared<FormField>(FormField::HiddenType, "urn:xmpp:dataforms:softwareinfo"); + field->setName("FORM_TYPE"); + extension->addField(field); + field = std::make_shared<FormField>(FormField::ListMultiType); + field->addValue("ipv6"); + field->addValue("ipv4"); + field->setName("ip_version"); + extension->addField(field); + field = std::make_shared<FormField>(FormField::TextSingleType, "Psi"); + field->setName("software"); + extension->addField(field); + field = std::make_shared<FormField>(FormField::TextSingleType, "0.11"); + field->setName("software_version"); + extension->addField(field); + field = std::make_shared<FormField>(FormField::TextSingleType, "Mac"); + field->setName("os"); + extension->addField(field); + field = std::make_shared<FormField>(FormField::TextSingleType, "10.5.1"); + field->setName("os_version"); + extension->addField(field); + discoInfo.addExtension(extension); - Form::ref extension(new Form(Form::ResultType)); - FormField::ref field = boost::make_shared<FormField>(FormField::HiddenType, "urn:xmpp:dataforms:softwareinfo"); - field->setName("FORM_TYPE"); - extension->addField(field); - field = boost::make_shared<FormField>(FormField::ListMultiType); - field->addValue("ipv6"); - field->addValue("ipv4"); - field->setName("ip_version"); - extension->addField(field); - field = boost::make_shared<FormField>(FormField::TextSingleType, "Psi"); - field->setName("software"); - extension->addField(field); - field = boost::make_shared<FormField>(FormField::TextSingleType, "0.11"); - field->setName("software_version"); - extension->addField(field); - field = boost::make_shared<FormField>(FormField::TextSingleType, "Mac"); - field->setName("os"); - extension->addField(field); - field = boost::make_shared<FormField>(FormField::TextSingleType, "10.5.1"); - field->setName("os_version"); - extension->addField(field); - discoInfo.addExtension(extension); + CapsInfoGenerator testling("http://psi-im.org", crypto.get()); + CapsInfo result = testling.generateCapsInfo(discoInfo); - CapsInfoGenerator testling("http://psi-im.org", crypto.get()); - CapsInfo result = testling.generateCapsInfo(discoInfo); + CPPUNIT_ASSERT_EQUAL(std::string("q07IKJEyjvHSyhy//CH0CxmKi8w="), result.getVersion()); + } - CPPUNIT_ASSERT_EQUAL(std::string("q07IKJEyjvHSyhy//CH0CxmKi8w="), result.getVersion()); - } - - private: - boost::shared_ptr<CryptoProvider> crypto; + private: + std::shared_ptr<CryptoProvider> crypto; }; CPPUNIT_TEST_SUITE_REGISTRATION(CapsInfoGeneratorTest); diff --git a/Swiften/Disco/UnitTest/CapsManagerTest.cpp b/Swiften/Disco/UnitTest/CapsManagerTest.cpp index 5841ef4..153e821 100644 --- a/Swiften/Disco/UnitTest/CapsManagerTest.cpp +++ b/Swiften/Disco/UnitTest/CapsManagerTest.cpp @@ -1,290 +1,291 @@ /* - * Copyright (c) 2010-2013 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ -#include <cppunit/extensions/HelperMacros.h> -#include <cppunit/extensions/TestFactoryRegistry.h> +#include <memory> #include <vector> + #include <boost/bind.hpp> +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include <Swiften/Client/DummyStanzaChannel.h> +#include <Swiften/Crypto/CryptoProvider.h> +#include <Swiften/Crypto/PlatformCryptoProvider.h> +#include <Swiften/Disco/CapsInfoGenerator.h> #include <Swiften/Disco/CapsManager.h> #include <Swiften/Disco/CapsMemoryStorage.h> -#include <Swiften/Disco/CapsInfoGenerator.h> -#include <Swiften/Queries/IQRouter.h> #include <Swiften/Elements/CapsInfo.h> #include <Swiften/Elements/DiscoInfo.h> -#include <Swiften/Client/DummyStanzaChannel.h> -#include <Swiften/Crypto/CryptoProvider.h> -#include <Swiften/Crypto/PlatformCryptoProvider.h> +#include <Swiften/Queries/IQRouter.h> using namespace Swift; class CapsManagerTest : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(CapsManagerTest); - CPPUNIT_TEST(testReceiveNewHashRequestsDisco); - CPPUNIT_TEST(testReceiveSameHashDoesNotRequestDisco); - CPPUNIT_TEST(testReceiveLegacyCapsDoesNotRequestDisco); - CPPUNIT_TEST(testReceiveSameHashFromSameUserAfterFailedDiscoDoesNotRequestDisco); - CPPUNIT_TEST(testReceiveSameHashFromDifferentUserAfterFailedDiscoRequestsDisco); - CPPUNIT_TEST(testReceiveSameHashFromDifferentUserAfterIncorrectVerificationRequestsDisco); - CPPUNIT_TEST(testReceiveDifferentHashFromSameUserAfterFailedDiscoDoesNotRequestDisco); - CPPUNIT_TEST(testReceiveSameHashAfterSuccesfulDiscoDoesNotRequestDisco); - CPPUNIT_TEST(testReceiveSuccesfulDiscoStoresCaps); - CPPUNIT_TEST(testReceiveIncorrectVerificationDiscoDoesNotStoreCaps); - CPPUNIT_TEST(testReceiveFailingDiscoFallsBack); - CPPUNIT_TEST(testReceiveNoDiscoFallsBack); - CPPUNIT_TEST(testReceiveFailingFallbackDiscoFallsBack); - CPPUNIT_TEST(testReceiveSameHashFromFailingUserAfterReconnectRequestsDisco); - CPPUNIT_TEST(testReconnectResetsFallback); - CPPUNIT_TEST(testReconnectResetsRequests); - CPPUNIT_TEST_SUITE_END(); - - public: - void setUp() { - crypto = boost::shared_ptr<CryptoProvider>(PlatformCryptoProvider::create()); - stanzaChannel = new DummyStanzaChannel(); - iqRouter = new IQRouter(stanzaChannel); - storage = new CapsMemoryStorage(); - user1 = JID("user1@bar.com/bla"); - discoInfo1 = boost::make_shared<DiscoInfo>(); - discoInfo1->addFeature("http://swift.im/feature1"); - capsInfo1 = boost::make_shared<CapsInfo>(CapsInfoGenerator("http://node1.im", crypto.get()).generateCapsInfo(*discoInfo1.get())); - capsInfo1alt = boost::make_shared<CapsInfo>(CapsInfoGenerator("http://node2.im", crypto.get()).generateCapsInfo(*discoInfo1.get())); - user2 = JID("user2@foo.com/baz"); - discoInfo2 = boost::make_shared<DiscoInfo>(); - discoInfo2->addFeature("http://swift.im/feature2"); - capsInfo2 = boost::make_shared<CapsInfo>(CapsInfoGenerator("http://node2.im", crypto.get()).generateCapsInfo(*discoInfo2.get())); - user3 = JID("user3@foo.com/baz"); - legacyCapsInfo = boost::make_shared<CapsInfo>("http://swift.im", "ver1", ""); - } - - void tearDown() { - delete storage; - delete iqRouter; - delete stanzaChannel; - } - - void testReceiveNewHashRequestsDisco() { - boost::shared_ptr<CapsManager> testling = createManager(); - sendPresenceWithCaps(user1, capsInfo1); - - CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<DiscoInfo>(0, user1, IQ::Get)); - boost::shared_ptr<DiscoInfo> discoInfo(stanzaChannel->sentStanzas[0]->getPayload<DiscoInfo>()); - CPPUNIT_ASSERT(discoInfo); - CPPUNIT_ASSERT_EQUAL("http://node1.im#" + capsInfo1->getVersion(), discoInfo->getNode()); - } - - void testReceiveSameHashDoesNotRequestDisco() { - boost::shared_ptr<CapsManager> testling = createManager(); - sendPresenceWithCaps(user1, capsInfo1); - stanzaChannel->sentStanzas.clear(); - sendPresenceWithCaps(user1, capsInfo1); - - CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(stanzaChannel->sentStanzas.size())); - } - - void testReceiveLegacyCapsDoesNotRequestDisco() { - boost::shared_ptr<CapsManager> testling = createManager(); - sendPresenceWithCaps(user1, legacyCapsInfo); - - CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(stanzaChannel->sentStanzas.size())); - } - - void testReceiveSameHashAfterSuccesfulDiscoDoesNotRequestDisco() { - boost::shared_ptr<CapsManager> testling = createManager(); - sendPresenceWithCaps(user1, capsInfo1); - sendDiscoInfoResult(discoInfo1); - - stanzaChannel->sentStanzas.clear(); - sendPresenceWithCaps(user1, capsInfo1); - - CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(stanzaChannel->sentStanzas.size())); - } - - void testReceiveSameHashFromSameUserAfterFailedDiscoDoesNotRequestDisco() { - boost::shared_ptr<CapsManager> testling = createManager(); - sendPresenceWithCaps(user1, capsInfo1); - stanzaChannel->onIQReceived(IQ::createError(JID("baz@fum.com/foo"), stanzaChannel->sentStanzas[0]->getID())); - - stanzaChannel->sentStanzas.clear(); - sendPresenceWithCaps(user1, capsInfo1); - - CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(stanzaChannel->sentStanzas.size())); - } - - void testReceiveSameHashFromSameUserAfterIncorrectVerificationDoesNotRequestDisco() { - boost::shared_ptr<CapsManager> testling = createManager(); - sendPresenceWithCaps(user1, capsInfo1); - sendDiscoInfoResult(discoInfo2); - - stanzaChannel->sentStanzas.clear(); - sendPresenceWithCaps(user1, capsInfo1); - - CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(stanzaChannel->sentStanzas.size())); - } - - void testReceiveSameHashFromDifferentUserAfterFailedDiscoRequestsDisco() { - boost::shared_ptr<CapsManager> testling = createManager(); - sendPresenceWithCaps(user1, capsInfo1); - stanzaChannel->onIQReceived(IQ::createError(JID("baz@fum.com/foo"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID())); - - stanzaChannel->sentStanzas.clear(); - sendPresenceWithCaps(user2, capsInfo1); - CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<DiscoInfo>(0, user2, IQ::Get)); - } - - void testReceiveSameHashFromDifferentUserAfterIncorrectVerificationRequestsDisco() { - boost::shared_ptr<CapsManager> testling = createManager(); - sendPresenceWithCaps(user1, capsInfo1); - sendDiscoInfoResult(discoInfo2); - - stanzaChannel->sentStanzas.clear(); - sendPresenceWithCaps(user2, capsInfo1); - CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<DiscoInfo>(0, user2, IQ::Get)); - } - - void testReceiveDifferentHashFromSameUserAfterFailedDiscoDoesNotRequestDisco() { - boost::shared_ptr<CapsManager> testling = createManager(); - sendPresenceWithCaps(user1, capsInfo1); - stanzaChannel->onIQReceived(IQ::createError(JID("baz@fum.com/foo"), stanzaChannel->sentStanzas[0]->getID())); - - stanzaChannel->sentStanzas.clear(); - sendPresenceWithCaps(user1, capsInfo2); - - CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<DiscoInfo>(0, user1, IQ::Get)); - } - - void testReceiveSuccesfulDiscoStoresCaps() { - boost::shared_ptr<CapsManager> testling = createManager(); - sendPresenceWithCaps(user1, capsInfo1); - sendDiscoInfoResult(discoInfo1); - - boost::shared_ptr<DiscoInfo> discoInfo(storage->getDiscoInfo(capsInfo1->getVersion())); - CPPUNIT_ASSERT(discoInfo); - CPPUNIT_ASSERT(discoInfo->hasFeature("http://swift.im/feature1")); - } - - void testReceiveIncorrectVerificationDiscoDoesNotStoreCaps() { - boost::shared_ptr<CapsManager> testling = createManager(); - sendPresenceWithCaps(user1, capsInfo1); - sendDiscoInfoResult(discoInfo2); - - boost::shared_ptr<DiscoInfo> discoInfo(storage->getDiscoInfo(capsInfo1->getVersion())); - CPPUNIT_ASSERT(!discoInfo); - } - - void testReceiveFailingDiscoFallsBack() { - boost::shared_ptr<CapsManager> testling = createManager(); - sendPresenceWithCaps(user1, capsInfo1); - sendPresenceWithCaps(user2, capsInfo1alt); - stanzaChannel->onIQReceived(IQ::createError(JID("baz@fum.com/foo"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID())); - - CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<DiscoInfo>(1, user2, IQ::Get)); - boost::shared_ptr<DiscoInfo> discoInfo(stanzaChannel->sentStanzas[1]->getPayload<DiscoInfo>()); - CPPUNIT_ASSERT(discoInfo); - CPPUNIT_ASSERT_EQUAL("http://node2.im#" + capsInfo1alt->getVersion(), discoInfo->getNode()); - } - - void testReceiveNoDiscoFallsBack() { - boost::shared_ptr<CapsManager> testling = createManager(); - sendPresenceWithCaps(user1, capsInfo1); - sendPresenceWithCaps(user2, capsInfo1alt); - stanzaChannel->onIQReceived(IQ::createResult(JID("baz@fum.com/dum"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID(), boost::shared_ptr<DiscoInfo>())); - - CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<DiscoInfo>(1, user2, IQ::Get)); - boost::shared_ptr<DiscoInfo> discoInfo(stanzaChannel->sentStanzas[1]->getPayload<DiscoInfo>()); - CPPUNIT_ASSERT(discoInfo); - CPPUNIT_ASSERT_EQUAL("http://node2.im#" + capsInfo1alt->getVersion(), discoInfo->getNode()); - } - - void testReceiveFailingFallbackDiscoFallsBack() { - boost::shared_ptr<CapsManager> testling = createManager(); - sendPresenceWithCaps(user1, capsInfo1); - sendPresenceWithCaps(user2, capsInfo1alt); - sendPresenceWithCaps(user3, capsInfo1); - stanzaChannel->onIQReceived(IQ::createError(JID("baz@fum.com/foo"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID())); - stanzaChannel->onIQReceived(IQ::createError(JID("baz@fum.com/foo"), stanzaChannel->sentStanzas[1]->getTo(), stanzaChannel->sentStanzas[1]->getID())); - - CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<DiscoInfo>(2, user3, IQ::Get)); - } - - void testReceiveSameHashFromFailingUserAfterReconnectRequestsDisco() { - boost::shared_ptr<CapsManager> testling = createManager(); - sendPresenceWithCaps(user1, capsInfo1); - stanzaChannel->onIQReceived(IQ::createError(JID("baz@fum.com/foo"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID())); - stanzaChannel->setAvailable(false); - stanzaChannel->setAvailable(true); - stanzaChannel->sentStanzas.clear(); - - sendPresenceWithCaps(user1, capsInfo1); - - CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<DiscoInfo>(0, user1, IQ::Get)); - } - - void testReconnectResetsFallback() { - boost::shared_ptr<CapsManager> testling = createManager(); - sendPresenceWithCaps(user1, capsInfo1); - sendPresenceWithCaps(user2, capsInfo1alt); - stanzaChannel->setAvailable(false); - stanzaChannel->setAvailable(true); - stanzaChannel->sentStanzas.clear(); - sendPresenceWithCaps(user1, capsInfo1); - stanzaChannel->onIQReceived(IQ::createError(JID("baz@fum.com/foo"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID())); - - CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(stanzaChannel->sentStanzas.size())); - } - - void testReconnectResetsRequests() { - boost::shared_ptr<CapsManager> testling = createManager(); - sendPresenceWithCaps(user1, capsInfo1); - stanzaChannel->sentStanzas.clear(); - stanzaChannel->setAvailable(false); - stanzaChannel->setAvailable(true); - sendPresenceWithCaps(user1, capsInfo1); - - CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<DiscoInfo>(0, user1, IQ::Get)); - } - - private: - boost::shared_ptr<CapsManager> createManager() { - boost::shared_ptr<CapsManager> manager(new CapsManager(storage, stanzaChannel, iqRouter, crypto.get())); - manager->setWarnOnInvalidHash(false); - //manager->onCapsChanged.connect(boost::bind(&CapsManagerTest::handleCapsChanged, this, _1)); - return manager; - } - - void handleCapsChanged(const JID& jid) { - changes.push_back(jid); - } - - void sendPresenceWithCaps(const JID& jid, boost::shared_ptr<CapsInfo> caps) { - boost::shared_ptr<Presence> presence(new Presence()); - presence->setFrom(jid); - presence->addPayload(caps); - stanzaChannel->onPresenceReceived(presence); - } - - void sendDiscoInfoResult(boost::shared_ptr<DiscoInfo> discoInfo) { - stanzaChannel->onIQReceived(IQ::createResult(JID("baz@fum.com/dum"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID(), discoInfo)); - } - - private: - DummyStanzaChannel* stanzaChannel; - IQRouter* iqRouter; - CapsStorage* storage; - std::vector<JID> changes; - JID user1; - boost::shared_ptr<DiscoInfo> discoInfo1; - boost::shared_ptr<CapsInfo> capsInfo1; - boost::shared_ptr<CapsInfo> capsInfo1alt; - JID user2; - boost::shared_ptr<DiscoInfo> discoInfo2; - boost::shared_ptr<CapsInfo> capsInfo2; - boost::shared_ptr<CapsInfo> legacyCapsInfo; - JID user3; - boost::shared_ptr<CryptoProvider> crypto; + CPPUNIT_TEST_SUITE(CapsManagerTest); + CPPUNIT_TEST(testReceiveNewHashRequestsDisco); + CPPUNIT_TEST(testReceiveSameHashDoesNotRequestDisco); + CPPUNIT_TEST(testReceiveLegacyCapsDoesNotRequestDisco); + CPPUNIT_TEST(testReceiveSameHashFromSameUserAfterFailedDiscoDoesNotRequestDisco); + CPPUNIT_TEST(testReceiveSameHashFromDifferentUserAfterFailedDiscoRequestsDisco); + CPPUNIT_TEST(testReceiveSameHashFromDifferentUserAfterIncorrectVerificationRequestsDisco); + CPPUNIT_TEST(testReceiveDifferentHashFromSameUserAfterFailedDiscoDoesNotRequestDisco); + CPPUNIT_TEST(testReceiveSameHashAfterSuccesfulDiscoDoesNotRequestDisco); + CPPUNIT_TEST(testReceiveSuccesfulDiscoStoresCaps); + CPPUNIT_TEST(testReceiveIncorrectVerificationDiscoDoesNotStoreCaps); + CPPUNIT_TEST(testReceiveFailingDiscoFallsBack); + CPPUNIT_TEST(testReceiveNoDiscoFallsBack); + CPPUNIT_TEST(testReceiveFailingFallbackDiscoFallsBack); + CPPUNIT_TEST(testReceiveSameHashFromFailingUserAfterReconnectRequestsDisco); + CPPUNIT_TEST(testReconnectResetsFallback); + CPPUNIT_TEST(testReconnectResetsRequests); + CPPUNIT_TEST_SUITE_END(); + + public: + void setUp() { + crypto = std::shared_ptr<CryptoProvider>(PlatformCryptoProvider::create()); + stanzaChannel = std::unique_ptr<DummyStanzaChannel>(new DummyStanzaChannel()); + iqRouter = std::unique_ptr<IQRouter>(new IQRouter(stanzaChannel.get())); + storage = std::unique_ptr<CapsMemoryStorage>(new CapsMemoryStorage()); + user1 = JID("user1@bar.com/bla"); + discoInfo1 = std::make_shared<DiscoInfo>(); + discoInfo1->addFeature("http://swift.im/feature1"); + capsInfo1 = std::make_shared<CapsInfo>(CapsInfoGenerator("http://node1.im", crypto.get()).generateCapsInfo(*discoInfo1.get())); + capsInfo1alt = std::make_shared<CapsInfo>(CapsInfoGenerator("http://node2.im", crypto.get()).generateCapsInfo(*discoInfo1.get())); + user2 = JID("user2@foo.com/baz"); + discoInfo2 = std::make_shared<DiscoInfo>(); + discoInfo2->addFeature("http://swift.im/feature2"); + capsInfo2 = std::make_shared<CapsInfo>(CapsInfoGenerator("http://node2.im", crypto.get()).generateCapsInfo(*discoInfo2.get())); + user3 = JID("user3@foo.com/baz"); + legacyCapsInfo = std::make_shared<CapsInfo>("http://swift.im", "ver1", ""); + } + + void tearDown() { + iqRouter.reset(); + } + + void testReceiveNewHashRequestsDisco() { + std::shared_ptr<CapsManager> testling = createManager(); + sendPresenceWithCaps(user1, capsInfo1); + + CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<DiscoInfo>(0, user1, IQ::Get)); + std::shared_ptr<DiscoInfo> discoInfo(stanzaChannel->sentStanzas[0]->getPayload<DiscoInfo>()); + CPPUNIT_ASSERT(discoInfo); + CPPUNIT_ASSERT_EQUAL("http://node1.im#" + capsInfo1->getVersion(), discoInfo->getNode()); + } + + void testReceiveSameHashDoesNotRequestDisco() { + std::shared_ptr<CapsManager> testling = createManager(); + sendPresenceWithCaps(user1, capsInfo1); + stanzaChannel->sentStanzas.clear(); + sendPresenceWithCaps(user1, capsInfo1); + + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(stanzaChannel->sentStanzas.size())); + } + + void testReceiveLegacyCapsDoesNotRequestDisco() { + std::shared_ptr<CapsManager> testling = createManager(); + sendPresenceWithCaps(user1, legacyCapsInfo); + + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(stanzaChannel->sentStanzas.size())); + } + + void testReceiveSameHashAfterSuccesfulDiscoDoesNotRequestDisco() { + std::shared_ptr<CapsManager> testling = createManager(); + sendPresenceWithCaps(user1, capsInfo1); + sendDiscoInfoResult(discoInfo1); + + stanzaChannel->sentStanzas.clear(); + sendPresenceWithCaps(user1, capsInfo1); + + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(stanzaChannel->sentStanzas.size())); + } + + void testReceiveSameHashFromSameUserAfterFailedDiscoDoesNotRequestDisco() { + std::shared_ptr<CapsManager> testling = createManager(); + sendPresenceWithCaps(user1, capsInfo1); + stanzaChannel->onIQReceived(IQ::createError(JID("baz@fum.com/foo"), stanzaChannel->sentStanzas[0]->getID())); + + stanzaChannel->sentStanzas.clear(); + sendPresenceWithCaps(user1, capsInfo1); + + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(stanzaChannel->sentStanzas.size())); + } + + void testReceiveSameHashFromSameUserAfterIncorrectVerificationDoesNotRequestDisco() { + std::shared_ptr<CapsManager> testling = createManager(); + sendPresenceWithCaps(user1, capsInfo1); + sendDiscoInfoResult(discoInfo2); + + stanzaChannel->sentStanzas.clear(); + sendPresenceWithCaps(user1, capsInfo1); + + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(stanzaChannel->sentStanzas.size())); + } + + void testReceiveSameHashFromDifferentUserAfterFailedDiscoRequestsDisco() { + std::shared_ptr<CapsManager> testling = createManager(); + sendPresenceWithCaps(user1, capsInfo1); + stanzaChannel->onIQReceived(IQ::createError(JID("baz@fum.com/foo"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID())); + + stanzaChannel->sentStanzas.clear(); + sendPresenceWithCaps(user2, capsInfo1); + CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<DiscoInfo>(0, user2, IQ::Get)); + } + + void testReceiveSameHashFromDifferentUserAfterIncorrectVerificationRequestsDisco() { + std::shared_ptr<CapsManager> testling = createManager(); + sendPresenceWithCaps(user1, capsInfo1); + sendDiscoInfoResult(discoInfo2); + + stanzaChannel->sentStanzas.clear(); + sendPresenceWithCaps(user2, capsInfo1); + CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<DiscoInfo>(0, user2, IQ::Get)); + } + + void testReceiveDifferentHashFromSameUserAfterFailedDiscoDoesNotRequestDisco() { + std::shared_ptr<CapsManager> testling = createManager(); + sendPresenceWithCaps(user1, capsInfo1); + stanzaChannel->onIQReceived(IQ::createError(JID("baz@fum.com/foo"), stanzaChannel->sentStanzas[0]->getID())); + + stanzaChannel->sentStanzas.clear(); + sendPresenceWithCaps(user1, capsInfo2); + + CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<DiscoInfo>(0, user1, IQ::Get)); + } + + void testReceiveSuccesfulDiscoStoresCaps() { + std::shared_ptr<CapsManager> testling = createManager(); + sendPresenceWithCaps(user1, capsInfo1); + sendDiscoInfoResult(discoInfo1); + + std::shared_ptr<DiscoInfo> discoInfo(storage->getDiscoInfo(capsInfo1->getVersion())); + CPPUNIT_ASSERT(discoInfo); + CPPUNIT_ASSERT(discoInfo->hasFeature("http://swift.im/feature1")); + } + + void testReceiveIncorrectVerificationDiscoDoesNotStoreCaps() { + std::shared_ptr<CapsManager> testling = createManager(); + sendPresenceWithCaps(user1, capsInfo1); + sendDiscoInfoResult(discoInfo2); + + std::shared_ptr<DiscoInfo> discoInfo(storage->getDiscoInfo(capsInfo1->getVersion())); + CPPUNIT_ASSERT(!discoInfo); + } + + void testReceiveFailingDiscoFallsBack() { + std::shared_ptr<CapsManager> testling = createManager(); + sendPresenceWithCaps(user1, capsInfo1); + sendPresenceWithCaps(user2, capsInfo1alt); + stanzaChannel->onIQReceived(IQ::createError(JID("baz@fum.com/foo"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID())); + + CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<DiscoInfo>(1, user2, IQ::Get)); + std::shared_ptr<DiscoInfo> discoInfo(stanzaChannel->sentStanzas[1]->getPayload<DiscoInfo>()); + CPPUNIT_ASSERT(discoInfo); + CPPUNIT_ASSERT_EQUAL("http://node2.im#" + capsInfo1alt->getVersion(), discoInfo->getNode()); + } + + void testReceiveNoDiscoFallsBack() { + std::shared_ptr<CapsManager> testling = createManager(); + sendPresenceWithCaps(user1, capsInfo1); + sendPresenceWithCaps(user2, capsInfo1alt); + stanzaChannel->onIQReceived(IQ::createResult(JID("baz@fum.com/dum"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID(), std::shared_ptr<DiscoInfo>())); + + CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<DiscoInfo>(1, user2, IQ::Get)); + std::shared_ptr<DiscoInfo> discoInfo(stanzaChannel->sentStanzas[1]->getPayload<DiscoInfo>()); + CPPUNIT_ASSERT(discoInfo); + CPPUNIT_ASSERT_EQUAL("http://node2.im#" + capsInfo1alt->getVersion(), discoInfo->getNode()); + } + + void testReceiveFailingFallbackDiscoFallsBack() { + std::shared_ptr<CapsManager> testling = createManager(); + sendPresenceWithCaps(user1, capsInfo1); + sendPresenceWithCaps(user2, capsInfo1alt); + sendPresenceWithCaps(user3, capsInfo1); + stanzaChannel->onIQReceived(IQ::createError(JID("baz@fum.com/foo"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID())); + stanzaChannel->onIQReceived(IQ::createError(JID("baz@fum.com/foo"), stanzaChannel->sentStanzas[1]->getTo(), stanzaChannel->sentStanzas[1]->getID())); + + CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<DiscoInfo>(2, user3, IQ::Get)); + } + + void testReceiveSameHashFromFailingUserAfterReconnectRequestsDisco() { + std::shared_ptr<CapsManager> testling = createManager(); + sendPresenceWithCaps(user1, capsInfo1); + stanzaChannel->onIQReceived(IQ::createError(JID("baz@fum.com/foo"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID())); + stanzaChannel->setAvailable(false); + stanzaChannel->setAvailable(true); + stanzaChannel->sentStanzas.clear(); + + sendPresenceWithCaps(user1, capsInfo1); + + CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<DiscoInfo>(0, user1, IQ::Get)); + } + + void testReconnectResetsFallback() { + std::shared_ptr<CapsManager> testling = createManager(); + sendPresenceWithCaps(user1, capsInfo1); + sendPresenceWithCaps(user2, capsInfo1alt); + stanzaChannel->setAvailable(false); + stanzaChannel->setAvailable(true); + stanzaChannel->sentStanzas.clear(); + sendPresenceWithCaps(user1, capsInfo1); + stanzaChannel->onIQReceived(IQ::createError(JID("baz@fum.com/foo"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID())); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(stanzaChannel->sentStanzas.size())); + } + + void testReconnectResetsRequests() { + std::shared_ptr<CapsManager> testling = createManager(); + sendPresenceWithCaps(user1, capsInfo1); + stanzaChannel->sentStanzas.clear(); + stanzaChannel->setAvailable(false); + stanzaChannel->setAvailable(true); + sendPresenceWithCaps(user1, capsInfo1); + + CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<DiscoInfo>(0, user1, IQ::Get)); + } + + private: + std::shared_ptr<CapsManager> createManager() { + std::shared_ptr<CapsManager> manager(new CapsManager(storage.get(), stanzaChannel.get(), iqRouter.get(), crypto.get())); + manager->setWarnOnInvalidHash(false); + //manager->onCapsChanged.connect(boost::bind(&CapsManagerTest::handleCapsChanged, this, _1)); + return manager; + } + + void handleCapsChanged(const JID& jid) { + changes.push_back(jid); + } + + void sendPresenceWithCaps(const JID& jid, std::shared_ptr<CapsInfo> caps) { + std::shared_ptr<Presence> presence(new Presence()); + presence->setFrom(jid); + presence->addPayload(caps); + stanzaChannel->onPresenceReceived(presence); + } + + void sendDiscoInfoResult(std::shared_ptr<DiscoInfo> discoInfo) { + stanzaChannel->onIQReceived(IQ::createResult(JID("baz@fum.com/dum"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID(), discoInfo)); + } + + private: + std::unique_ptr<DummyStanzaChannel> stanzaChannel; + std::unique_ptr<IQRouter> iqRouter; + std::unique_ptr<CapsStorage> storage; + std::vector<JID> changes; + JID user1; + std::shared_ptr<DiscoInfo> discoInfo1; + std::shared_ptr<CapsInfo> capsInfo1; + std::shared_ptr<CapsInfo> capsInfo1alt; + JID user2; + std::shared_ptr<DiscoInfo> discoInfo2; + std::shared_ptr<CapsInfo> capsInfo2; + std::shared_ptr<CapsInfo> legacyCapsInfo; + JID user3; + std::shared_ptr<CryptoProvider> crypto; }; CPPUNIT_TEST_SUITE_REGISTRATION(CapsManagerTest); diff --git a/Swiften/Disco/UnitTest/DiscoInfoResponderTest.cpp b/Swiften/Disco/UnitTest/DiscoInfoResponderTest.cpp index 62760f0..45dc959 100644 --- a/Swiften/Disco/UnitTest/DiscoInfoResponderTest.cpp +++ b/Swiften/Disco/UnitTest/DiscoInfoResponderTest.cpp @@ -1,96 +1,97 @@ /* - * Copyright (c) 2010 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ +#include <typeinfo> + #include <cppunit/extensions/HelperMacros.h> #include <cppunit/extensions/TestFactoryRegistry.h> -#include <typeinfo> #include <Swiften/Disco/DiscoInfoResponder.h> -#include <Swiften/Queries/IQRouter.h> #include <Swiften/Queries/DummyIQChannel.h> +#include <Swiften/Queries/IQRouter.h> using namespace Swift; class DiscoInfoResponderTest : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(DiscoInfoResponderTest); - CPPUNIT_TEST(testHandleRequest_GetToplevelInfo); - CPPUNIT_TEST(testHandleRequest_GetNodeInfo); - CPPUNIT_TEST(testHandleRequest_GetInvalidNodeInfo); - CPPUNIT_TEST_SUITE_END(); - - public: - void setUp() { - channel_ = new DummyIQChannel(); - router_ = new IQRouter(channel_); - } - - void tearDown() { - delete router_; - delete channel_; - } - - void testHandleRequest_GetToplevelInfo() { - DiscoInfoResponder testling(router_); - testling.start(); - DiscoInfo discoInfo; - discoInfo.addFeature("foo"); - testling.setDiscoInfo(discoInfo); - - boost::shared_ptr<DiscoInfo> query(new DiscoInfo()); - channel_->onIQReceived(IQ::createRequest(IQ::Get, JID("foo@bar.com"), "id-1", query)); - - CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size())); - boost::shared_ptr<DiscoInfo> payload(channel_->iqs_[0]->getPayload<DiscoInfo>()); - CPPUNIT_ASSERT(payload); - CPPUNIT_ASSERT_EQUAL(std::string(""), payload->getNode()); - CPPUNIT_ASSERT(payload->hasFeature("foo")); - - testling.stop(); - } - - void testHandleRequest_GetNodeInfo() { - DiscoInfoResponder testling(router_); - testling.start(); - DiscoInfo discoInfo; - discoInfo.addFeature("foo"); - testling.setDiscoInfo(discoInfo); - DiscoInfo discoInfoBar; - discoInfoBar.addFeature("bar"); - testling.setDiscoInfo("bar-node", discoInfoBar); - - boost::shared_ptr<DiscoInfo> query(new DiscoInfo()); - query->setNode("bar-node"); - channel_->onIQReceived(IQ::createRequest(IQ::Get, JID("foo@bar.com"), "id-1", query)); - - CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size())); - boost::shared_ptr<DiscoInfo> payload(channel_->iqs_[0]->getPayload<DiscoInfo>()); - CPPUNIT_ASSERT(payload); - CPPUNIT_ASSERT_EQUAL(std::string("bar-node"), payload->getNode()); - CPPUNIT_ASSERT(payload->hasFeature("bar")); - - testling.stop(); - } - - void testHandleRequest_GetInvalidNodeInfo() { - DiscoInfoResponder testling(router_); - boost::shared_ptr<DiscoInfo> query(new DiscoInfo()); - query->setNode("bar-node"); - channel_->onIQReceived(IQ::createRequest(IQ::Get, JID("foo@bar.com"), "id-1", query)); - testling.start(); - - CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size())); - boost::shared_ptr<ErrorPayload> payload(channel_->iqs_[0]->getPayload<ErrorPayload>()); - CPPUNIT_ASSERT(payload); - - testling.stop(); - } - - private: - IQRouter* router_; - DummyIQChannel* channel_; + CPPUNIT_TEST_SUITE(DiscoInfoResponderTest); + CPPUNIT_TEST(testHandleRequest_GetToplevelInfo); + CPPUNIT_TEST(testHandleRequest_GetNodeInfo); + CPPUNIT_TEST(testHandleRequest_GetInvalidNodeInfo); + CPPUNIT_TEST_SUITE_END(); + + public: + void setUp() { + channel_ = new DummyIQChannel(); + router_ = new IQRouter(channel_); + } + + void tearDown() { + delete router_; + delete channel_; + } + + void testHandleRequest_GetToplevelInfo() { + DiscoInfoResponder testling(router_); + testling.start(); + DiscoInfo discoInfo; + discoInfo.addFeature("foo"); + testling.setDiscoInfo(discoInfo); + + std::shared_ptr<DiscoInfo> query(new DiscoInfo()); + channel_->onIQReceived(IQ::createRequest(IQ::Get, JID("foo@bar.com"), "id-1", query)); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size())); + std::shared_ptr<DiscoInfo> payload(channel_->iqs_[0]->getPayload<DiscoInfo>()); + CPPUNIT_ASSERT(payload); + CPPUNIT_ASSERT_EQUAL(std::string(""), payload->getNode()); + CPPUNIT_ASSERT(payload->hasFeature("foo")); + + testling.stop(); + } + + void testHandleRequest_GetNodeInfo() { + DiscoInfoResponder testling(router_); + testling.start(); + DiscoInfo discoInfo; + discoInfo.addFeature("foo"); + testling.setDiscoInfo(discoInfo); + DiscoInfo discoInfoBar; + discoInfoBar.addFeature("bar"); + testling.setDiscoInfo("bar-node", discoInfoBar); + + std::shared_ptr<DiscoInfo> query(new DiscoInfo()); + query->setNode("bar-node"); + channel_->onIQReceived(IQ::createRequest(IQ::Get, JID("foo@bar.com"), "id-1", query)); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size())); + std::shared_ptr<DiscoInfo> payload(channel_->iqs_[0]->getPayload<DiscoInfo>()); + CPPUNIT_ASSERT(payload); + CPPUNIT_ASSERT_EQUAL(std::string("bar-node"), payload->getNode()); + CPPUNIT_ASSERT(payload->hasFeature("bar")); + + testling.stop(); + } + + void testHandleRequest_GetInvalidNodeInfo() { + DiscoInfoResponder testling(router_); + std::shared_ptr<DiscoInfo> query(new DiscoInfo()); + query->setNode("bar-node"); + channel_->onIQReceived(IQ::createRequest(IQ::Get, JID("foo@bar.com"), "id-1", query)); + testling.start(); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size())); + std::shared_ptr<ErrorPayload> payload(channel_->iqs_[0]->getPayload<ErrorPayload>()); + CPPUNIT_ASSERT(payload); + + testling.stop(); + } + + private: + IQRouter* router_; + DummyIQChannel* channel_; }; CPPUNIT_TEST_SUITE_REGISTRATION(DiscoInfoResponderTest); diff --git a/Swiften/Disco/UnitTest/EntityCapsManagerTest.cpp b/Swiften/Disco/UnitTest/EntityCapsManagerTest.cpp index d5ad27a..8c59741 100644 --- a/Swiften/Disco/UnitTest/EntityCapsManagerTest.cpp +++ b/Swiften/Disco/UnitTest/EntityCapsManagerTest.cpp @@ -1,193 +1,191 @@ /* - * Copyright (c) 2010-2013 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ -#include <cppunit/extensions/HelperMacros.h> -#include <cppunit/extensions/TestFactoryRegistry.h> +#include <memory> #include <vector> + #include <boost/bind.hpp> -#include <Swiften/Disco/EntityCapsManager.h> -#include <Swiften/Disco/CapsProvider.h> -#include <Swiften/Elements/CapsInfo.h> +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + #include <Swiften/Client/DummyStanzaChannel.h> -#include <Swiften/Disco/CapsInfoGenerator.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/Elements/CapsInfo.h> using namespace Swift; class EntityCapsManagerTest : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(EntityCapsManagerTest); - CPPUNIT_TEST(testReceiveKnownHash); - CPPUNIT_TEST(testReceiveKnownHashTwiceDoesNotTriggerChange); - CPPUNIT_TEST(testReceiveUnknownHashDoesNotTriggerChange); - CPPUNIT_TEST(testReceiveUnknownHashAfterKnownHashTriggersChangeAndClearsCaps); - CPPUNIT_TEST(testReceiveUnavailablePresenceAfterKnownHashTriggersChangeAndClearsCaps); - CPPUNIT_TEST(testReconnectTriggersChangeAndClearsCaps); - CPPUNIT_TEST(testHashAvailable); - CPPUNIT_TEST_SUITE_END(); - - public: - void setUp() { - crypto = boost::shared_ptr<CryptoProvider>(PlatformCryptoProvider::create()); - - stanzaChannel = new DummyStanzaChannel(); - capsProvider = new DummyCapsProvider(); - - user1 = JID("user1@bar.com/bla"); - discoInfo1 = boost::make_shared<DiscoInfo>(); - discoInfo1->addFeature("http://swift.im/feature1"); - capsInfo1 = boost::make_shared<CapsInfo>(CapsInfoGenerator("http://node1.im", crypto.get()).generateCapsInfo(*discoInfo1.get())); - capsInfo1alt = boost::make_shared<CapsInfo>(CapsInfoGenerator("http://node2.im", crypto.get()).generateCapsInfo(*discoInfo1.get())); - user2 = JID("user2@foo.com/baz"); - discoInfo2 = boost::make_shared<DiscoInfo>(); - discoInfo2->addFeature("http://swift.im/feature2"); - capsInfo2 = boost::make_shared<CapsInfo>(CapsInfoGenerator("http://node2.im", crypto.get()).generateCapsInfo(*discoInfo2.get())); - user3 = JID("user3@foo.com/baz"); - legacyCapsInfo = boost::make_shared<CapsInfo>("http://swift.im", "ver1", ""); - } - - void tearDown() { - delete capsProvider; - delete stanzaChannel; - } - - void testReceiveKnownHash() { - boost::shared_ptr<EntityCapsManager> testling = createManager(); - capsProvider->caps[capsInfo1->getVersion()] = discoInfo1; - sendPresenceWithCaps(user1, capsInfo1); - - CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(changes.size())); - CPPUNIT_ASSERT_EQUAL(user1, changes[0]); - CPPUNIT_ASSERT_EQUAL(discoInfo1, testling->getCaps(user1)); - } - - void testReceiveKnownHashTwiceDoesNotTriggerChange() { - boost::shared_ptr<EntityCapsManager> testling = createManager(); - capsProvider->caps[capsInfo1->getVersion()] = discoInfo1; - sendPresenceWithCaps(user1, capsInfo1); - changes.clear(); - - sendPresenceWithCaps(user1, capsInfo1); - - CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(changes.size())); - } - - void testReceiveUnknownHashDoesNotTriggerChange() { - boost::shared_ptr<EntityCapsManager> testling = createManager(); - sendPresenceWithCaps(user1, capsInfo1); - - CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(changes.size())); - } - - void testHashAvailable() { - boost::shared_ptr<EntityCapsManager> testling = createManager(); - sendPresenceWithCaps(user1, capsInfo1); - - capsProvider->caps[capsInfo1->getVersion()] = discoInfo1; - capsProvider->onCapsAvailable(capsInfo1->getVersion()); - - CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(changes.size())); - CPPUNIT_ASSERT_EQUAL(user1, changes[0]); - CPPUNIT_ASSERT_EQUAL(discoInfo1, testling->getCaps(user1)); - } - - void testReceiveUnknownHashAfterKnownHashTriggersChangeAndClearsCaps() { - boost::shared_ptr<EntityCapsManager> testling = createManager(); - capsProvider->caps[capsInfo1->getVersion()] = discoInfo1; - sendPresenceWithCaps(user1, capsInfo1); - changes.clear(); - sendPresenceWithCaps(user1, capsInfo2); - - CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(changes.size())); - CPPUNIT_ASSERT_EQUAL(user1, changes[0]); - CPPUNIT_ASSERT(!testling->getCaps(user1)); - } - - void testReceiveUnavailablePresenceAfterKnownHashTriggersChangeAndClearsCaps() { - boost::shared_ptr<EntityCapsManager> testling = createManager(); - capsProvider->caps[capsInfo1->getVersion()] = discoInfo1; - sendPresenceWithCaps(user1, capsInfo1); - changes.clear(); - sendUnavailablePresence(user1); - - CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(changes.size())); - CPPUNIT_ASSERT_EQUAL(user1, changes[0]); - CPPUNIT_ASSERT(!testling->getCaps(user1)); - } - - void testReconnectTriggersChangeAndClearsCaps() { - boost::shared_ptr<EntityCapsManager> testling = createManager(); - capsProvider->caps[capsInfo1->getVersion()] = discoInfo1; - capsProvider->caps[capsInfo2->getVersion()] = discoInfo2; - sendPresenceWithCaps(user1, capsInfo1); - sendPresenceWithCaps(user2, capsInfo2); - changes.clear(); - stanzaChannel->setAvailable(false); - stanzaChannel->setAvailable(true); - - CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(changes.size())); - CPPUNIT_ASSERT_EQUAL(user1, changes[0]); - CPPUNIT_ASSERT(!testling->getCaps(user1)); - CPPUNIT_ASSERT_EQUAL(user2, changes[1]); - CPPUNIT_ASSERT(!testling->getCaps(user2)); - } - - private: - boost::shared_ptr<EntityCapsManager> createManager() { - boost::shared_ptr<EntityCapsManager> manager(new EntityCapsManager(capsProvider, stanzaChannel)); - manager->onCapsChanged.connect(boost::bind(&EntityCapsManagerTest::handleCapsChanged, this, _1)); - return manager; - } - - void handleCapsChanged(const JID& jid) { - changes.push_back(jid); - } - - void sendPresenceWithCaps(const JID& jid, boost::shared_ptr<CapsInfo> caps) { - boost::shared_ptr<Presence> presence(new Presence()); - presence->setFrom(jid); - presence->addPayload(caps); - stanzaChannel->onPresenceReceived(presence); - } - - void sendUnavailablePresence(const JID& jid) { - boost::shared_ptr<Presence> presence(new Presence()); - presence->setFrom(jid); - presence->setType(Presence::Unavailable); - stanzaChannel->onPresenceReceived(presence); - } - - 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: - DummyStanzaChannel* stanzaChannel; - DummyCapsProvider* capsProvider; - JID user1; - boost::shared_ptr<DiscoInfo> discoInfo1; - boost::shared_ptr<CapsInfo> capsInfo1; - boost::shared_ptr<CapsInfo> capsInfo1alt; - JID user2; - boost::shared_ptr<DiscoInfo> discoInfo2; - boost::shared_ptr<CapsInfo> capsInfo2; - boost::shared_ptr<CapsInfo> legacyCapsInfo; - JID user3; - std::vector<JID> changes; - boost::shared_ptr<CryptoProvider> crypto; + CPPUNIT_TEST_SUITE(EntityCapsManagerTest); + CPPUNIT_TEST(testReceiveKnownHash); + CPPUNIT_TEST(testReceiveKnownHashTwiceDoesNotTriggerChange); + CPPUNIT_TEST(testReceiveUnknownHashDoesNotTriggerChange); + CPPUNIT_TEST(testReceiveUnknownHashAfterKnownHashTriggersChangeAndClearsCaps); + CPPUNIT_TEST(testReceiveUnavailablePresenceAfterKnownHashTriggersChangeAndClearsCaps); + CPPUNIT_TEST(testReconnectTriggersChangeAndClearsCaps); + CPPUNIT_TEST(testHashAvailable); + CPPUNIT_TEST_SUITE_END(); + + public: + void setUp() { + crypto = std::shared_ptr<CryptoProvider>(PlatformCryptoProvider::create()); + + stanzaChannel = std::unique_ptr<DummyStanzaChannel>(new DummyStanzaChannel()); + capsProvider = std::unique_ptr<DummyCapsProvider>(new DummyCapsProvider()); + + user1 = JID("user1@bar.com/bla"); + discoInfo1 = std::make_shared<DiscoInfo>(); + discoInfo1->addFeature("http://swift.im/feature1"); + capsInfo1 = std::make_shared<CapsInfo>(CapsInfoGenerator("http://node1.im", crypto.get()).generateCapsInfo(*discoInfo1.get())); + capsInfo1alt = std::make_shared<CapsInfo>(CapsInfoGenerator("http://node2.im", crypto.get()).generateCapsInfo(*discoInfo1.get())); + user2 = JID("user2@foo.com/baz"); + discoInfo2 = std::make_shared<DiscoInfo>(); + discoInfo2->addFeature("http://swift.im/feature2"); + capsInfo2 = std::make_shared<CapsInfo>(CapsInfoGenerator("http://node2.im", crypto.get()).generateCapsInfo(*discoInfo2.get())); + user3 = JID("user3@foo.com/baz"); + legacyCapsInfo = std::make_shared<CapsInfo>("http://swift.im", "ver1", ""); + } + + void testReceiveKnownHash() { + std::shared_ptr<EntityCapsManager> testling = createManager(); + capsProvider->caps[capsInfo1->getVersion()] = discoInfo1; + sendPresenceWithCaps(user1, capsInfo1); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(changes.size())); + CPPUNIT_ASSERT_EQUAL(user1, changes[0]); + CPPUNIT_ASSERT_EQUAL(discoInfo1, testling->getCaps(user1)); + } + + void testReceiveKnownHashTwiceDoesNotTriggerChange() { + std::shared_ptr<EntityCapsManager> testling = createManager(); + capsProvider->caps[capsInfo1->getVersion()] = discoInfo1; + sendPresenceWithCaps(user1, capsInfo1); + changes.clear(); + + sendPresenceWithCaps(user1, capsInfo1); + + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(changes.size())); + } + + void testReceiveUnknownHashDoesNotTriggerChange() { + std::shared_ptr<EntityCapsManager> testling = createManager(); + sendPresenceWithCaps(user1, capsInfo1); + + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(changes.size())); + } + + void testHashAvailable() { + std::shared_ptr<EntityCapsManager> testling = createManager(); + sendPresenceWithCaps(user1, capsInfo1); + + capsProvider->caps[capsInfo1->getVersion()] = discoInfo1; + capsProvider->onCapsAvailable(capsInfo1->getVersion()); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(changes.size())); + CPPUNIT_ASSERT_EQUAL(user1, changes[0]); + CPPUNIT_ASSERT_EQUAL(discoInfo1, testling->getCaps(user1)); + } + + void testReceiveUnknownHashAfterKnownHashTriggersChangeAndClearsCaps() { + std::shared_ptr<EntityCapsManager> testling = createManager(); + capsProvider->caps[capsInfo1->getVersion()] = discoInfo1; + sendPresenceWithCaps(user1, capsInfo1); + changes.clear(); + sendPresenceWithCaps(user1, capsInfo2); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(changes.size())); + CPPUNIT_ASSERT_EQUAL(user1, changes[0]); + CPPUNIT_ASSERT(!testling->getCaps(user1)); + } + + void testReceiveUnavailablePresenceAfterKnownHashTriggersChangeAndClearsCaps() { + std::shared_ptr<EntityCapsManager> testling = createManager(); + capsProvider->caps[capsInfo1->getVersion()] = discoInfo1; + sendPresenceWithCaps(user1, capsInfo1); + changes.clear(); + sendUnavailablePresence(user1); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(changes.size())); + CPPUNIT_ASSERT_EQUAL(user1, changes[0]); + CPPUNIT_ASSERT(!testling->getCaps(user1)); + } + + void testReconnectTriggersChangeAndClearsCaps() { + std::shared_ptr<EntityCapsManager> testling = createManager(); + capsProvider->caps[capsInfo1->getVersion()] = discoInfo1; + capsProvider->caps[capsInfo2->getVersion()] = discoInfo2; + sendPresenceWithCaps(user1, capsInfo1); + sendPresenceWithCaps(user2, capsInfo2); + changes.clear(); + stanzaChannel->setAvailable(false); + stanzaChannel->setAvailable(true); + + CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(changes.size())); + CPPUNIT_ASSERT_EQUAL(user1, changes[0]); + CPPUNIT_ASSERT(!testling->getCaps(user1)); + CPPUNIT_ASSERT_EQUAL(user2, changes[1]); + CPPUNIT_ASSERT(!testling->getCaps(user2)); + } + + private: + std::shared_ptr<EntityCapsManager> createManager() { + std::shared_ptr<EntityCapsManager> manager(new EntityCapsManager(capsProvider.get(), stanzaChannel.get())); + manager->onCapsChanged.connect(boost::bind(&EntityCapsManagerTest::handleCapsChanged, this, _1)); + return manager; + } + + void handleCapsChanged(const JID& jid) { + changes.push_back(jid); + } + + void sendPresenceWithCaps(const JID& jid, std::shared_ptr<CapsInfo> caps) { + std::shared_ptr<Presence> presence(new Presence()); + presence->setFrom(jid); + presence->addPayload(caps); + stanzaChannel->onPresenceReceived(presence); + } + + void sendUnavailablePresence(const JID& jid) { + std::shared_ptr<Presence> presence(new Presence()); + presence->setFrom(jid); + presence->setType(Presence::Unavailable); + stanzaChannel->onPresenceReceived(presence); + } + + 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: + std::unique_ptr<DummyStanzaChannel> stanzaChannel; + std::unique_ptr<DummyCapsProvider> capsProvider; + JID user1; + std::shared_ptr<DiscoInfo> discoInfo1; + std::shared_ptr<CapsInfo> capsInfo1; + std::shared_ptr<CapsInfo> capsInfo1alt; + JID user2; + std::shared_ptr<DiscoInfo> discoInfo2; + std::shared_ptr<CapsInfo> capsInfo2; + std::shared_ptr<CapsInfo> legacyCapsInfo; + JID user3; + std::vector<JID> changes; + std::shared_ptr<CryptoProvider> crypto; }; CPPUNIT_TEST_SUITE_REGISTRATION(EntityCapsManagerTest); 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/Disco/UnitTest/JIDDiscoInfoResponderTest.cpp b/Swiften/Disco/UnitTest/JIDDiscoInfoResponderTest.cpp index adf709e..9369a04 100644 --- a/Swiften/Disco/UnitTest/JIDDiscoInfoResponderTest.cpp +++ b/Swiften/Disco/UnitTest/JIDDiscoInfoResponderTest.cpp @@ -1,118 +1,119 @@ /* - * Copyright (c) 2010 Isode Limited. + * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ +#include <typeinfo> + #include <cppunit/extensions/HelperMacros.h> #include <cppunit/extensions/TestFactoryRegistry.h> -#include <typeinfo> #include <Swiften/Disco/JIDDiscoInfoResponder.h> -#include <Swiften/Queries/IQRouter.h> #include <Swiften/Queries/DummyIQChannel.h> +#include <Swiften/Queries/IQRouter.h> using namespace Swift; class JIDDiscoInfoResponderTest : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(JIDDiscoInfoResponderTest); - CPPUNIT_TEST(testHandleRequest_GetToplevelInfo); - CPPUNIT_TEST(testHandleRequest_GetNodeInfo); - CPPUNIT_TEST(testHandleRequest_GetInvalidNodeInfo); - CPPUNIT_TEST(testHandleRequest_GetUnknownJID); - CPPUNIT_TEST_SUITE_END(); - - public: - void setUp() { - channel_ = new DummyIQChannel(); - router_ = new IQRouter(channel_); - } - - void tearDown() { - delete router_; - delete channel_; - } - - void testHandleRequest_GetToplevelInfo() { - JIDDiscoInfoResponder testling(router_); - testling.start(); - DiscoInfo discoInfo; - discoInfo.addFeature("foo"); - testling.setDiscoInfo(JID("foo@bar.com/baz"), discoInfo); - - boost::shared_ptr<DiscoInfo> query(new DiscoInfo()); - channel_->onIQReceived(IQ::createRequest(IQ::Get, JID("foo@bar.com/baz"), "id-1", query)); - - CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size())); - boost::shared_ptr<DiscoInfo> payload(channel_->iqs_[0]->getPayload<DiscoInfo>()); - CPPUNIT_ASSERT(payload); - CPPUNIT_ASSERT_EQUAL(std::string(""), payload->getNode()); - CPPUNIT_ASSERT(payload->hasFeature("foo")); - - testling.stop(); - } - - void testHandleRequest_GetNodeInfo() { - JIDDiscoInfoResponder testling(router_); - testling.start(); - DiscoInfo discoInfo; - discoInfo.addFeature("foo"); - testling.setDiscoInfo(JID("foo@bar.com/baz"), discoInfo); - DiscoInfo discoInfoBar; - discoInfoBar.addFeature("bar"); - testling.setDiscoInfo(JID("foo@bar.com/baz"), "bar-node", discoInfoBar); - - boost::shared_ptr<DiscoInfo> query(new DiscoInfo()); - query->setNode("bar-node"); - channel_->onIQReceived(IQ::createRequest(IQ::Get, JID("foo@bar.com/baz"), "id-1", query)); - - CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size())); - boost::shared_ptr<DiscoInfo> payload(channel_->iqs_[0]->getPayload<DiscoInfo>()); - CPPUNIT_ASSERT(payload); - CPPUNIT_ASSERT_EQUAL(std::string("bar-node"), payload->getNode()); - CPPUNIT_ASSERT(payload->hasFeature("bar")); - - testling.stop(); - } - - void testHandleRequest_GetInvalidNodeInfo() { - JIDDiscoInfoResponder testling(router_); - DiscoInfo discoInfo; - discoInfo.addFeature("foo"); - testling.setDiscoInfo(JID("foo@bar.com/baz"), discoInfo); - testling.start(); - - boost::shared_ptr<DiscoInfo> query(new DiscoInfo()); - query->setNode("bar-node"); - channel_->onIQReceived(IQ::createRequest(IQ::Get, JID("foo@bar.com/baz"), "id-1", query)); - - CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size())); - boost::shared_ptr<ErrorPayload> payload(channel_->iqs_[0]->getPayload<ErrorPayload>()); - CPPUNIT_ASSERT(payload); - - testling.stop(); - } - - void testHandleRequest_GetUnknownJID() { - JIDDiscoInfoResponder testling(router_); - DiscoInfo discoInfo; - discoInfo.addFeature("foo"); - testling.setDiscoInfo(JID("foo@bar.com/baz"), discoInfo); - testling.start(); - - boost::shared_ptr<DiscoInfo> query(new DiscoInfo()); - channel_->onIQReceived(IQ::createRequest(IQ::Get, JID("foo@bar.com/fum"), "id-1", query)); - - CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size())); - boost::shared_ptr<ErrorPayload> payload(channel_->iqs_[0]->getPayload<ErrorPayload>()); - CPPUNIT_ASSERT(payload); - - testling.stop(); - } - - private: - IQRouter* router_; - DummyIQChannel* channel_; + CPPUNIT_TEST_SUITE(JIDDiscoInfoResponderTest); + CPPUNIT_TEST(testHandleRequest_GetToplevelInfo); + CPPUNIT_TEST(testHandleRequest_GetNodeInfo); + CPPUNIT_TEST(testHandleRequest_GetInvalidNodeInfo); + CPPUNIT_TEST(testHandleRequest_GetUnknownJID); + CPPUNIT_TEST_SUITE_END(); + + public: + void setUp() { + channel_ = new DummyIQChannel(); + router_ = new IQRouter(channel_); + } + + void tearDown() { + delete router_; + delete channel_; + } + + void testHandleRequest_GetToplevelInfo() { + JIDDiscoInfoResponder testling(router_); + testling.start(); + DiscoInfo discoInfo; + discoInfo.addFeature("foo"); + testling.setDiscoInfo(JID("foo@bar.com/baz"), discoInfo); + + std::shared_ptr<DiscoInfo> query(new DiscoInfo()); + channel_->onIQReceived(IQ::createRequest(IQ::Get, JID("foo@bar.com/baz"), "id-1", query)); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size())); + std::shared_ptr<DiscoInfo> payload(channel_->iqs_[0]->getPayload<DiscoInfo>()); + CPPUNIT_ASSERT(payload); + CPPUNIT_ASSERT_EQUAL(std::string(""), payload->getNode()); + CPPUNIT_ASSERT(payload->hasFeature("foo")); + + testling.stop(); + } + + void testHandleRequest_GetNodeInfo() { + JIDDiscoInfoResponder testling(router_); + testling.start(); + DiscoInfo discoInfo; + discoInfo.addFeature("foo"); + testling.setDiscoInfo(JID("foo@bar.com/baz"), discoInfo); + DiscoInfo discoInfoBar; + discoInfoBar.addFeature("bar"); + testling.setDiscoInfo(JID("foo@bar.com/baz"), "bar-node", discoInfoBar); + + std::shared_ptr<DiscoInfo> query(new DiscoInfo()); + query->setNode("bar-node"); + channel_->onIQReceived(IQ::createRequest(IQ::Get, JID("foo@bar.com/baz"), "id-1", query)); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size())); + std::shared_ptr<DiscoInfo> payload(channel_->iqs_[0]->getPayload<DiscoInfo>()); + CPPUNIT_ASSERT(payload); + CPPUNIT_ASSERT_EQUAL(std::string("bar-node"), payload->getNode()); + CPPUNIT_ASSERT(payload->hasFeature("bar")); + + testling.stop(); + } + + void testHandleRequest_GetInvalidNodeInfo() { + JIDDiscoInfoResponder testling(router_); + DiscoInfo discoInfo; + discoInfo.addFeature("foo"); + testling.setDiscoInfo(JID("foo@bar.com/baz"), discoInfo); + testling.start(); + + std::shared_ptr<DiscoInfo> query(new DiscoInfo()); + query->setNode("bar-node"); + channel_->onIQReceived(IQ::createRequest(IQ::Get, JID("foo@bar.com/baz"), "id-1", query)); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size())); + std::shared_ptr<ErrorPayload> payload(channel_->iqs_[0]->getPayload<ErrorPayload>()); + CPPUNIT_ASSERT(payload); + + testling.stop(); + } + + void testHandleRequest_GetUnknownJID() { + JIDDiscoInfoResponder testling(router_); + DiscoInfo discoInfo; + discoInfo.addFeature("foo"); + testling.setDiscoInfo(JID("foo@bar.com/baz"), discoInfo); + testling.start(); + + std::shared_ptr<DiscoInfo> query(new DiscoInfo()); + channel_->onIQReceived(IQ::createRequest(IQ::Get, JID("foo@bar.com/fum"), "id-1", query)); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size())); + std::shared_ptr<ErrorPayload> payload(channel_->iqs_[0]->getPayload<ErrorPayload>()); + CPPUNIT_ASSERT(payload); + + testling.stop(); + } + + private: + IQRouter* router_; + DummyIQChannel* channel_; }; CPPUNIT_TEST_SUITE_REGISTRATION(JIDDiscoInfoResponderTest); |