236 lines
4.5 KiB
C++
236 lines
4.5 KiB
C++
#include <xmpp/xmpp_stanza.hpp>
|
|
|
|
#include <utils/encoding.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):
|
|
name(name),
|
|
parent(parent),
|
|
closed(false)
|
|
{
|
|
}
|
|
|
|
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
|
|
{
|
|
for (auto& child: this->children)
|
|
{
|
|
if (child->name == name)
|
|
return child;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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 += " " + utils::remove_invalid_xml_chars(it.first) + "='" +
|
|
utils::remove_invalid_xml_chars(it.second) + "'";
|
|
if (this->closed && !this->has_children() && this->inner.empty())
|
|
res += "/>";
|
|
else
|
|
{
|
|
res += ">" + utils::remove_invalid_xml_chars(this->inner);
|
|
for (const auto& child: this->children)
|
|
res += child->to_string();
|
|
if (this->closed)
|
|
{
|
|
res += "</" + this->name + ">";
|
|
}
|
|
}
|
|
res += utils::remove_invalid_xml_chars(this->tail);
|
|
return res;
|
|
}
|
|
|
|
bool XmlNode::has_children() const
|
|
{
|
|
return !this->children.empty();
|
|
}
|
|
|
|
const std::string& XmlNode::operator[](const std::string& name) const
|
|
{
|
|
try
|
|
{
|
|
const auto& value = this->attributes.at(name);
|
|
return value;
|
|
}
|
|
catch (const std::out_of_range& e)
|
|
{
|
|
throw AttributeNotFound();
|
|
}
|
|
}
|
|
|
|
std::string& XmlNode::operator[](const std::string& name)
|
|
{
|
|
return this->attributes[name];
|
|
}
|