#include <xmpp/xmpp_stanza.hpp> #include <utils/encoding.hpp> #include <utils/split.hpp> #include <stdexcept> #include <iostream> #include <sstream> #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 sanitize(const std::string& data, const std::string& encoding) { 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, encoding.data()))); } XmlNode::XmlNode(const std::string& name, XmlNode* parent): parent(parent) { // 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) { } void XmlNode::delete_all_children() { 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 = data; } void XmlNode::add_to_tail(const std::string& data) { this->tail += data; } void XmlNode::set_inner(const std::string& data) { this->inner = data; } void XmlNode::add_to_inner(const std::string& data) { this->inner += data; } std::string XmlNode::get_inner() const { return this->inner; } std::string XmlNode::get_tail() const { return this->tail; } const XmlNode* XmlNode::get_child(const std::string& name, const std::string& xmlns) const { for (const auto& child: this->children) { if (child->name == name && child->get_tag("xmlns") == xmlns) return child.get(); } return nullptr; } std::vector<const XmlNode*> XmlNode::get_children(const std::string& name, const std::string& xmlns) const { std::vector<const XmlNode*> res; for (const auto& child: this->children) { if (child->name == name && child->get_tag("xmlns") == xmlns) res.push_back(child.get()); } return res; } XmlNode* XmlNode::add_child(std::unique_ptr<XmlNode> child) { child->parent = this; auto ret = child.get(); this->children.push_back(std::move(child)); return ret; } XmlNode* XmlNode::add_child(XmlNode&& child) { auto new_node = std::make_unique<XmlNode>(std::move(child)); return this->add_child(std::move(new_node)); } XmlNode* XmlNode::add_child(const XmlNode& child) { auto new_node = std::make_unique<XmlNode>(child); return this->add_child(std::move(new_node)); } XmlNode* XmlNode::get_last_child() const { return this->children.back().get(); } XmlNode* XmlNode::get_parent() const { return this->parent; } void XmlNode::set_name(const std::string& name) { this->name = name; } void XmlNode::set_name(std::string&& name) { this->name = std::move(name); } const std::string XmlNode::get_name() const { return this->name; } std::string XmlNode::to_string() const { std::ostringstream res; res << "<" << this->name; for (const auto& it: this->attributes) res << " " << it.first << "='" << sanitize(it.second) + "'"; if (!this->has_children() && this->inner.empty()) res << "/>"; else { res << ">" + sanitize(this->inner); for (const auto& child: this->children) res << child->to_string(); res << "</" << this->get_name() << ">"; } res << sanitize(this->tail); return res.str(); } 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) { static const std::string def{}; return def; } } 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::ostream& operator<<(std::ostream& os, const XmlNode& node) { return os << node.to_string(); }