xmpp_stanza.cpp 4.48 KB
Newer Older
1 2
#include <xmpp/xmpp_stanza.hpp>

louiz’'s avatar
louiz’ committed
3 4
#include <utils/encoding.hpp>

louiz’'s avatar
louiz’ committed
5
#include <stdexcept>
6 7
#include <iostream>

8 9
#include <string.h>

louiz’'s avatar
louiz’ committed
10 11 12
std::string xml_escape(const std::string& data)
{
  std::string res;
louiz’'s avatar
louiz’ committed
13 14
  res.reserve(data.size());
  for (size_t pos = 0; pos != data.size(); ++pos)
louiz’'s avatar
louiz’ committed
15 16 17
    {
      switch(data[pos])
        {
louiz’'s avatar
louiz’ committed
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
        case '&':
          res += "&amp;";
          break;
        case '<':
          res += "&lt;";
          break;
        case '>':
          res += "&gt;";
          break;
        case '\"':
          res += "&quot;";
          break;
        case '\'':
          res += "&apos;";
          break;
        default:
          res += data[pos];
          break;
louiz’'s avatar
louiz’ committed
36 37
        }
    }
louiz’'s avatar
louiz’ committed
38
  return res;
louiz’'s avatar
louiz’ committed
39 40
}

41 42 43 44 45
std::string xml_unescape(const std::string& data)
{
  std::string res;
  res.reserve(data.size());
  const char* str = data.c_str();
louiz’'s avatar
louiz’ committed
46
  while (str && *str && static_cast<size_t>(str - data.c_str()) < data.size())
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
    {
      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;
}
louiz’'s avatar
louiz’ committed
84

85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
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)
{
louiz’'s avatar
louiz’ committed
118
  this->tail = xml_escape(data);
119 120
}

121 122
void XmlNode::add_to_tail(const std::string& data)
{
louiz’'s avatar
louiz’ committed
123
  this->tail += xml_escape(data);
124 125
}

126 127
void XmlNode::set_inner(const std::string& data)
{
louiz’'s avatar
louiz’ committed
128 129 130
  this->inner = xml_escape(data);
}

131 132 133 134 135
void XmlNode::add_to_inner(const std::string& data)
{
  this->inner += xml_escape(data);
}

louiz’'s avatar
louiz’ committed
136 137
std::string XmlNode::get_inner() const
{
138
  return xml_unescape(this->inner);
louiz’'s avatar
louiz’ committed
139 140
}

141 142
std::string XmlNode::get_tail() const
{
143
  return xml_unescape(this->tail);
144 145
}

louiz’'s avatar
louiz’ committed
146 147 148 149 150 151 152 153
XmlNode* XmlNode::get_child(const std::string& name) const
{
  for (auto& child: this->children)
    {
      if (child->name == name)
        return child;
    }
  return nullptr;
154 155
}

156
XmlNode* XmlNode::add_child(XmlNode* child)
157
{
158
  child->parent = this;
159
  this->children.push_back(child);
160
  return child;
161 162
}

163
XmlNode* XmlNode::add_child(XmlNode&& child)
164 165
{
  XmlNode* new_node = new XmlNode(std::move(child));
166
  return this->add_child(new_node);
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
}

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)
196 197
    res += " " + utils::remove_invalid_xml_chars(it.first) + "='" +
      utils::remove_invalid_xml_chars(it.second) + "'";
198 199 200 201
  if (this->closed && !this->has_children() && this->inner.empty())
    res += "/>";
  else
    {
202
      res += ">" + utils::remove_invalid_xml_chars(this->inner);
203 204 205 206 207 208 209
      for (const auto& child: this->children)
        res += child->to_string();
      if (this->closed)
        {
          res += "</" + this->name + ">";
        }
    }
210
  res += utils::remove_invalid_xml_chars(this->tail);
211 212 213 214 215 216 217 218
  return res;
}

bool XmlNode::has_children() const
{
  return !this->children.empty();
}

219
const std::string XmlNode::get_tag(const std::string& name) const
220 221 222 223 224 225 226 227
{
  try
    {
      const auto& value = this->attributes.at(name);
      return value;
    }
  catch (const std::out_of_range& e)
    {
228
      return "";
229 230 231 232 233 234 235
    }
}

std::string& XmlNode::operator[](const std::string& name)
{
  return this->attributes[name];
}