/* * Copyright (c) 2010-2013 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #define SWIFTEN_CACHE_JID_PREP #include #include #include #include #ifdef SWIFTEN_CACHE_JID_PREP #include #include #endif #include #include #include #include #include #include #include #include using namespace Swift; #ifdef SWIFTEN_CACHE_JID_PREP typedef boost::unordered_map PrepCache; static boost::mutex namePrepCacheMutex; static PrepCache nodePrepCache; static PrepCache domainPrepCache; static PrepCache resourcePrepCache; #endif static const std::list escapedChars = boost::assign::list_of(' ')('"')('&')('\'')('/')('<')('>')('@')(':'); static std::string getEscaped(char c) { return makeString() << '\\' << std::hex << static_cast(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(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 boost::iterator_range operator()(Iterator begin, Iterator end) { for (; begin != end; ++begin) { if (std::find(escapedChars.begin(), escapedChars.end(), *begin) != escapedChars.end()) { return boost::iterator_range(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(begin, begin + 1); } } } } return boost::iterator_range(end, end); } }; struct UnescapedCharacterFormatter { template std::string operator()(const FindResult& match) const { std::ostringstream s; s << '\\' << std::hex << static_cast(*match.begin()); return s.str(); } }; struct EscapedCharacterFinder { template boost::iterator_range 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(begin, innerEnd); } } } return boost::iterator_range(end, end); } }; struct EscapedCharacterFormatter { template 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(&value), 1); } return boost::copy_range(match); } }; #endif namespace Swift { JID::JID(const char* jid) : valid_(true) { initializeFromString(std::string(jid)); } JID::JID(const std::string& jid) : valid_(true) { initializeFromString(jid); } JID::JID(const std::string& node, const std::string& domain) : valid_(true), hasResource_(false) { 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); } 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 nodeAndDomain = String::getSplittedAtFirst(bare, '@'); if (nodeAndDomain.second.empty()) { nameprepAndSetComponents("", nodeAndDomain.first, resource); } else { nameprepAndSetComponents(nodeAndDomain.first, nodeAndDomain.second, resource); } } void JID::nameprepAndSetComponents(const std::string& node, const std::string& domain, const std::string& resource) { if (domain.empty()) { valid_ = false; return; } try { #ifndef SWIFTEN_CACHE_JID_PREP node_ = StringPrep::getPrepared(node, StringPrep::NamePrep); domain_ = StringPrep::getPrepared(domain, StringPrep::XMPPNodePrep); resource_ = StringPrep::getPrepared(resource, StringPrep::XMPPResourcePrep); #else boost::mutex::scoped_lock lock(namePrepCacheMutex); std::pair r; r = nodePrepCache.insert(std::make_pair(node, std::string())); if (r.second) { r.first->second = StringPrep::getPrepared(node, StringPrep::NamePrep); } node_ = r.first->second; r = domainPrepCache.insert(std::make_pair(domain, std::string())); if (r.second) { r.first->second = StringPrep::getPrepared(domain, StringPrep::XMPPNodePrep); } domain_ = r.first->second; r = resourcePrepCache.insert(std::make_pair(resource, std::string())); if (r.second) { r.first->second = StringPrep::getPrepared(resource, StringPrep::XMPPResourcePrep); } resource_ = r.first->second; #endif if (domain_.empty()) { valid_ = false; return; } } catch (const std::exception&) { valid_ = false; } } std::string JID::toString() const { 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; } 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 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(&value), 1); j = innerEnd; continue; } } result += *j; ++j; } return result; //return boost::find_format_all_copy(node_, EscapedCharacterFinder(), EscapedCharacterFormatter()); } std::ostream& operator<<(std::ostream& os, const JID& j) { os << j.toString(); return os; } }