xmpp_stanza.hpp 4.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 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 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 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
#ifndef XMPP_STANZA_INCLUDED
# define XMPP_STANZA_INCLUDED

#include <unordered_map>
#include <string>
#include <vector>

std::string xml_escape(const std::string& data);
std::string xml_unescape(const std::string& data);
std::string sanitize(const std::string& data);

/**
 * Represent an XML node. It has
 * - A parent XML node (in the case of the first-level nodes, the parent is
     nullptr)
 * - zero, one or more children XML nodes
 * - A name
 * - A map of attributes
 * - inner data (text inside the node)
 * - tail data (text just after the node)
 */
class XmlNode
{
public:
  explicit XmlNode(const std::string& name, XmlNode* parent);
  explicit XmlNode(const std::string& name);
  XmlNode(XmlNode&& node):
    name(std::move(node.name)),
    parent(node.parent),
    attributes(std::move(node.attributes)),
    children(std::move(node.children)),
    inner(std::move(node.inner)),
    tail(std::move(node.tail))
  {
    node.parent = nullptr;
  }
  /**
   * The copy constructor do not copy the parent attribute. The children
   * nodes are all copied recursively.
   */
  XmlNode(const XmlNode& node):
    name(node.name),
    parent(nullptr),
    attributes(node.attributes),
    children{},
    inner(node.inner),
    tail(node.tail)
  {
    for (XmlNode* child: node.children)
      {
        XmlNode* child_copy = new XmlNode(*child);
        this->add_child(child_copy);
      }
  }

  ~XmlNode();

  void delete_all_children();
  void set_attribute(const std::string& name, const std::string& value);
  /**
   * Set the content of the tail, that is the text just after this node
   */
  void set_tail(const std::string& data);
  /**
   * Append the given data to the content of the tail. This exists because
   * the expat library may provide the complete text of an element in more
   * than one call
   */
  void add_to_tail(const std::string& data);
  /**
   * Set the content of the inner, that is the text inside this node.
   */
  void set_inner(const std::string& data);
  /**
   * Append the given data to the content of the inner. For the reason
   * described in add_to_tail comment.
   */
  void add_to_inner(const std::string& data);
  /**
   * Get the content of inner
   */
  std::string get_inner() const;
  /**
   * Get the content of the tail
   */
  std::string get_tail() const;
  /**
   * Get a pointer to the first child element with that name and that xml namespace
   */
  XmlNode* get_child(const std::string& name, const std::string& xmlns) const;
  /**
   * Get a vector of all the children that have that name and that xml namespace.
   */
  std::vector<XmlNode*> get_children(const std::string& name, const std::string& xmlns) const;
  /**
   * Add a node child to this node. Assign this node to the child’s parent.
   * Returns a pointer to the newly added child.
   */
  XmlNode* add_child(XmlNode* child);
  XmlNode* add_child(XmlNode&& child);
  /**
   * Returns the last of the children. If the node doesn't have any child,
   * the behaviour is undefined. The user should make sure this is the case
   * by calling has_children() for example.
   */
  XmlNode* get_last_child() const;
  XmlNode* get_parent() const;
  void set_name(const std::string& name);
  const std::string get_name() const;
  /**
   * Serialize the stanza into a string
   */
  std::string to_string() const;
  /**
   * Whether or not this node has at least one child (if not, this is a leaf
   * node)
   */
  bool has_children() const;
  /**
   * Gets the value for the given attribute, returns an empty string if the
   * node as no such attribute.
   */
  const std::string get_tag(const std::string& name) const;
  /**
   * Remove the attribute of the node. Does nothing if that attribute is not
   * present. Returns true if the tag was removed, false if it was absent.
   */
  bool del_tag(const std::string& name);
  /**
   * Use this to set an attribute's value, like node["id"] = "12";
   */
  std::string& operator[](const std::string& name);

private:
  std::string name;
  XmlNode* parent;
  std::unordered_map<std::string, std::string> attributes;
  std::vector<XmlNode*> children;
  std::string inner;
  std::string tail;

  XmlNode& operator=(const XmlNode&) = delete;
  XmlNode& operator=(XmlNode&&) = delete;
};

/**
 * An XMPP stanza is just an XML node of level 2 in the XMPP document (the
 * level 1 ones are the <stream::stream/>, and the ones above 2 are just the
 * content of the stanzas)
 */
typedef XmlNode Stanza;

#endif // XMPP_STANZA_INCLUDED