diff options
Diffstat (limited to 'Swiften/JID/JID.cpp')
-rw-r--r-- | Swiften/JID/JID.cpp | 467 |
1 files changed, 211 insertions, 256 deletions
diff --git a/Swiften/JID/JID.cpp b/Swiften/JID/JID.cpp index 0f2d8d1..eb72014 100644 --- a/Swiften/JID/JID.cpp +++ b/Swiften/JID/JID.cpp @@ -1,326 +1,281 @@ /* - * Copyright (c) 2010-2013 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2018 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ -#define SWIFTEN_CACHE_JID_PREP - +#include <sstream> +#include <string> #include <vector> -#include <list> -#include <iostream> -#include <string> -#ifdef SWIFTEN_CACHE_JID_PREP -#include <boost/thread/mutex.hpp> -#include <boost/unordered_map.hpp> -#endif -#include <boost/assign/list_of.hpp> -#include <boost/algorithm/string/find_format.hpp> -#include <boost/algorithm/string/finder.hpp> #include <boost/optional.hpp> -#include <iostream> -#include <sstream> #include <Swiften/Base/String.h> -#include <Swiften/JID/JID.h> #include <Swiften/IDN/IDNConverter.h> +#include <Swiften/JID/JID.h> +#include <Swiften/Network/HostAddress.h> + #ifndef SWIFTEN_JID_NO_DEFAULT_IDN_CONVERTER -#include <boost/shared_ptr.hpp> +#include <memory> #include <Swiften/IDN/PlatformIDNConverter.h> #endif using namespace Swift; -#ifdef SWIFTEN_CACHE_JID_PREP -typedef boost::unordered_map<std::string, std::string> PrepCache; - -static boost::mutex namePrepCacheMutex; -static PrepCache nodePrepCache; -static PrepCache domainPrepCache; -static PrepCache resourcePrepCache; -#endif - -static const std::list<char> escapedChars = boost::assign::list_of(' ')('"')('&')('\'')('/')('<')('>')('@')(':'); +static const std::vector<char> escapedChars = {' ', '"', '&', '\'', '/', '<', '>', '@', ':'}; -static IDNConverter* idnConverter = NULL; +static IDNConverter* idnConverter = nullptr; #ifndef SWIFTEN_JID_NO_DEFAULT_IDN_CONVERTER namespace { - struct IDNInitializer { - IDNInitializer() : defaultIDNConverter(PlatformIDNConverter::create()) { - idnConverter = defaultIDNConverter.get(); - } - boost::shared_ptr<IDNConverter> defaultIDNConverter; - } initializer; + struct IDNInitializer { + IDNInitializer() : defaultIDNConverter(PlatformIDNConverter::create()) { + idnConverter = defaultIDNConverter.get(); + } + std::shared_ptr<IDNConverter> defaultIDNConverter; + } initializer; } #endif static std::string getEscaped(char c) { - return makeString() << '\\' << std::hex << static_cast<int>(c); + return makeString() << '\\' << std::hex << static_cast<int>(c); } static bool getEscapeSequenceValue(const std::string& sequence, unsigned char& value) { - std::stringstream s; - unsigned int v; - s << std::hex << sequence; - s >> v; - value = static_cast<unsigned char>(v); - return (!s.fail() && !s.bad() && (value == 0x5C || std::find(escapedChars.begin(), escapedChars.end(), value) != escapedChars.end())); + std::stringstream s; + unsigned int v; + s << std::hex << sequence; + s >> v; + value = static_cast<unsigned char>(v); + return (!s.fail() && !s.bad() && (value == 0x5C || std::find(escapedChars.begin(), escapedChars.end(), value) != escapedChars.end())); } -// Disabling this code for now, since GCC4.5+boost1.42 (on ubuntu) seems to -// result in a bug. Replacing it with naive code. -#if 0 -struct UnescapedCharacterFinder { - template<typename Iterator> boost::iterator_range<Iterator> operator()(Iterator begin, Iterator end) { - for (; begin != end; ++begin) { - if (std::find(escapedChars.begin(), escapedChars.end(), *begin) != escapedChars.end()) { - return boost::iterator_range<Iterator>(begin, begin + 1); - } - else if (*begin == '\\') { - // Check if we have an escaped dissalowed character sequence - Iterator innerBegin = begin + 1; - if (innerBegin != end && innerBegin + 1 != end) { - Iterator innerEnd = innerBegin + 2; - unsigned char value; - if (getEscapeSequenceValue(std::string(innerBegin, innerEnd), value)) { - return boost::iterator_range<Iterator>(begin, begin + 1); - } - } - } - } - return boost::iterator_range<Iterator>(end, end); - } -}; - -struct UnescapedCharacterFormatter { - template<typename FindResult> std::string operator()(const FindResult& match) const { - std::ostringstream s; - s << '\\' << std::hex << static_cast<int>(*match.begin()); - return s.str(); - } -}; - -struct EscapedCharacterFinder { - template<typename Iterator> boost::iterator_range<Iterator> operator()(Iterator begin, Iterator end) { - for (; begin != end; ++begin) { - if (*begin == '\\') { - Iterator innerEnd = begin + 1; - for (size_t i = 0; i < 2 && innerEnd != end; ++i, ++innerEnd) { - } - unsigned char value; - if (getEscapeSequenceValue(std::string(begin + 1, innerEnd), value)) { - return boost::iterator_range<Iterator>(begin, innerEnd); - } - } - } - return boost::iterator_range<Iterator>(end, end); - } -}; - -struct EscapedCharacterFormatter { - template<typename FindResult> std::string operator()(const FindResult& match) const { - unsigned char value; - if (getEscapeSequenceValue(std::string(match.begin() + 1, match.end()), value)) { - return std::string(reinterpret_cast<const char*>(&value), 1); - } - return boost::copy_range<std::string>(match); - } -}; -#endif - namespace Swift { JID::JID(const char* jid) : valid_(true) { - assert(jid); - initializeFromString(std::string(jid)); + assert(jid); + initializeFromString(std::string(jid)); } JID::JID(const std::string& jid) : valid_(true) { - initializeFromString(jid); + initializeFromString(jid); } JID::JID(const std::string& node, const std::string& domain) : valid_(true), hasResource_(false) { - nameprepAndSetComponents(node, domain, ""); + nameprepAndSetComponents(node, domain, ""); } JID::JID(const std::string& node, const std::string& domain, const std::string& resource) : valid_(true), hasResource_(true) { - nameprepAndSetComponents(node, domain, resource); + if (resource.empty()) { + valid_ = false; + } + nameprepAndSetComponents(node, domain, resource); +} + +JID::JID(const JID& other) { + this->operator=(other); +} + +JID::JID(JID&& other) { + this->operator=(std::move(other)); +} + +JID& JID::operator=(const JID& other) { + valid_ = other.valid_; + node_ = other.node_; + domain_ = other.domain_; + hasResource_ = other.hasResource_; + resource_ = other.resource_; + return *this; +} + +JID& JID::operator=(JID&& other) { + valid_ = other.valid_; + other.valid_ = false; + node_ = std::move(other.node_); + domain_ = std::move(other.domain_); + hasResource_ = other.hasResource_; + resource_ = std::move(other.resource_); + return *this; } void JID::initializeFromString(const std::string& jid) { - if (String::beginsWith(jid, '@')) { - valid_ = false; - return; - } - - std::string bare, resource; - size_t slashIndex = jid.find('/'); - if (slashIndex != jid.npos) { - hasResource_ = true; - bare = jid.substr(0, slashIndex); - resource = jid.substr(slashIndex + 1, jid.npos); - } - else { - hasResource_ = false; - bare = jid; - } - std::pair<std::string,std::string> nodeAndDomain = String::getSplittedAtFirst(bare, '@'); - if (nodeAndDomain.second.empty()) { - nameprepAndSetComponents("", nodeAndDomain.first, resource); - } - else { - nameprepAndSetComponents(nodeAndDomain.first, nodeAndDomain.second, resource); - } + if (String::beginsWith(jid, '@')) { + valid_ = false; + return; + } + + std::string bare, resource; + size_t slashIndex = jid.find('/'); + if (slashIndex != jid.npos) { + hasResource_ = true; + bare = jid.substr(0, slashIndex); + resource = jid.substr(slashIndex + 1, jid.npos); + } + else { + hasResource_ = false; + bare = jid; + } + auto firstMatch = bare.find('@'); + if (firstMatch != bare.npos) { + nameprepAndSetComponents(bare.substr(0, firstMatch), bare.substr(firstMatch + 1), resource); + } + else { + nameprepAndSetComponents("", bare, resource); + } } +void JID::setComponents(const std::string& node, const std::string& domain, const std::string& resource) { + domain_ = domain; + try { + node_ = idnConverter->getStringPrepared(node, IDNConverter::XMPPNodePrep); + resource_ = idnConverter->getStringPrepared(resource, IDNConverter::XMPPResourcePrep); + } + catch (...) { + valid_ = false; + return; + } +} void JID::nameprepAndSetComponents(const std::string& node, const std::string& domain, const std::string& resource) { - if (domain.empty()) { - valid_ = false; - return; - } -#ifndef SWIFTEN_CACHE_JID_PREP - node_ = idnConverter->getStringPrepared(node, IDNConverter::XMPPNodePrep); - domain_ = idnConverter->getStringPrepared(domain, IDNConverter::NamePrep); - resource_ = idnConverter->getStringPrepared(resource, IDNConverter::XMPPResourcePrep); -#else - boost::mutex::scoped_lock lock(namePrepCacheMutex); - - std::pair<PrepCache::iterator, bool> r; - - r = nodePrepCache.insert(std::make_pair(node, std::string())); - if (r.second) { - try { - r.first->second = idnConverter->getStringPrepared(node, IDNConverter::XMPPNodePrep); - } - catch (...) { - nodePrepCache.erase(r.first); - valid_ = false; - return; - } - } - node_ = r.first->second; - - r = domainPrepCache.insert(std::make_pair(domain, std::string())); - if (r.second) { - try { - r.first->second = idnConverter->getStringPrepared(domain, IDNConverter::NamePrep); - } - catch (...) { - domainPrepCache.erase(r.first); - valid_ = false; - return; - } - } - domain_ = r.first->second; - - r = resourcePrepCache.insert(std::make_pair(resource, std::string())); - if (r.second) { - try { - r.first->second = idnConverter->getStringPrepared(resource, IDNConverter::XMPPResourcePrep); - } - catch (...) { - resourcePrepCache.erase(r.first); - valid_ = false; - return; - } - } - resource_ = r.first->second; -#endif - - if (domain_.empty()) { - valid_ = false; - return; - } + if (domain.empty() || (hasResource_ && resource.empty())) { + valid_ = false; + return; + } + + // Handling IPv6 addresses according to RFC 3986 rules + // saying that they are enclosed in square brackets + // which we have to remove when passing to HostAddress + if (domain.size() > 2 && domain.front() == '[' && domain.back() == ']') { + auto inner = std::string(domain.begin() + 1, domain.end() - 1); + auto hostAddress = HostAddress::fromString(inner); + if (hostAddress && hostAddress->isValid()) { + setComponents(node, domain, resource); + return; + } + } + + const auto isAnyOfNonNumericAndNotDot = std::any_of(std::begin(domain), std::end(domain), [](char c) {return !::isdigit(c) && c != '.'; }); + const auto isDomainAllNumeric = std::all_of(std::begin(domain), std::end(domain), [](char c) {return ::isdigit(c) ; }); + + //Prevent Windows validating non-dotted integers as OK if it can unpack them + if (!isAnyOfNonNumericAndNotDot && !isDomainAllNumeric) { + auto hostAddress = HostAddress::fromString(domain); + if (hostAddress && hostAddress->isValid()) { + setComponents(node, domain, resource); + return; + } + } + + if (!isAnyOfNonNumericAndNotDot || !idnConverter->getIDNAEncoded(domain)) { + valid_ = false; + return; + } + + try { + node_ = idnConverter->getStringPrepared(node, IDNConverter::XMPPNodePrep); + if (domain.back() == '.') { + domain_ = idnConverter->getStringPrepared(domain.substr(0, domain.size() - 1), IDNConverter::NamePrep); + } + else { + domain_ = idnConverter->getStringPrepared(domain, IDNConverter::NamePrep); + } + resource_ = idnConverter->getStringPrepared(resource, IDNConverter::XMPPResourcePrep); + } + catch (...) { + valid_ = false; + return; + } + + if (domain_.empty()) { + valid_ = false; + return; + } } std::string JID::toString() const { - std::string string; - if (!node_.empty()) { - string += node_ + "@"; - } - string += domain_; - if (!isBare()) { - string += "/" + resource_; - } - return string; + std::string string; + if (!node_.empty()) { + string += node_ + "@"; + } + string += domain_; + if (!isBare()) { + string += "/" + resource_; + } + return string; } int JID::compare(const Swift::JID& o, CompareType compareType) const { - if (node_ < o.node_) { return -1; } - if (node_ > o.node_) { return 1; } - if (domain_ < o.domain_) { return -1; } - if (domain_ > o.domain_) { return 1; } - if (compareType == WithResource) { - if (hasResource_ != o.hasResource_) { - return hasResource_ ? 1 : -1; - } - if (resource_ < o.resource_) { return -1; } - if (resource_ > o.resource_) { return 1; } - } - return 0; + if (node_ < o.node_) { return -1; } + if (node_ > o.node_) { return 1; } + if (domain_ < o.domain_) { return -1; } + if (domain_ > o.domain_) { return 1; } + if (compareType == WithResource) { + if (hasResource_ != o.hasResource_) { + return hasResource_ ? 1 : -1; + } + if (resource_ < o.resource_) { return -1; } + if (resource_ > o.resource_) { return 1; } + } + return 0; } std::string JID::getEscapedNode(const std::string& node) { - std::string result; - for (std::string::const_iterator i = node.begin(); i != node.end(); ++i) { - if (std::find(escapedChars.begin(), escapedChars.end(), *i) != escapedChars.end()) { - result += getEscaped(*i); - continue; - } - else if (*i == '\\') { - // Check if we have an escaped dissalowed character sequence - std::string::const_iterator innerBegin = i + 1; - if (innerBegin != node.end() && innerBegin + 1 != node.end()) { - std::string::const_iterator innerEnd = innerBegin + 2; - unsigned char value; - if (getEscapeSequenceValue(std::string(innerBegin, innerEnd), value)) { - result += getEscaped(*i); - continue; - } - } - } - result += *i; - } - return result; - //return boost::find_format_all_copy(node, UnescapedCharacterFinder(), UnescapedCharacterFormatter()); + std::string result; + for (std::string::const_iterator i = node.begin(); i != node.end(); ++i) { + if (std::find(escapedChars.begin(), escapedChars.end(), *i) != escapedChars.end()) { + result += getEscaped(*i); + continue; + } + else if (*i == '\\') { + // Check if we have an escaped dissalowed character sequence + std::string::const_iterator innerBegin = i + 1; + if (innerBegin != node.end() && innerBegin + 1 != node.end()) { + std::string::const_iterator innerEnd = innerBegin + 2; + unsigned char value; + if (getEscapeSequenceValue(std::string(innerBegin, innerEnd), value)) { + result += getEscaped(*i); + continue; + } + } + } + result += *i; + } + return result; } - + std::string JID::getUnescapedNode() const { - std::string result; - for (std::string::const_iterator j = node_.begin(); j != node_.end();) { - if (*j == '\\') { - std::string::const_iterator innerEnd = j + 1; - for (size_t i = 0; i < 2 && innerEnd != node_.end(); ++i, ++innerEnd) { - } - unsigned char value; - if (getEscapeSequenceValue(std::string(j + 1, innerEnd), value)) { - result += std::string(reinterpret_cast<const char*>(&value), 1); - j = innerEnd; - continue; - } - } - result += *j; - ++j; - } - return result; - //return boost::find_format_all_copy(node_, EscapedCharacterFinder(), EscapedCharacterFormatter()); + std::string result; + for (std::string::const_iterator j = node_.begin(); j != node_.end();) { + if (*j == '\\') { + std::string::const_iterator innerEnd = j + 1; + for (size_t i = 0; i < 2 && innerEnd != node_.end(); ++i, ++innerEnd) { + } + unsigned char value; + if (getEscapeSequenceValue(std::string(j + 1, innerEnd), value)) { + result += std::string(reinterpret_cast<const char*>(&value), 1); + j = innerEnd; + continue; + } + } + result += *j; + ++j; + } + return result; } void JID::setIDNConverter(IDNConverter* converter) { - idnConverter = converter; + idnConverter = converter; } std::ostream& operator<<(std::ostream& os, const JID& j) { - os << j.toString(); - return os; + os << j.toString(); + return os; } boost::optional<JID> JID::parse(const std::string& s) { - JID jid(s); - return jid.isValid() ? jid : boost::optional<JID>(); + JID jid(s); + return jid.isValid() ? jid : boost::optional<JID>(); } } |