263 lines
5.3 KiB
C++
263 lines
5.3 KiB
C++
#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 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)
|
|
{
|
|
// 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;
|
|
}
|
|
|
|
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)
|
|
{
|
|
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")));
|
|
}
|