diff options
Diffstat (limited to 'src/xmpp')
-rw-r--r-- | src/xmpp/jid.cpp | 94 | ||||
-rw-r--r-- | src/xmpp/jid.hpp | 36 | ||||
-rw-r--r-- | src/xmpp/xmpp_parser.cpp | 169 | ||||
-rw-r--r-- | src/xmpp/xmpp_parser.hpp | 126 | ||||
-rw-r--r-- | src/xmpp/xmpp_stanza.cpp | 274 | ||||
-rw-r--r-- | src/xmpp/xmpp_stanza.hpp | 160 |
6 files changed, 0 insertions, 859 deletions
diff --git a/src/xmpp/jid.cpp b/src/xmpp/jid.cpp deleted file mode 100644 index 6149ceb..0000000 --- a/src/xmpp/jid.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#include <xmpp/jid.hpp> -#include <cstring> -#include <map> - -#include <louloulibs.h> -#ifdef LIBIDN_FOUND - #include <stringprep.h> -#endif - -#include <logger/logger.hpp> - -Jid::Jid(const std::string& jid) -{ - std::string::size_type slash = jid.find('/'); - if (slash != std::string::npos) - { - this->resource = jid.substr(slash + 1); - } - - std::string::size_type at = jid.find('@'); - if (at != std::string::npos && at < slash) - { - this->local = jid.substr(0, at); - at++; - } - else - at = 0; - - this->domain = jid.substr(at, slash - at); -} - -#include <iostream> - -static constexpr size_t max_jid_part_len = 1023; - -std::string jidprep(const std::string& original) -{ -#ifdef LIBIDN_FOUND - using CacheType = std::map<std::string, std::string>; - static CacheType cache; - std::pair<CacheType::iterator, bool> cached = cache.insert({original, {}}); - if (std::get<1>(cached) == false) - { // Insertion failed: the result is already in the cache, return it - return std::get<0>(cached)->second; - } - - const std::string error_msg("Failed to convert " + original + " into a valid JID:"); - Jid jid(original); - - char local[max_jid_part_len] = {}; - memcpy(local, jid.local.data(), jid.local.size()); - Stringprep_rc rc = static_cast<Stringprep_rc>(::stringprep(local, max_jid_part_len, - static_cast<Stringprep_profile_flags>(0), stringprep_xmpp_nodeprep)); - if (rc != STRINGPREP_OK) - { - log_error(error_msg + stringprep_strerror(rc)); - return ""; - } - - char domain[max_jid_part_len] = {}; - memcpy(domain, jid.domain.data(), jid.domain.size()); - rc = static_cast<Stringprep_rc>(::stringprep(domain, max_jid_part_len, - static_cast<Stringprep_profile_flags>(0), stringprep_nameprep)); - if (rc != STRINGPREP_OK) - { - log_error(error_msg + stringprep_strerror(rc)); - return ""; - } - - // If there is no resource, stop here - if (jid.resource.empty()) - { - std::get<0>(cached)->second = std::string(local) + "@" + domain; - return std::get<0>(cached)->second; - } - - // Otherwise, also process the resource part - char resource[max_jid_part_len] = {}; - memcpy(resource, jid.resource.data(), jid.resource.size()); - rc = static_cast<Stringprep_rc>(::stringprep(resource, max_jid_part_len, - static_cast<Stringprep_profile_flags>(0), stringprep_xmpp_resourceprep)); - if (rc != STRINGPREP_OK) - { - log_error(error_msg + stringprep_strerror(rc)); - return ""; - } - std::get<0>(cached)->second = std::string(local) + "@" + domain + "/" + resource; - return std::get<0>(cached)->second; - -#else - (void)original; - return ""; -#endif -} diff --git a/src/xmpp/jid.hpp b/src/xmpp/jid.hpp deleted file mode 100644 index b6975a2..0000000 --- a/src/xmpp/jid.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef JID_INCLUDED -# define JID_INCLUDED - -#include <string> - -/** - * Parse a JID into its different subart - */ -class Jid -{ -public: - explicit Jid(const std::string& jid); - - std::string domain; - std::string local; - std::string resource; - -private: - Jid(const Jid&) = delete; - Jid(Jid&&) = delete; - Jid& operator=(const Jid&) = delete; - Jid& operator=(Jid&&) = delete; -}; - -/** - * Prepare the given UTF-8 string according to the XMPP node stringprep - * identifier profile. This is used to send properly-formed JID to the XMPP - * server. - * - * If the stringprep library is not found, we return an empty string. When - * this function is used, the result must always be checked for an empty - * value, and if this is the case it must not be used as a JID. - */ -std::string jidprep(const std::string& original); - -#endif // JID_INCLUDED diff --git a/src/xmpp/xmpp_parser.cpp b/src/xmpp/xmpp_parser.cpp deleted file mode 100644 index 6bb0d28..0000000 --- a/src/xmpp/xmpp_parser.cpp +++ /dev/null @@ -1,169 +0,0 @@ -#include <xmpp/xmpp_parser.hpp> -#include <xmpp/xmpp_stanza.hpp> - -#include <logger/logger.hpp> - -/** - * Expat handlers. Called by the Expat library, never by ourself. - * They just forward the call to the XmppParser corresponding methods. - */ - -static void start_element_handler(void* user_data, const XML_Char* name, const XML_Char** atts) -{ - static_cast<XmppParser*>(user_data)->start_element(name, atts); -} - -static void end_element_handler(void* user_data, const XML_Char* name) -{ - static_cast<XmppParser*>(user_data)->end_element(name); -} - -static void character_data_handler(void *user_data, const XML_Char *s, int len) -{ - static_cast<XmppParser*>(user_data)->char_data(s, len); -} - -/** - * XmppParser class - */ - -XmppParser::XmppParser(): - level(0), - current_node(nullptr) -{ - this->init_xml_parser(); -} - -void XmppParser::init_xml_parser() -{ - // Create the expat parser - this->parser = XML_ParserCreateNS("UTF-8", ':'); - XML_SetUserData(this->parser, static_cast<void*>(this)); - - // Install Expat handlers - XML_SetElementHandler(this->parser, &start_element_handler, &end_element_handler); - XML_SetCharacterDataHandler(this->parser, &character_data_handler); -} - -XmppParser::~XmppParser() -{ - if (this->current_node) - delete this->current_node; - XML_ParserFree(this->parser); -} - -int XmppParser::feed(const char* data, const int len, const bool is_final) -{ - int res = XML_Parse(this->parser, data, len, is_final); - if (res == XML_STATUS_ERROR && - (XML_GetErrorCode(this->parser) != XML_ERROR_FINISHED)) - log_error("Xml_Parse encountered an error: " << - XML_ErrorString(XML_GetErrorCode(this->parser))) - return res; -} - -int XmppParser::parse(const int len, const bool is_final) -{ - int res = XML_ParseBuffer(this->parser, len, is_final); - if (res == XML_STATUS_ERROR) - log_error("Xml_Parsebuffer encountered an error: " << - XML_ErrorString(XML_GetErrorCode(this->parser))); - return res; -} - -void XmppParser::reset() -{ - XML_ParserFree(this->parser); - this->init_xml_parser(); - if (this->current_node) - delete this->current_node; - this->current_node = nullptr; - this->level = 0; -} - -void* XmppParser::get_buffer(const size_t size) const -{ - return XML_GetBuffer(this->parser, static_cast<int>(size)); -} - -void XmppParser::start_element(const XML_Char* name, const XML_Char** attribute) -{ - level++; - - XmlNode* new_node = new XmlNode(name, this->current_node); - if (this->current_node) - this->current_node->add_child(new_node); - this->current_node = new_node; - for (size_t i = 0; attribute[i]; i += 2) - this->current_node->set_attribute(attribute[i], attribute[i+1]); - if (this->level == 1) - this->stream_open_event(*this->current_node); -} - -void XmppParser::end_element(const XML_Char* name) -{ - (void)name; - level--; - this->current_node->close(); - if (level == 1) - { - this->stanza_event(*this->current_node); - } - if (level == 0) - { - this->stream_close_event(*this->current_node); - delete this->current_node; - this->current_node = nullptr; - } - else - this->current_node = this->current_node->get_parent(); - if (level == 1) - this->current_node->delete_all_children(); -} - -void XmppParser::char_data(const XML_Char* data, int len) -{ - if (this->current_node->has_children()) - this->current_node->get_last_child()->add_to_tail(std::string(data, len)); - else - this->current_node->add_to_inner(std::string(data, len)); -} - -void XmppParser::stanza_event(const Stanza& stanza) const -{ - for (const auto& callback: this->stanza_callbacks) - { - try { - callback(stanza); - } catch (const std::exception& e) { - log_debug("Unhandled exception: " << e.what()); - } - } -} - -void XmppParser::stream_open_event(const XmlNode& node) const -{ - for (const auto& callback: this->stream_open_callbacks) - callback(node); -} - -void XmppParser::stream_close_event(const XmlNode& node) const -{ - for (const auto& callback: this->stream_close_callbacks) - callback(node); -} - -void XmppParser::add_stanza_callback(std::function<void(const Stanza&)>&& callback) -{ - this->stanza_callbacks.emplace_back(std::move(callback)); -} - -void XmppParser::add_stream_open_callback(std::function<void(const XmlNode&)>&& callback) -{ - this->stream_open_callbacks.emplace_back(std::move(callback)); -} - -void XmppParser::add_stream_close_callback(std::function<void(const XmlNode&)>&& callback) -{ - this->stream_close_callbacks.emplace_back(std::move(callback)); -} diff --git a/src/xmpp/xmpp_parser.hpp b/src/xmpp/xmpp_parser.hpp deleted file mode 100644 index 79c9f8f..0000000 --- a/src/xmpp/xmpp_parser.hpp +++ /dev/null @@ -1,126 +0,0 @@ -#ifndef XMPP_PARSER_INCLUDED -# define XMPP_PARSER_INCLUDED - -#include <xmpp/xmpp_stanza.hpp> - -#include <functional> - -#include <expat.h> - -/** - * A SAX XML parser that builds XML nodes and spawns events when a complete - * stanza is received (an element of level 2), or when the document is - * opened/closed (an element of level 1) - * - * After a stanza_event has been spawned, we delete the whole stanza. This - * means that even with a very long document (in XMPP the document is - * potentially infinite), the memory is never exhausted as long as each - * stanza is reasonnably short. - * - * The element names generated by expat contain the namespace of the - * element, a colon (':') and then the actual name of the element. To get - * an element "x" with a namespace of "http://jabber.org/protocol/muc", you - * just look for an XmlNode named "http://jabber.org/protocol/muc:x" - * - * TODO: enforce the size-limit for the stanza (limit the number of childs - * it can contain). For example forbid the parser going further than level - * 20 (arbitrary number here), and each XML node to have more than 15 childs - * (arbitrary number again). - */ -class XmppParser -{ -public: - explicit XmppParser(); - ~XmppParser(); - -public: - /** - * Init the XML parser and install the callbacks - */ - void init_xml_parser(); - /** - * Feed the parser with some XML data - */ - int feed(const char* data, const int len, const bool is_final); - /** - * Parse the data placed in the parser buffer - */ - int parse(const int size, const bool is_final); - /** - * Reset the parser, so it can be used from scratch afterward - */ - void reset(); - /** - * Get a buffer provided by the xml parser. - */ - void* get_buffer(const size_t size) const; - /** - * Add one callback for the various events that this parser can spawn. - */ - void add_stanza_callback(std::function<void(const Stanza&)>&& callback); - void add_stream_open_callback(std::function<void(const XmlNode&)>&& callback); - void add_stream_close_callback(std::function<void(const XmlNode&)>&& callback); - - /** - * Called when a new XML element has been opened. We instanciate a new - * XmlNode and set it as our current node. The parent of this new node is - * the previous "current" node. We have all the element's attributes in - * this event. - * - * We spawn a stream_event with this node if this is a level-1 element. - */ - void start_element(const XML_Char* name, const XML_Char** attribute); - /** - * Called when an XML element has been closed. We close the current_node, - * set our current_node as the parent of the current_node, and if that was - * a level-2 element we spawn a stanza_event with this node. - * - * And we then delete the stanza (and everything under it, its children, - * attribute, etc). - */ - void end_element(const XML_Char* name); - /** - * Some inner or tail data has been parsed - */ - void char_data(const XML_Char* data, int len); - /** - * Calls all the stanza_callbacks one by one. - */ - void stanza_event(const Stanza& stanza) const; - /** - * Calls all the stream_open_callbacks one by one. Note: the passed node is not - * closed yet. - */ - void stream_open_event(const XmlNode& node) const; - /** - * Calls all the stream_close_callbacks one by one. - */ - void stream_close_event(const XmlNode& node) const; - -private: - /** - * Expat structure. - */ - XML_Parser parser; - /** - * The current depth in the XML document - */ - size_t level; - /** - * The deepest XML node opened but not yet closed (to which we are adding - * new children, inner or tail) - */ - XmlNode* current_node; - /** - * A list of callbacks to be called on an *_event, receiving the - * concerned Stanza/XmlNode. - */ - std::vector<std::function<void(const Stanza&)>> stanza_callbacks; - std::vector<std::function<void(const XmlNode&)>> stream_open_callbacks; - std::vector<std::function<void(const XmlNode&)>> stream_close_callbacks; - - XmppParser(const XmppParser&) = delete; - XmppParser& operator=(const XmppParser&) = delete; -}; - -#endif // XMPP_PARSER_INCLUDED diff --git a/src/xmpp/xmpp_stanza.cpp b/src/xmpp/xmpp_stanza.cpp deleted file mode 100644 index df19105..0000000 --- a/src/xmpp/xmpp_stanza.cpp +++ /dev/null @@ -1,274 +0,0 @@ -#include <xmpp/xmpp_stanza.hpp> - -#include <utils/encoding.hpp> -#include <utils/split.hpp> - -#include <stdexcept> -#include <iostream> - -#include <string.h> - -std::string xml_escape(const std::string& data) -{ - std::string res; - res.reserve(data.size()); - for (size_t pos = 0; pos != data.size(); ++pos) - { - switch(data[pos]) - { - case '&': - res += "&"; - break; - case '<': - res += "<"; - break; - case '>': - res += ">"; - break; - case '\"': - res += """; - break; - case '\'': - res += "'"; - break; - default: - res += data[pos]; - break; - } - } - return res; -} - -std::string xml_unescape(const std::string& data) -{ - std::string res; - res.reserve(data.size()); - const char* str = data.c_str(); - while (str && *str && static_cast<size_t>(str - data.c_str()) < data.size()) - { - if (*str == '&') - { - if (strncmp(str+1, "amp;", 4) == 0) - { - res += "&"; - str += 4; - } - else if (strncmp(str+1, "lt;", 3) == 0) - { - res += "<"; - str += 3; - } - else if (strncmp(str+1, "gt;", 3) == 0) - { - res += ">"; - str += 3; - } - else if (strncmp(str+1, "quot;", 5) == 0) - { - res += "\""; - str += 5; - } - else if (strncmp(str+1, "apos;", 5) == 0) - { - res += "'"; - str += 5; - } - else - res += "&"; - } - else - res += *str; - str++; - } - return res; -} - -XmlNode::XmlNode(const std::string& name, XmlNode* parent): - parent(parent), - closed(false) -{ - // split the namespace and the name - auto n = name.rfind(":"); - if (n == std::string::npos) - this->name = name; - else - { - this->name = name.substr(n+1); - this->attributes["xmlns"] = name.substr(0, n); - } -} - -XmlNode::XmlNode(const std::string& name): - XmlNode(name, nullptr) -{ -} - -XmlNode::~XmlNode() -{ - this->delete_all_children(); -} - -void XmlNode::delete_all_children() -{ - for (auto& child: this->children) - { - delete child; - } - this->children.clear(); -} - -void XmlNode::set_attribute(const std::string& name, const std::string& value) -{ - this->attributes[name] = value; -} - -void XmlNode::set_tail(const std::string& data) -{ - this->tail = xml_escape(data); -} - -void XmlNode::add_to_tail(const std::string& data) -{ - this->tail += xml_escape(data); -} - -void XmlNode::set_inner(const std::string& data) -{ - this->inner = xml_escape(data); -} - -void XmlNode::add_to_inner(const std::string& data) -{ - this->inner += xml_escape(data); -} - -std::string XmlNode::get_inner() const -{ - return xml_unescape(this->inner); -} - -std::string XmlNode::get_tail() const -{ - return xml_unescape(this->tail); -} - -XmlNode* XmlNode::get_child(const std::string& name, const std::string& xmlns) const -{ - for (auto& child: this->children) - { - if (child->name == name && child->get_tag("xmlns") == xmlns) - return child; - } - return nullptr; -} - -std::vector<XmlNode*> XmlNode::get_children(const std::string& name, const std::string& xmlns) const -{ - std::vector<XmlNode*> res; - for (auto& child: this->children) - { - if (child->name == name && child->get_tag("xmlns") == xmlns) - res.push_back(child); - } - return res; -} - -XmlNode* XmlNode::add_child(XmlNode* child) -{ - child->parent = this; - this->children.push_back(child); - return child; -} - -XmlNode* XmlNode::add_child(XmlNode&& child) -{ - XmlNode* new_node = new XmlNode(std::move(child)); - return this->add_child(new_node); -} - -XmlNode* XmlNode::get_last_child() const -{ - return this->children.back(); -} - -void XmlNode::close() -{ - if (this->closed) - throw std::runtime_error("Closing an already closed XmlNode"); - this->closed = true; -} - -XmlNode* XmlNode::get_parent() const -{ - return this->parent; -} - -void XmlNode::set_name(const std::string& name) -{ - this->name = name; -} - -const std::string XmlNode::get_name() const -{ - return this->name; -} - -std::string XmlNode::to_string() const -{ - std::string res("<"); - res += this->name; - for (const auto& it: this->attributes) - res += " " + it.first + "='" + sanitize(it.second) + "'"; - if (this->closed && !this->has_children() && this->inner.empty()) - res += "/>"; - else - { - res += ">" + sanitize(this->inner); - for (const auto& child: this->children) - res += child->to_string(); - if (this->closed) - { - res += "</" + this->get_name() + ">"; - } - } - res += sanitize(this->tail); - return res; -} - -bool XmlNode::has_children() const -{ - return !this->children.empty(); -} - -const std::string XmlNode::get_tag(const std::string& name) const -{ - try - { - const auto& value = this->attributes.at(name); - return value; - } - catch (const std::out_of_range& e) - { - return ""; - } -} - -bool XmlNode::del_tag(const std::string& name) -{ - if (this->attributes.erase(name) != 0) - return true; - return false; -} - -std::string& XmlNode::operator[](const std::string& name) -{ - return this->attributes[name]; -} - -std::string sanitize(const std::string& data) -{ - if (utils::is_valid_utf8(data.data())) - return xml_escape(utils::remove_invalid_xml_chars(data)); - else - return xml_escape(utils::remove_invalid_xml_chars(utils::convert_to_utf8(data, "ISO-8859-1"))); -} diff --git a/src/xmpp/xmpp_stanza.hpp b/src/xmpp/xmpp_stanza.hpp deleted file mode 100644 index f1a6a0f..0000000 --- a/src/xmpp/xmpp_stanza.hpp +++ /dev/null @@ -1,160 +0,0 @@ -#ifndef XMPP_STANZA_INCLUDED -# define XMPP_STANZA_INCLUDED - -#include <unordered_map> -#include <string> -#include <vector> - -std::string xml_escape(const std::string& data); -std::string xml_unescape(const std::string& data); -std::string sanitize(const std::string& data); - -/** - * Represent an XML node. It has - * - A parent XML node (in the case of the first-level nodes, the parent is - nullptr) - * - zero, one or more children XML nodes - * - A name - * - A map of attributes - * - inner data (text inside the node) - * - tail data (text just after the node) - */ -class XmlNode -{ -public: - explicit XmlNode(const std::string& name, XmlNode* parent); - explicit XmlNode(const std::string& name); - XmlNode(XmlNode&& node): - name(std::move(node.name)), - parent(node.parent), - closed(node.closed), - attributes(std::move(node.attributes)), - children(std::move(node.children)), - inner(std::move(node.inner)), - tail(std::move(node.tail)) - { - node.parent = nullptr; - } - /** - * The copy constructor do not copy the parent attribute. The children - * nodes are all copied recursively. - */ - XmlNode(const XmlNode& node): - name(node.name), - parent(nullptr), - closed(node.closed), - attributes(node.attributes), - children{}, - inner(node.inner), - tail(node.tail) - { - for (XmlNode* child: node.children) - { - XmlNode* child_copy = new XmlNode(*child); - this->add_child(child_copy); - } - } - - ~XmlNode(); - - void delete_all_children(); - void set_attribute(const std::string& name, const std::string& value); - /** - * Set the content of the tail, that is the text just after this node - */ - void set_tail(const std::string& data); - /** - * Append the given data to the content of the tail. This exists because - * the expat library may provide the complete text of an element in more - * than one call - */ - void add_to_tail(const std::string& data); - /** - * Set the content of the inner, that is the text inside this node. - */ - void set_inner(const std::string& data); - /** - * Append the given data to the content of the inner. For the reason - * described in add_to_tail comment. - */ - void add_to_inner(const std::string& data); - /** - * Get the content of inner - */ - std::string get_inner() const; - /** - * Get the content of the tail - */ - std::string get_tail() const; - /** - * Get a pointer to the first child element with that name and that xml namespace - */ - XmlNode* get_child(const std::string& name, const std::string& xmlns) const; - /** - * Get a vector of all the children that have that name and that xml namespace. - */ - std::vector<XmlNode*> get_children(const std::string& name, const std::string& xmlns) const; - /** - * Add a node child to this node. Assign this node to the child’s parent. - * Returns a pointer to the newly added child. - */ - XmlNode* add_child(XmlNode* child); - XmlNode* add_child(XmlNode&& child); - /** - * Returns the last of the children. If the node doesn't have any child, - * the behaviour is undefined. The user should make sure this is the case - * by calling has_children() for example. - */ - XmlNode* get_last_child() const; - /** - * Mark this node as closed, nothing else - */ - void close(); - XmlNode* get_parent() const; - void set_name(const std::string& name); - const std::string get_name() const; - /** - * Serialize the stanza into a string - */ - std::string to_string() const; - /** - * Whether or not this node has at least one child (if not, this is a leaf - * node) - */ - bool has_children() const; - /** - * Gets the value for the given attribute, returns an empty string if the - * node as no such attribute. - */ - const std::string get_tag(const std::string& name) const; - /** - * Remove the attribute of the node. Does nothing if that attribute is not - * present. Returns true if the tag was removed, false if it was absent. - */ - bool del_tag(const std::string& name); - /** - * Use this to set an attribute's value, like node["id"] = "12"; - */ - std::string& operator[](const std::string& name); - -private: - std::string name; - XmlNode* parent; - bool closed; - std::unordered_map<std::string, std::string> attributes; - std::vector<XmlNode*> children; - std::string inner; - std::string tail; - - XmlNode& operator=(const XmlNode&) = delete; - XmlNode& operator=(XmlNode&&) = delete; -}; - -/** - * An XMPP stanza is just an XML node of level 2 in the XMPP document (the - * level 1 ones are the <stream::stream/>, and the ones above 2 are just the - * content of the stanzas) - */ -typedef XmlNode Stanza; - -#endif // XMPP_STANZA_INCLUDED |