diff options
Diffstat (limited to 'Swiften/JID')
-rw-r--r-- | Swiften/JID/JID.cpp | 467 | ||||
-rw-r--r-- | Swiften/JID/JID.h | 372 | ||||
-rw-r--r-- | Swiften/JID/SConscript | 4 | ||||
-rw-r--r-- | Swiften/JID/UnitTest/JIDTest.cpp | 832 |
4 files changed, 898 insertions, 777 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>(); } } diff --git a/Swiften/JID/JID.h b/Swiften/JID/JID.h index 126a7b1..aecc7cb 100644 --- a/Swiften/JID/JID.h +++ b/Swiften/JID/JID.h @@ -1,190 +1,204 @@ /* - * 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-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once -#include <string> #include <iosfwd> +#include <string> + +#include <boost/optional/optional.hpp> #include <Swiften/Base/API.h> -#include <boost/optional/optional_fwd.hpp> namespace Swift { - class IDNConverter; - - /** - * This represents the JID used in XMPP - * (RFC6120 - http://tools.ietf.org/html/rfc6120 ). - * For a description of format, see the RFC or page 14 of - * XMPP: The Definitive Guide (Saint-Andre et al.) - * - * Particularly - a Bare JID is a JID without a resource part. - * - * A JID can be invalid (when isValid() returns false). No member methods are - * guaranteed to work correctly if they do. - */ - class SWIFTEN_API JID { - public: - enum CompareType { - WithResource, WithoutResource - }; - - /** - * Create a JID from its String representation. - * - * e.g. - * wonderland.lit - * wonderland.lit/rabbithole - * alice@wonderland.lit - * alice@wonderland.lit/TeaParty - * - * @param jid String representation. Invalid JID if empty or invalid. - */ - JID(const std::string& jid = std::string()); - - /** - * See std::string constructor. - * - * Must not be NULL. - */ - JID(const char*); - - /** - * Create a bare JID from the node and domain parts. - * - * JID("node@domain") == JID("node", "domain") - * unless you pass in empty values. - * - * @param node JID node part. - * @param domain JID domain part. - */ - JID(const std::string& node, const std::string& domain); - /** - * Create a bare JID from the node, domain and resource parts. - * - * JID("node@domain/resource") == JID("node", "domain", "resource") - * unless you pass in empty values. - * - * @param node JID node part. - * @param domain JID domain part. - * @param resource JID resource part. - */ - JID(const std::string& node, const std::string& domain, const std::string& resource); - - /** - * @return Is a correctly-formatted JID. - */ - bool isValid() const { - return valid_; - } - - /** - * e.g. JID("node@domain").getNode() == "node" - * @return could be empty. - */ - const std::string& getNode() const { - return node_; - } - - /** - * e.g. JID("node@domain").getDomain() == "domain" - */ - const std::string& getDomain() const { - return domain_; - } - - /** - * e.g. JID("node@domain/resource").getResource() == "resource" - * @return could be empty. - */ - const std::string& getResource() const { - return resource_; - } - - /** - * Is a bare JID, i.e. has no resource part. - */ - bool isBare() const { - return !hasResource_; - } - - /** - * Returns the given node, escaped according to XEP-0106. - * The resulting node is a valid node for a JID, whereas the input value can contain characters - * that are not allowed. - */ - static std::string getEscapedNode(const std::string& node); - - /** - * Returns the node of the current JID, unescaped according to XEP-0106. - */ - std::string getUnescapedNode() const; - - /** - * Get the JID without a resource. - * @return Invalid if the original is invalid. - */ - JID toBare() const { - JID result(*this); - result.hasResource_ = false; - result.resource_ = ""; - return result; - } - - std::string toString() const; - - bool equals(const JID& o, CompareType compareType) const { - return compare(o, compareType) == 0; - } - - int compare(const JID& o, CompareType compareType) const; - - operator std::string() const { - return toString(); - } - - bool operator<(const Swift::JID& b) const { - return compare(b, Swift::JID::WithResource) < 0; - } - - SWIFTEN_API friend std::ostream& operator<<(std::ostream& os, const Swift::JID& j); - - friend bool operator==(const Swift::JID& a, const Swift::JID& b) { - return a.compare(b, Swift::JID::WithResource) == 0; - } - - friend bool operator!=(const Swift::JID& a, const Swift::JID& b) { - return a.compare(b, Swift::JID::WithResource) != 0; - } - - /** - * Returns an empty optional if the JID is invalid, and an - * optional with a value if the JID is valid. - */ - static boost::optional<JID> parse(const std::string&); - - /** - * If Swiften was compiled with SWIFTEN_JID_NO_DEFAULT_IDN_CONVERTER (not default), use this method at - * the beginning of the program to set an IDN converter to use for JID IDN conversions. - * By default, this method shouldn't be used. - */ - static void setIDNConverter(IDNConverter*); - - private: - void nameprepAndSetComponents(const std::string& node, const std::string& domain, const std::string& resource); - void initializeFromString(const std::string&); - - private: - bool valid_; - std::string node_; - std::string domain_; - bool hasResource_; - std::string resource_; - }; - - SWIFTEN_API std::ostream& operator<<(std::ostream& os, const Swift::JID& j); + class IDNConverter; + + /** + * This represents the JID used in XMPP + * (RFC6120 - http://tools.ietf.org/html/rfc6120 ). + * For a description of format, see the RFC or page 14 of + * XMPP: The Definitive Guide (Saint-Andre et al.) + * + * Particularly - a Bare JID is a JID without a resource part. + * + * A JID can be invalid (when isValid() returns false). No member methods are + * guaranteed to work correctly if they do. + */ + class SWIFTEN_API JID { + public: + enum CompareType { + WithResource, WithoutResource + }; + + /** + * Create a JID from its String representation. + * + * e.g. + * wonderland.lit + * wonderland.lit/rabbithole + * alice@wonderland.lit + * alice@wonderland.lit/TeaParty + * + * @param jid String representation. Invalid JID if empty or invalid. + */ + JID(const std::string& jid = std::string()); + + /** + * See std::string constructor. + * + * Must not be NULL. + */ + JID(const char*); + + /** + * Create a bare JID from the node and domain parts. + * + * JID("node@domain") == JID("node", "domain") + * unless you pass in empty values. + * + * @param node JID node part. + * @param domain JID domain part. + */ + JID(const std::string& node, const std::string& domain); + /** + * Create a bare JID from the node, domain and resource parts. + * + * JID("node@domain/resource") == JID("node", "domain", "resource") + * unless you pass in empty values. + * + * @param node JID node part. + * @param domain JID domain part. + * @param resource JID resource part. + */ + JID(const std::string& node, const std::string& domain, const std::string& resource); + + JID(const JID& other); + JID(JID&& other); + JID& operator=(const JID& other); + JID& operator=(JID&& other); + + /** + * @return Is a correctly-formatted JID. + */ + bool isValid() const { + return valid_; + } + + /** + * e.g. JID("node@domain").getNode() == "node" + * @return could be empty. + */ + const std::string& getNode() const { + return node_; + } + + /** + * e.g. JID("node@domain").getDomain() == "domain" + */ + const std::string& getDomain() const { + return domain_; + } + + /** + * e.g. JID("node@domain/resource").getResource() == "resource" + * @return could be empty. + */ + const std::string& getResource() const { + return resource_; + } + + /** + * Is a bare JID, i.e. has no resource part. + */ + bool isBare() const { + return !hasResource_; + } + + /** + * Returns the given node, escaped according to XEP-0106. + * The resulting node is a valid node for a JID, whereas the input value can contain characters + * that are not allowed. + */ + static std::string getEscapedNode(const std::string& node); + + /** + * Returns the node of the current JID, unescaped according to XEP-0106. + */ + std::string getUnescapedNode() const; + + /** + * Get the JID without a resource. + * @return Invalid if the original is invalid. + */ + JID toBare() const { + JID result(*this); + result.hasResource_ = false; + result.resource_ = ""; + return result; + } + + /** + * Get the full JID with the supplied resource. + */ + JID withResource(const std::string& resource) const { + JID result(this->getNode(), this->getDomain(), resource); + return result; + } + + std::string toString() const; + + bool equals(const JID& o, CompareType compareType) const { + return compare(o, compareType) == 0; + } + + int compare(const JID& o, CompareType compareType) const; + + operator std::string() const { + return toString(); + } + + bool operator<(const Swift::JID& b) const { + return compare(b, Swift::JID::WithResource) < 0; + } + + SWIFTEN_API friend std::ostream& operator<<(std::ostream& os, const Swift::JID& j); + + friend bool operator==(const Swift::JID& a, const Swift::JID& b) { + return a.compare(b, Swift::JID::WithResource) == 0; + } + + friend bool operator!=(const Swift::JID& a, const Swift::JID& b) { + return a.compare(b, Swift::JID::WithResource) != 0; + } + + /** + * Returns an empty optional if the JID is invalid, and an + * optional with a value if the JID is valid. + */ + static boost::optional<JID> parse(const std::string&); + + /** + * If Swiften was compiled with SWIFTEN_JID_NO_DEFAULT_IDN_CONVERTER (not default), use this method at + * the beginning of the program to set an IDN converter to use for JID IDN conversions. + * By default, this method shouldn't be used. + */ + static void setIDNConverter(IDNConverter*); + + private: + void nameprepAndSetComponents(const std::string& node, const std::string& domain, const std::string& resource); + void setComponents(const std::string& node, const std::string& domain, const std::string& resource); + void initializeFromString(const std::string&); + + private: + bool valid_; + std::string node_; + std::string domain_; + bool hasResource_; + std::string resource_; + }; + + SWIFTEN_API std::ostream& operator<<(std::ostream& os, const Swift::JID& j); } - diff --git a/Swiften/JID/SConscript b/Swiften/JID/SConscript index 12565fc..122388e 100644 --- a/Swiften/JID/SConscript +++ b/Swiften/JID/SConscript @@ -2,6 +2,6 @@ Import("swiften_env") myenv = swiften_env.Clone() objects = myenv.SwiftenObject([ - "JID.cpp", - ]) + "JID.cpp", + ]) swiften_env.Append(SWIFTEN_OBJECTS = [objects]) diff --git a/Swiften/JID/UnitTest/JIDTest.cpp b/Swiften/JID/UnitTest/JIDTest.cpp index 72ca884..fc7583f 100644 --- a/Swiften/JID/UnitTest/JIDTest.cpp +++ b/Swiften/JID/UnitTest/JIDTest.cpp @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010 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. */ #include <cppunit/extensions/HelperMacros.h> @@ -13,384 +13,536 @@ using namespace Swift; class JIDTest : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(JIDTest); - CPPUNIT_TEST(testConstructorWithString); - CPPUNIT_TEST(testConstructorWithString_NoResource); - CPPUNIT_TEST(testConstructorWithString_NoNode); - CPPUNIT_TEST(testConstructorWithString_EmptyResource); - CPPUNIT_TEST(testConstructorWithString_OnlyDomain); - CPPUNIT_TEST(testConstructorWithString_UpperCaseNode); - CPPUNIT_TEST(testConstructorWithString_UpperCaseDomain); - CPPUNIT_TEST(testConstructorWithString_UpperCaseResource); - CPPUNIT_TEST(testConstructorWithString_EmptyNode); - CPPUNIT_TEST(testConstructorWithString_IllegalResource); - CPPUNIT_TEST(testConstructorWithString_SpacesInNode); - CPPUNIT_TEST(testConstructorWithStrings); - CPPUNIT_TEST(testConstructorWithStrings_EmptyDomain); - CPPUNIT_TEST(testIsBare); - CPPUNIT_TEST(testIsBare_NotBare); - CPPUNIT_TEST(testToBare); - CPPUNIT_TEST(testToBare_EmptyNode); - CPPUNIT_TEST(testToBare_EmptyResource); - CPPUNIT_TEST(testToString); - CPPUNIT_TEST(testToString_EmptyNode); - CPPUNIT_TEST(testToString_EmptyResource); - CPPUNIT_TEST(testToString_NoResource); - CPPUNIT_TEST(testCompare_SmallerNode); - CPPUNIT_TEST(testCompare_LargerNode); - CPPUNIT_TEST(testCompare_SmallerDomain); - CPPUNIT_TEST(testCompare_LargerDomain); - CPPUNIT_TEST(testCompare_SmallerResource); - CPPUNIT_TEST(testCompare_LargerResource); - CPPUNIT_TEST(testCompare_Equal); - CPPUNIT_TEST(testCompare_EqualWithoutResource); - CPPUNIT_TEST(testCompare_NoResourceAndEmptyResource); - CPPUNIT_TEST(testCompare_EmptyResourceAndNoResource); - CPPUNIT_TEST(testEquals); - CPPUNIT_TEST(testEquals_NotEqual); - CPPUNIT_TEST(testEquals_WithoutResource); - CPPUNIT_TEST(testSmallerThan); - CPPUNIT_TEST(testSmallerThan_Equal); - CPPUNIT_TEST(testSmallerThan_Larger); - CPPUNIT_TEST(testHasResource); - CPPUNIT_TEST(testHasResource_NoResource); - CPPUNIT_TEST(testGetEscapedNode); - CPPUNIT_TEST(testGetEscapedNode_XEP106Examples); - CPPUNIT_TEST(testGetEscapedNode_BackslashAtEnd); - CPPUNIT_TEST(testGetUnescapedNode); - CPPUNIT_TEST(testGetUnescapedNode_XEP106Examples); - CPPUNIT_TEST_SUITE_END(); - - public: - JIDTest() {} - - void testConstructorWithString() { - JID testling("foo@bar/baz"); - - CPPUNIT_ASSERT_EQUAL(std::string("foo"), testling.getNode()); - CPPUNIT_ASSERT_EQUAL(std::string("bar"), testling.getDomain()); - CPPUNIT_ASSERT_EQUAL(std::string("baz"), testling.getResource()); - CPPUNIT_ASSERT(!testling.isBare()); - } - - void testConstructorWithString_NoResource() { - JID testling("foo@bar"); - - CPPUNIT_ASSERT_EQUAL(std::string("foo"), testling.getNode()); - CPPUNIT_ASSERT_EQUAL(std::string("bar"), testling.getDomain()); - CPPUNIT_ASSERT_EQUAL(std::string(""), testling.getResource()); - CPPUNIT_ASSERT(testling.isBare()); - } - - void testConstructorWithString_EmptyResource() { - JID testling("bar/"); - - CPPUNIT_ASSERT(testling.isValid()); - CPPUNIT_ASSERT(!testling.isBare()); - } - - void testConstructorWithString_NoNode() { - JID testling("bar/baz"); - - CPPUNIT_ASSERT_EQUAL(std::string(""), testling.getNode()); - CPPUNIT_ASSERT_EQUAL(std::string("bar"), testling.getDomain()); - CPPUNIT_ASSERT_EQUAL(std::string("baz"), testling.getResource()); - CPPUNIT_ASSERT(!testling.isBare()); - } - - void testConstructorWithString_OnlyDomain() { - JID testling("bar"); - - CPPUNIT_ASSERT_EQUAL(std::string(""), testling.getNode()); - CPPUNIT_ASSERT_EQUAL(std::string("bar"), testling.getDomain()); - CPPUNIT_ASSERT_EQUAL(std::string(""), testling.getResource()); - CPPUNIT_ASSERT(testling.isBare()); - } - - void testConstructorWithString_UpperCaseNode() { - JID testling("Fo\xCE\xA9@bar"); - - CPPUNIT_ASSERT_EQUAL(std::string("fo\xCF\x89"), testling.getNode()); - CPPUNIT_ASSERT_EQUAL(std::string("bar"), testling.getDomain()); - } - - void testConstructorWithString_UpperCaseDomain() { - JID testling("Fo\xCE\xA9"); - - CPPUNIT_ASSERT_EQUAL(std::string("fo\xCF\x89"), testling.getDomain()); - } - - void testConstructorWithString_UpperCaseResource() { - JID testling("bar/Fo\xCE\xA9"); - - CPPUNIT_ASSERT_EQUAL(testling.getResource(), std::string("Fo\xCE\xA9")); - } - - void testConstructorWithString_EmptyNode() { - JID testling("@bar"); - - CPPUNIT_ASSERT(!testling.isValid()); - } - - void testConstructorWithString_IllegalResource() { - JID testling("foo@bar.com/\xd8\xb1\xd9\x85\xd9\x82\xd9\x87\x20\xd8\xaa\xd8\xb1\xd9\x86\xd8\xb3\x20"); - - CPPUNIT_ASSERT(!testling.isValid()); - } - - void testConstructorWithString_SpacesInNode() { - CPPUNIT_ASSERT(!JID(" alice@wonderland.lit").isValid()); - CPPUNIT_ASSERT(!JID("alice @wonderland.lit").isValid()); - } - - void testConstructorWithStrings() { - JID testling("foo", "bar", "baz"); + CPPUNIT_TEST_SUITE(JIDTest); + CPPUNIT_TEST(testConstructorWithString); + CPPUNIT_TEST(testConstructorWithString_Empty); + CPPUNIT_TEST(testConstructorWithString_NoResource); + CPPUNIT_TEST(testConstructorWithString_NoNode); + CPPUNIT_TEST(testConstructorWithString_EmptyResource); + CPPUNIT_TEST(testConstructorWithString_OnlyDomain); + CPPUNIT_TEST(testConstructorWithString_OnlyDomainWithDot); + CPPUNIT_TEST(testConstructorWithString_OnlyDomainDotStrippedOff); + CPPUNIT_TEST(testConstructorWithString_InvalidOnlyDomainSingleDot); + CPPUNIT_TEST(testConstructorWithString_InvalidDomain); + CPPUNIT_TEST(testConstructorWithString_InvalidDomainOnlyDigits); + CPPUNIT_TEST(testConstructorWithString_InvalidDomainEmptyLabel); + CPPUNIT_TEST(testConstructorWithString_UpperCaseNode); + CPPUNIT_TEST(testConstructorWithString_UpperCaseDomain); + CPPUNIT_TEST(testConstructorWithString_UpperCaseResource); + CPPUNIT_TEST(testConstructorWithString_EmptyNode); + CPPUNIT_TEST(testConstructorWithString_EmptyDomain); + CPPUNIT_TEST(testConstructorWithString_EmptyDomainWithResource); + CPPUNIT_TEST(testConstructorWithString_DotDomain); + CPPUNIT_TEST(testConstructorWithString_DotDomainWithResource); + CPPUNIT_TEST(testConstructorWithString_IllegalResource); + CPPUNIT_TEST(testConstructorWithString_SpacesInNode); + CPPUNIT_TEST(testConstructorWithStrings); + CPPUNIT_TEST(testConstructorWithStrings_EmptyDomain); + CPPUNIT_TEST(testConstructorWithStrings_EmptyResource); + CPPUNIT_TEST(testIsBare); + CPPUNIT_TEST(testIsBare_NotBare); + CPPUNIT_TEST(testToBare); + CPPUNIT_TEST(testToBare_EmptyNode); + CPPUNIT_TEST(testToBare_EmptyResource); + CPPUNIT_TEST(testToString); + CPPUNIT_TEST(testToString_EmptyNode); + CPPUNIT_TEST(testToString_EmptyResource); + CPPUNIT_TEST(testToString_NoResource); + CPPUNIT_TEST(testCompare_SmallerNode); + CPPUNIT_TEST(testCompare_LargerNode); + CPPUNIT_TEST(testCompare_SmallerDomain); + CPPUNIT_TEST(testCompare_LargerDomain); + CPPUNIT_TEST(testCompare_SmallerResource); + CPPUNIT_TEST(testCompare_LargerResource); + CPPUNIT_TEST(testCompare_Equal); + CPPUNIT_TEST(testCompare_EqualWithoutResource); + CPPUNIT_TEST(testCompare_NoResourceAndEmptyResource); + CPPUNIT_TEST(testCompare_EmptyResourceAndNoResource); + CPPUNIT_TEST(testEquals); + CPPUNIT_TEST(testEquals_NotEqual); + CPPUNIT_TEST(testEquals_WithoutResource); + CPPUNIT_TEST(testSmallerThan); + CPPUNIT_TEST(testSmallerThan_Equal); + CPPUNIT_TEST(testSmallerThan_Larger); + CPPUNIT_TEST(testHasResource); + CPPUNIT_TEST(testHasResource_NoResource); + CPPUNIT_TEST(testGetEscapedNode); + CPPUNIT_TEST(testGetEscapedNode_XEP106Examples); + CPPUNIT_TEST(testGetEscapedNode_BackslashAtEnd); + CPPUNIT_TEST(testGetUnescapedNode); + CPPUNIT_TEST(testGetUnescapedNode_XEP106Examples); + CPPUNIT_TEST(testStringPrepFailures); + CPPUNIT_TEST(testConstructorWithString_DomainIPv4); + CPPUNIT_TEST(testConstructorWithString_DomainNOTIPv4); + CPPUNIT_TEST(testConstructorWithString_ValidDomainNOTIPv4); + CPPUNIT_TEST(testConstructorWithString_DomainIPv6); + CPPUNIT_TEST(testConstructorWithString_DomainInvalidIPv6); + CPPUNIT_TEST(testConstructorWithString_DomainIPv6NoBrackets); + CPPUNIT_TEST_SUITE_END(); + + public: + JIDTest() {} + + void testConstructorWithString() { + JID testling("foo@bar/baz"); + + CPPUNIT_ASSERT_EQUAL(std::string("foo"), testling.getNode()); + CPPUNIT_ASSERT_EQUAL(std::string("bar"), testling.getDomain()); + CPPUNIT_ASSERT_EQUAL(std::string("baz"), testling.getResource()); + CPPUNIT_ASSERT(!testling.isBare()); + CPPUNIT_ASSERT(testling.isValid()); + } + + void testConstructorWithString_Empty() { + JID testling(""); + CPPUNIT_ASSERT_EQUAL(false, testling.isValid()); + } + + void testConstructorWithString_NoResource() { + JID testling("foo@bar"); + + CPPUNIT_ASSERT_EQUAL(std::string("foo"), testling.getNode()); + CPPUNIT_ASSERT_EQUAL(std::string("bar"), testling.getDomain()); + CPPUNIT_ASSERT_EQUAL(std::string(""), testling.getResource()); + CPPUNIT_ASSERT(testling.isBare()); + CPPUNIT_ASSERT(testling.isValid()); + } + + void testConstructorWithString_EmptyResource() { + JID testling("bar/"); + + CPPUNIT_ASSERT(!testling.isValid()); + CPPUNIT_ASSERT(!testling.isBare()); + } + + void testConstructorWithString_NoNode() { + JID testling("bar/baz"); + + CPPUNIT_ASSERT_EQUAL(std::string(""), testling.getNode()); + CPPUNIT_ASSERT_EQUAL(std::string("bar"), testling.getDomain()); + CPPUNIT_ASSERT_EQUAL(std::string("baz"), testling.getResource()); + CPPUNIT_ASSERT(!testling.isBare()); + CPPUNIT_ASSERT(testling.isValid()); + } + + void testConstructorWithString_OnlyDomain() { + JID testling("bar"); + + CPPUNIT_ASSERT_EQUAL(std::string(""), testling.getNode()); + CPPUNIT_ASSERT_EQUAL(std::string("bar"), testling.getDomain()); + CPPUNIT_ASSERT_EQUAL(std::string(""), testling.getResource()); + CPPUNIT_ASSERT(testling.isBare()); + CPPUNIT_ASSERT(testling.isValid()); + } + + void testConstructorWithString_OnlyDomainWithDot() { + JID testling("bar."); + + CPPUNIT_ASSERT_EQUAL(std::string(""), testling.getNode()); + CPPUNIT_ASSERT_EQUAL(std::string("bar"), testling.getDomain()); + CPPUNIT_ASSERT_EQUAL(std::string(""), testling.getResource()); + CPPUNIT_ASSERT(testling.isBare()); + CPPUNIT_ASSERT(testling.isValid()); + } + + void testConstructorWithString_OnlyDomainDotStrippedOff() { + JID testling("foo.@bar./resource."); + + CPPUNIT_ASSERT_EQUAL(std::string("foo."), testling.getNode()); + CPPUNIT_ASSERT_EQUAL(std::string("bar"), testling.getDomain()); + CPPUNIT_ASSERT_EQUAL(std::string("resource."), testling.getResource()); + CPPUNIT_ASSERT(!testling.isBare()); + CPPUNIT_ASSERT(testling.isValid()); + } + + void testConstructorWithString_InvalidOnlyDomainSingleDot() { + CPPUNIT_ASSERT(!JID(".").isValid()); + } + + void testConstructorWithString_InvalidDomain() { + CPPUNIT_ASSERT(!JID("foo@bar,baz").isValid()); + } + + void testConstructorWithString_InvalidDomainOnlyDigits() { + CPPUNIT_ASSERT(!JID("1234").isValid()); + } + + void testConstructorWithString_InvalidDomainEmptyLabel() { + CPPUNIT_ASSERT(!JID("foo@bar..").isValid()); + } + + void testConstructorWithString_UpperCaseNode() { + JID testling("Fo\xCE\xA9@bar"); - CPPUNIT_ASSERT_EQUAL(std::string("foo"), testling.getNode()); - CPPUNIT_ASSERT_EQUAL(std::string("bar"), testling.getDomain()); - CPPUNIT_ASSERT_EQUAL(std::string("baz"), testling.getResource()); - } + CPPUNIT_ASSERT_EQUAL(std::string("fo\xCF\x89"), testling.getNode()); + CPPUNIT_ASSERT_EQUAL(std::string("bar"), testling.getDomain()); + CPPUNIT_ASSERT(testling.isValid()); + } - void testConstructorWithStrings_EmptyDomain() { - JID testling("foo", "", "baz"); + void testConstructorWithString_UpperCaseDomain() { + JID testling("Fo\xCE\xA9"); - CPPUNIT_ASSERT(!testling.isValid()); - } + CPPUNIT_ASSERT_EQUAL(std::string("fo\xCF\x89"), testling.getDomain()); + CPPUNIT_ASSERT(testling.isValid()); + } - void testIsBare() { - CPPUNIT_ASSERT(JID("foo@bar").isBare()); - } + void testConstructorWithString_UpperCaseResource() { + JID testling("bar/Fo\xCE\xA9"); - void testIsBare_NotBare() { - CPPUNIT_ASSERT(!JID("foo@bar/baz").isBare()); - } + CPPUNIT_ASSERT_EQUAL(testling.getResource(), std::string("Fo\xCE\xA9")); + CPPUNIT_ASSERT(testling.isValid()); + } - void testToBare() { - JID testling("foo@bar/baz"); + void testConstructorWithString_EmptyNode() { + JID testling("@bar"); - CPPUNIT_ASSERT_EQUAL(std::string("foo"), testling.toBare().getNode()); - CPPUNIT_ASSERT_EQUAL(std::string("bar"), testling.toBare().getDomain()); - CPPUNIT_ASSERT(testling.toBare().isBare()); - } + CPPUNIT_ASSERT(!testling.isValid()); + } - void testToBare_EmptyNode() { - JID testling("bar/baz"); + void testConstructorWithString_EmptyDomain() { + JID testling("bar@"); - CPPUNIT_ASSERT_EQUAL(std::string(""), testling.toBare().getNode()); - CPPUNIT_ASSERT_EQUAL(std::string("bar"), testling.toBare().getDomain()); - CPPUNIT_ASSERT(testling.toBare().isBare()); - } + CPPUNIT_ASSERT(!testling.isValid()); + } - void testToBare_EmptyResource() { - JID testling("bar/"); + void testStringPrepFailures() { + CPPUNIT_ASSERT_EQUAL(false, JID("foo@bar", "example.com").isValid()); + CPPUNIT_ASSERT_EQUAL(false, JID("foo^", "example*com").isValid()); + CPPUNIT_ASSERT_EQUAL(false, JID("foobar", "example^com").isValid()); + } - CPPUNIT_ASSERT_EQUAL(std::string(""), testling.toBare().getNode()); - CPPUNIT_ASSERT_EQUAL(std::string("bar"), testling.toBare().getDomain()); - CPPUNIT_ASSERT(testling.toBare().isBare()); - } + void testConstructorWithString_EmptyDomainWithResource() { + JID testling("bar@/resource"); - void testToString() { - JID testling("foo@bar/baz"); + CPPUNIT_ASSERT(!testling.isValid()); + } - CPPUNIT_ASSERT_EQUAL(std::string("foo@bar/baz"), testling.toString()); - } + void testConstructorWithString_DotDomain() { + JID testling("bar@."); - void testToString_EmptyNode() { - JID testling("bar/baz"); + CPPUNIT_ASSERT(!testling.isValid()); + } - CPPUNIT_ASSERT_EQUAL(std::string("bar/baz"), testling.toString()); - } + void testConstructorWithString_DotDomainWithResource() { + JID testling("bar@./resource"); - void testToString_NoResource() { - JID testling("foo@bar"); + CPPUNIT_ASSERT(!testling.isValid()); + } - CPPUNIT_ASSERT_EQUAL(std::string("foo@bar"), testling.toString()); - } + void testConstructorWithString_IllegalResource() { + JID testling("foo@bar.com/\xd8\xb1\xd9\x85\xd9\x82\xd9\x87\x20\xd8\xaa\xd8\xb1\xd9\x86\xd8\xb3\x20"); - void testToString_EmptyResource() { - JID testling("foo@bar/"); + CPPUNIT_ASSERT(!testling.isValid()); + } - CPPUNIT_ASSERT_EQUAL(std::string("foo@bar/"), testling.toString()); - } + void testConstructorWithString_SpacesInNode() { + CPPUNIT_ASSERT(!JID(" alice@wonderland.lit").isValid()); + CPPUNIT_ASSERT(!JID("alice @wonderland.lit").isValid()); + } - void testCompare_SmallerNode() { - JID testling1("a@c"); - JID testling2("b@b"); + void testConstructorWithStrings() { + JID testling("foo", "bar", "baz"); - CPPUNIT_ASSERT_EQUAL(-1, testling1.compare(testling2, JID::WithResource)); - } + CPPUNIT_ASSERT_EQUAL(std::string("foo"), testling.getNode()); + CPPUNIT_ASSERT_EQUAL(std::string("bar"), testling.getDomain()); + CPPUNIT_ASSERT_EQUAL(std::string("baz"), testling.getResource()); + CPPUNIT_ASSERT(testling.isValid()); + } - void testCompare_LargerNode() { - JID testling1("c@a"); - JID testling2("b@b"); + void testConstructorWithStrings_EmptyDomain() { + JID testling("foo", "", "baz"); - CPPUNIT_ASSERT_EQUAL(1, testling1.compare(testling2, JID::WithResource)); - } + CPPUNIT_ASSERT(!testling.isValid()); + } - void testCompare_SmallerDomain() { - JID testling1("x@a/c"); - JID testling2("x@b/b"); + void testConstructorWithStrings_EmptyResource() { + JID testling("foo", "bar", ""); - CPPUNIT_ASSERT_EQUAL(-1, testling1.compare(testling2, JID::WithResource)); - } + CPPUNIT_ASSERT(!testling.isValid()); + } - void testCompare_LargerDomain() { - JID testling1("x@b/b"); - JID testling2("x@a/c"); + void testIsBare() { + CPPUNIT_ASSERT(JID("foo@bar").isBare()); + } - CPPUNIT_ASSERT_EQUAL(1, testling1.compare(testling2, JID::WithResource)); - } + void testIsBare_NotBare() { + CPPUNIT_ASSERT(!JID("foo@bar/baz").isBare()); + } - void testCompare_SmallerResource() { - JID testling1("x@y/a"); - JID testling2("x@y/b"); + void testToBare() { + JID testling("foo@bar/baz"); - CPPUNIT_ASSERT_EQUAL(-1, testling1.compare(testling2, JID::WithResource)); - } + CPPUNIT_ASSERT_EQUAL(std::string("foo"), testling.toBare().getNode()); + CPPUNIT_ASSERT_EQUAL(std::string("bar"), testling.toBare().getDomain()); + CPPUNIT_ASSERT(testling.toBare().isBare()); + } - void testCompare_LargerResource() { - JID testling1("x@y/b"); - JID testling2("x@y/a"); + void testToBare_EmptyNode() { + JID testling("bar/baz"); - CPPUNIT_ASSERT_EQUAL(1, testling1.compare(testling2, JID::WithResource)); - } + CPPUNIT_ASSERT_EQUAL(std::string(""), testling.toBare().getNode()); + CPPUNIT_ASSERT_EQUAL(std::string("bar"), testling.toBare().getDomain()); + CPPUNIT_ASSERT(testling.toBare().isBare()); + CPPUNIT_ASSERT(testling.isValid()); + } - void testCompare_Equal() { - JID testling1("x@y/z"); - JID testling2("x@y/z"); + void testToBare_EmptyResource() { + JID testling("bar/"); - CPPUNIT_ASSERT_EQUAL(0, testling1.compare(testling2, JID::WithResource)); - } + CPPUNIT_ASSERT_EQUAL(std::string(""), testling.toBare().getNode()); + CPPUNIT_ASSERT(!testling.isValid()); + } - void testCompare_EqualWithoutResource() { - JID testling1("x@y/a"); - JID testling2("x@y/b"); + void testToString() { + JID testling("foo@bar/baz"); - CPPUNIT_ASSERT_EQUAL(0, testling1.compare(testling2, JID::WithoutResource)); - } + CPPUNIT_ASSERT_EQUAL(std::string("foo@bar/baz"), testling.toString()); + } - void testCompare_NoResourceAndEmptyResource() { - JID testling1("x@y/"); - JID testling2("x@y"); + void testToString_EmptyNode() { + JID testling("bar/baz"); - CPPUNIT_ASSERT_EQUAL(1, testling1.compare(testling2, JID::WithResource)); - } + CPPUNIT_ASSERT_EQUAL(std::string("bar/baz"), testling.toString()); + } - void testCompare_EmptyResourceAndNoResource() { - JID testling1("x@y"); - JID testling2("x@y/"); + void testToString_NoResource() { + JID testling("foo@bar"); - CPPUNIT_ASSERT_EQUAL(-1, testling1.compare(testling2, JID::WithResource)); - } + CPPUNIT_ASSERT_EQUAL(std::string("foo@bar"), testling.toString()); + } - void testEquals() { - JID testling1("x@y/c"); - JID testling2("x@y/c"); + void testToString_EmptyResource() { + JID testling("foo@bar/"); - CPPUNIT_ASSERT(testling1.equals(testling2, JID::WithResource)); - } - - void testEquals_NotEqual() { - JID testling1("x@y/c"); - JID testling2("x@y/d"); - - CPPUNIT_ASSERT(!testling1.equals(testling2, JID::WithResource)); - } - - void testEquals_WithoutResource() { - JID testling1("x@y/c"); - JID testling2("x@y/d"); - - CPPUNIT_ASSERT(testling1.equals(testling2, JID::WithoutResource)); - } - - void testSmallerThan() { - JID testling1("x@y/c"); - JID testling2("x@y/d"); - - CPPUNIT_ASSERT(testling1 < testling2); - } - - void testSmallerThan_Equal() { - JID testling1("x@y/d"); - JID testling2("x@y/d"); - - CPPUNIT_ASSERT(!(testling1 < testling2)); - } - - void testSmallerThan_Larger() { - JID testling1("x@y/d"); - JID testling2("x@y/c"); - - CPPUNIT_ASSERT(!(testling1 < testling2)); - } - - void testHasResource() { - JID testling("x@y/d"); - - CPPUNIT_ASSERT(!testling.isBare()); - } - - void testHasResource_NoResource() { - JID testling("x@y"); - - CPPUNIT_ASSERT(testling.isBare()); - } - - void testGetEscapedNode() { - std::string escaped = JID::getEscapedNode("alice@wonderland.lit"); - CPPUNIT_ASSERT_EQUAL(std::string("alice\\40wonderland.lit"), escaped); - - escaped = JID::getEscapedNode("\\& \" ' / <\\\\> @ :\\3a\\40"); - CPPUNIT_ASSERT_EQUAL(std::string("\\\\26\\20\\22\\20\\27\\20\\2f\\20\\3c\\\\\\3e\\20\\40\\20\\3a\\5c3a\\5c40"), escaped); - } - - void testGetEscapedNode_XEP106Examples() { - CPPUNIT_ASSERT_EQUAL(std::string("\\2plus\\2is\\4"), JID::getEscapedNode("\\2plus\\2is\\4")); - CPPUNIT_ASSERT_EQUAL(std::string("foo\\bar"), JID::getEscapedNode("foo\\bar")); - CPPUNIT_ASSERT_EQUAL(std::string("foob\\41r"), JID::getEscapedNode("foob\\41r")); - CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("space cadet"), std::string("space\\20cadet")); - CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("call me \"ishmael\""), std::string("call\\20me\\20\\22ishmael\\22")); - CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("at&t guy"), std::string("at\\26t\\20guy")); - CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("d'artagnan"), std::string("d\\27artagnan")); - CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("/.fanboy"), std::string("\\2f.fanboy")); - CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("::foo::"), std::string("\\3a\\3afoo\\3a\\3a")); - CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("<foo>"), std::string("\\3cfoo\\3e")); - CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("user@host"), std::string("user\\40host")); - CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("c:\\net"), std::string("c\\3a\\net")); - CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("c:\\\\net"), std::string("c\\3a\\\\net")); - CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("c:\\cool stuff"), std::string("c\\3a\\cool\\20stuff")); - CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("c:\\5commas"), std::string("c\\3a\\5c5commas")); - } - - void testGetEscapedNode_BackslashAtEnd() { - CPPUNIT_ASSERT_EQUAL(std::string("foo\\"), JID::getEscapedNode("foo\\")); - } - - void testGetUnescapedNode() { - std::string input = "\\& \" ' / <\\\\> @ : \\5c\\40"; - JID testling(JID::getEscapedNode(input) + "@y"); - CPPUNIT_ASSERT(testling.isValid()); - CPPUNIT_ASSERT_EQUAL(input, testling.getUnescapedNode()); - } - - void testGetUnescapedNode_XEP106Examples() { - CPPUNIT_ASSERT_EQUAL(std::string("\\2plus\\2is\\4"), JID("\\2plus\\2is\\4@example.com").getUnescapedNode()); - CPPUNIT_ASSERT_EQUAL(std::string("foo\\bar"), JID("foo\\bar@example.com").getUnescapedNode()); - CPPUNIT_ASSERT_EQUAL(std::string("foob\\41r"), JID("foob\\41r@example.com").getUnescapedNode()); - CPPUNIT_ASSERT_EQUAL(std::string("space cadet"), JID("space\\20cadet@example.com").getUnescapedNode()); - CPPUNIT_ASSERT_EQUAL(std::string("call me \"ishmael\""), JID("call\\20me\\20\\22ishmael\\22@example.com").getUnescapedNode()); - CPPUNIT_ASSERT_EQUAL(std::string("at&t guy"), JID("at\\26t\\20guy@example.com").getUnescapedNode()); - CPPUNIT_ASSERT_EQUAL(std::string("d'artagnan"), JID("d\\27artagnan@example.com").getUnescapedNode()); - CPPUNIT_ASSERT_EQUAL(std::string("/.fanboy"), JID("\\2f.fanboy@example.com").getUnescapedNode()); - CPPUNIT_ASSERT_EQUAL(std::string("::foo::"), JID("\\3a\\3afoo\\3a\\3a@example.com").getUnescapedNode()); - CPPUNIT_ASSERT_EQUAL(std::string("<foo>"), JID("\\3cfoo\\3e@example.com").getUnescapedNode()); - CPPUNIT_ASSERT_EQUAL(std::string("user@host"), JID("user\\40host@example.com").getUnescapedNode()); - CPPUNIT_ASSERT_EQUAL(std::string("c:\\net"), JID("c\\3a\\net@example.com").getUnescapedNode()); - CPPUNIT_ASSERT_EQUAL(std::string("c:\\\\net"), JID("c\\3a\\\\net@example.com").getUnescapedNode()); - CPPUNIT_ASSERT_EQUAL(std::string("c:\\cool stuff"), JID("c\\3a\\cool\\20stuff@example.com").getUnescapedNode()); - CPPUNIT_ASSERT_EQUAL(std::string("c:\\5commas"), JID("c\\3a\\5c5commas@example.com").getUnescapedNode()); - } + CPPUNIT_ASSERT_EQUAL(false, testling.isValid()); + } + + void testCompare_SmallerNode() { + JID testling1("a@c"); + JID testling2("b@b"); + + CPPUNIT_ASSERT_EQUAL(-1, testling1.compare(testling2, JID::WithResource)); + } + + void testCompare_LargerNode() { + JID testling1("c@a"); + JID testling2("b@b"); + + CPPUNIT_ASSERT_EQUAL(1, testling1.compare(testling2, JID::WithResource)); + } + + void testCompare_SmallerDomain() { + JID testling1("x@a/c"); + JID testling2("x@b/b"); + + CPPUNIT_ASSERT_EQUAL(-1, testling1.compare(testling2, JID::WithResource)); + } + + void testCompare_LargerDomain() { + JID testling1("x@b/b"); + JID testling2("x@a/c"); + + CPPUNIT_ASSERT_EQUAL(1, testling1.compare(testling2, JID::WithResource)); + } + + void testCompare_SmallerResource() { + JID testling1("x@y/a"); + JID testling2("x@y/b"); + + CPPUNIT_ASSERT_EQUAL(-1, testling1.compare(testling2, JID::WithResource)); + } + + void testCompare_LargerResource() { + JID testling1("x@y/b"); + JID testling2("x@y/a"); + + CPPUNIT_ASSERT_EQUAL(1, testling1.compare(testling2, JID::WithResource)); + } + + void testCompare_Equal() { + JID testling1("x@y/z"); + JID testling2("x@y/z"); + + CPPUNIT_ASSERT_EQUAL(0, testling1.compare(testling2, JID::WithResource)); + } + + void testCompare_EqualWithoutResource() { + JID testling1("x@y/a"); + JID testling2("x@y/b"); + + CPPUNIT_ASSERT_EQUAL(0, testling1.compare(testling2, JID::WithoutResource)); + } + + void testCompare_NoResourceAndEmptyResource() { + JID testling1("x@y/"); + JID testling2("x@y"); + + CPPUNIT_ASSERT_EQUAL(-1, testling1.compare(testling2, JID::WithResource)); + } + + void testCompare_EmptyResourceAndNoResource() { + JID testling1("x@y"); + JID testling2("x@y/"); + + CPPUNIT_ASSERT_EQUAL(1, testling1.compare(testling2, JID::WithResource)); + } + + void testEquals() { + JID testling1("x@y/c"); + JID testling2("x@y/c"); + + CPPUNIT_ASSERT(testling1.equals(testling2, JID::WithResource)); + } + + void testEquals_NotEqual() { + JID testling1("x@y/c"); + JID testling2("x@y/d"); + + CPPUNIT_ASSERT(!testling1.equals(testling2, JID::WithResource)); + } + + void testEquals_WithoutResource() { + JID testling1("x@y/c"); + JID testling2("x@y/d"); + + CPPUNIT_ASSERT(testling1.equals(testling2, JID::WithoutResource)); + } + + void testSmallerThan() { + JID testling1("x@y/c"); + JID testling2("x@y/d"); + + CPPUNIT_ASSERT(testling1 < testling2); + } + + void testSmallerThan_Equal() { + JID testling1("x@y/d"); + JID testling2("x@y/d"); + + CPPUNIT_ASSERT(!(testling1 < testling2)); + } + + void testSmallerThan_Larger() { + JID testling1("x@y/d"); + JID testling2("x@y/c"); + + CPPUNIT_ASSERT(!(testling1 < testling2)); + } + + void testHasResource() { + JID testling("x@y/d"); + + CPPUNIT_ASSERT(!testling.isBare()); + } + + void testHasResource_NoResource() { + { + JID testling("x@y"); + + CPPUNIT_ASSERT(testling.isBare()); + CPPUNIT_ASSERT_EQUAL(true, testling.isValid()); + } + + { + JID testling("node@domain/"); + CPPUNIT_ASSERT_EQUAL(false, testling.isValid()); + } + } + + void testGetEscapedNode() { + std::string escaped = JID::getEscapedNode("alice@wonderland.lit"); + CPPUNIT_ASSERT_EQUAL(std::string("alice\\40wonderland.lit"), escaped); + + escaped = JID::getEscapedNode("\\& \" ' / <\\\\> @ :\\3a\\40"); + CPPUNIT_ASSERT_EQUAL(std::string("\\\\26\\20\\22\\20\\27\\20\\2f\\20\\3c\\\\\\3e\\20\\40\\20\\3a\\5c3a\\5c40"), escaped); + } + + void testGetEscapedNode_XEP106Examples() { + CPPUNIT_ASSERT_EQUAL(std::string("\\2plus\\2is\\4"), JID::getEscapedNode("\\2plus\\2is\\4")); + CPPUNIT_ASSERT_EQUAL(std::string("foo\\bar"), JID::getEscapedNode("foo\\bar")); + CPPUNIT_ASSERT_EQUAL(std::string("foob\\41r"), JID::getEscapedNode("foob\\41r")); + CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("space cadet"), std::string("space\\20cadet")); + CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("call me \"ishmael\""), std::string("call\\20me\\20\\22ishmael\\22")); + CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("at&t guy"), std::string("at\\26t\\20guy")); + CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("d'artagnan"), std::string("d\\27artagnan")); + CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("/.fanboy"), std::string("\\2f.fanboy")); + CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("::foo::"), std::string("\\3a\\3afoo\\3a\\3a")); + CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("<foo>"), std::string("\\3cfoo\\3e")); + CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("user@host"), std::string("user\\40host")); + CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("c:\\net"), std::string("c\\3a\\net")); + CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("c:\\\\net"), std::string("c\\3a\\\\net")); + CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("c:\\cool stuff"), std::string("c\\3a\\cool\\20stuff")); + CPPUNIT_ASSERT_EQUAL(JID::getEscapedNode("c:\\5commas"), std::string("c\\3a\\5c5commas")); + } + + void testGetEscapedNode_BackslashAtEnd() { + CPPUNIT_ASSERT_EQUAL(std::string("foo\\"), JID::getEscapedNode("foo\\")); + } + + void testGetUnescapedNode() { + std::string input = "\\& \" ' / <\\\\> @ : \\5c\\40"; + JID testling(JID::getEscapedNode(input) + "@y"); + CPPUNIT_ASSERT(testling.isValid()); + CPPUNIT_ASSERT_EQUAL(input, testling.getUnescapedNode()); + } + + void testGetUnescapedNode_XEP106Examples() { + CPPUNIT_ASSERT_EQUAL(std::string("\\2plus\\2is\\4"), JID("\\2plus\\2is\\4@example.com").getUnescapedNode()); + CPPUNIT_ASSERT_EQUAL(std::string("foo\\bar"), JID("foo\\bar@example.com").getUnescapedNode()); + CPPUNIT_ASSERT_EQUAL(std::string("foob\\41r"), JID("foob\\41r@example.com").getUnescapedNode()); + CPPUNIT_ASSERT_EQUAL(std::string("space cadet"), JID("space\\20cadet@example.com").getUnescapedNode()); + CPPUNIT_ASSERT_EQUAL(std::string("call me \"ishmael\""), JID("call\\20me\\20\\22ishmael\\22@example.com").getUnescapedNode()); + CPPUNIT_ASSERT_EQUAL(std::string("at&t guy"), JID("at\\26t\\20guy@example.com").getUnescapedNode()); + CPPUNIT_ASSERT_EQUAL(std::string("d'artagnan"), JID("d\\27artagnan@example.com").getUnescapedNode()); + CPPUNIT_ASSERT_EQUAL(std::string("/.fanboy"), JID("\\2f.fanboy@example.com").getUnescapedNode()); + CPPUNIT_ASSERT_EQUAL(std::string("::foo::"), JID("\\3a\\3afoo\\3a\\3a@example.com").getUnescapedNode()); + CPPUNIT_ASSERT_EQUAL(std::string("<foo>"), JID("\\3cfoo\\3e@example.com").getUnescapedNode()); + CPPUNIT_ASSERT_EQUAL(std::string("user@host"), JID("user\\40host@example.com").getUnescapedNode()); + CPPUNIT_ASSERT_EQUAL(std::string("c:\\net"), JID("c\\3a\\net@example.com").getUnescapedNode()); + CPPUNIT_ASSERT_EQUAL(std::string("c:\\\\net"), JID("c\\3a\\\\net@example.com").getUnescapedNode()); + CPPUNIT_ASSERT_EQUAL(std::string("c:\\cool stuff"), JID("c\\3a\\cool\\20stuff@example.com").getUnescapedNode()); + CPPUNIT_ASSERT_EQUAL(std::string("c:\\5commas"), JID("c\\3a\\5c5commas@example.com").getUnescapedNode()); + } + + void testConstructorWithString_DomainIPv4() { + JID testling("foo@192.34.12.1/resource"); + + CPPUNIT_ASSERT_EQUAL(std::string("foo"), testling.getNode()); + CPPUNIT_ASSERT_EQUAL(std::string("192.34.12.1"), testling.getDomain()); + CPPUNIT_ASSERT_EQUAL(std::string("resource"), testling.getResource()); + CPPUNIT_ASSERT(!testling.isBare()); + CPPUNIT_ASSERT(testling.isValid()); + } + + void testConstructorWithString_DomainNOTIPv4() { + JID testling("foo@500.34.12.1/resource"); + CPPUNIT_ASSERT(!testling.isValid()); + } + + void testConstructorWithString_ValidDomainNOTIPv4() { + JID testling("foo@500.34.12.1a/resource"); + CPPUNIT_ASSERT(testling.isValid()); + } + + void testConstructorWithString_DomainIPv6() { + JID testling("foo@[fe80::a857:33ff:febd:3580]/resource"); + + CPPUNIT_ASSERT_EQUAL(std::string("foo"), testling.getNode()); + CPPUNIT_ASSERT_EQUAL(std::string("[fe80::a857:33ff:febd:3580]"), testling.getDomain()); + CPPUNIT_ASSERT_EQUAL(std::string("resource"), testling.getResource()); + CPPUNIT_ASSERT(!testling.isBare()); + CPPUNIT_ASSERT(testling.isValid()); + } + + void testConstructorWithString_DomainInvalidIPv6() { + JID testling("foo@[1111::a1111:1111:111!:!!!!]/resource"); + CPPUNIT_ASSERT(!testling.isValid()); + } + + void testConstructorWithString_DomainIPv6NoBrackets() { + JID testling("foo@fe80::a857:33ff:febd:3580/resource"); + CPPUNIT_ASSERT(!testling.isValid()); + } }; CPPUNIT_TEST_SUITE_REGISTRATION(JIDTest); |