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

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

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

9 10
#include <string.h>

louiz’'s avatar
louiz’ committed
11 12 13
std::string xml_escape(const std::string& data)
{
  std::string res;
louiz’'s avatar
louiz’ committed
14 15
  res.reserve(data.size());
  for (size_t pos = 0; pos != data.size(); ++pos)
louiz’'s avatar
louiz’ committed
16 17 18
    {
      switch(data[pos])
        {
louiz’'s avatar
louiz’ committed
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
        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
37 38
        }
    }
louiz’'s avatar
louiz’ committed
39
  return res;
louiz’'s avatar
louiz’ committed
40 41
}

42 43 44 45 46
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
47
  while (str && *str && static_cast<size_t>(str - data.c_str()) < data.size())
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 84
    {
      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
85

86 87 88 89
XmlNode::XmlNode(const std::string& name, XmlNode* parent):
  parent(parent),
  closed(false)
{
90 91 92 93 94 95 96 97 98
  // 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);
    }
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
}

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
127
  this->tail = xml_escape(data);
128 129
}

130 131
void XmlNode::add_to_tail(const std::string& data)
{
louiz’'s avatar
louiz’ committed
132
  this->tail += xml_escape(data);
133 134
}

135 136
void XmlNode::set_inner(const std::string& data)
{
louiz’'s avatar
louiz’ committed
137 138 139
  this->inner = xml_escape(data);
}

140 141 142 143 144
void XmlNode::add_to_inner(const std::string& data)
{
  this->inner += xml_escape(data);
}

louiz’'s avatar
louiz’ committed
145 146
std::string XmlNode::get_inner() const
{
147
  return xml_unescape(this->inner);
louiz’'s avatar
louiz’ committed
148 149
}

150 151
std::string XmlNode::get_tail() const
{
152
  return xml_unescape(this->tail);
153 154
}

155
XmlNode* XmlNode::get_child(const std::string& name, const std::string& xmlns) const
louiz’'s avatar
louiz’ committed
156 157 158
{
  for (auto& child: this->children)
    {
159
      if (child->name == name && child->get_tag("xmlns") == xmlns)
louiz’'s avatar
louiz’ committed
160 161 162
        return child;
    }
  return nullptr;
163 164
}

165 166 167 168 169 170 171 172 173 174 175
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;
}

176
XmlNode* XmlNode::add_child(XmlNode* child)
177
{
178
  child->parent = this;
179
  this->children.push_back(child);
180
  return child;
181 182
}

183
XmlNode* XmlNode::add_child(XmlNode&& child)
184 185
{
  XmlNode* new_node = new XmlNode(std::move(child));
186
  return this->add_child(new_node);
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
}

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;
}

206 207 208 209 210
void XmlNode::set_name(const std::string& name)
{
  this->name = name;
}

211
const std::string XmlNode::get_name() const
212
{
213
  return this->name;
214 215 216 217 218 219 220
}

std::string XmlNode::to_string() const
{
  std::string res("<");
  res += this->name;
  for (const auto& it: this->attributes)
221 222
    res += " " + utils::remove_invalid_xml_chars(it.first) + "='" +
      utils::remove_invalid_xml_chars(it.second) + "'";
223 224 225 226
  if (this->closed && !this->has_children() && this->inner.empty())
    res += "/>";
  else
    {
227
      res += ">" + utils::remove_invalid_xml_chars(this->inner);
228 229 230 231
      for (const auto& child: this->children)
        res += child->to_string();
      if (this->closed)
        {
232
          res += "</" + this->get_name() + ">";
233 234
        }
    }
235
  res += utils::remove_invalid_xml_chars(this->tail);
236 237 238 239 240 241 242 243
  return res;
}

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

244
const std::string XmlNode::get_tag(const std::string& name) const
245 246 247 248 249 250 251 252
{
  try
    {
      const auto& value = this->attributes.at(name);
      return value;
    }
  catch (const std::out_of_range& e)
    {
253
      return "";
254 255 256
    }
}

257 258 259 260 261 262 263
bool XmlNode::del_tag(const std::string& name)
{
  if (this->attributes.erase(name) != 0)
    return true;
  return false;
}

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