Forgot to remove some XMPP files that are now in louloulibs instead
This commit is contained in:
@@ -76,9 +76,6 @@ include_directories(${ICONV_INCLUDE_DIRS})
|
||||
include_directories(${LIBUUID_INCLUDE_DIRS})
|
||||
|
||||
# If they are found in louloulibs CMakeLists.txt, we inherite these values
|
||||
if(LIBIDN_FOUND)
|
||||
include_directories(${LIBIDN_INCLUDE_DIRS})
|
||||
endif()
|
||||
if(SYSTEMD_FOUND)
|
||||
include_directories(${SYSTEMD_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
#include <xmpp/jid.hpp>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
|
||||
#include <louloulibs.h>
|
||||
#ifdef LIBIDN_FOUND
|
||||
#include <stringprep.h>
|
||||
#endif
|
||||
|
||||
#include <logger/logger.hpp>
|
||||
|
||||
Jid::Jid(const std::string& jid)
|
||||
{
|
||||
std::string::size_type slash = jid.find('/');
|
||||
if (slash != std::string::npos)
|
||||
{
|
||||
this->resource = jid.substr(slash + 1);
|
||||
}
|
||||
|
||||
std::string::size_type at = jid.find('@');
|
||||
if (at != std::string::npos && at < slash)
|
||||
{
|
||||
this->local = jid.substr(0, at);
|
||||
at++;
|
||||
}
|
||||
else
|
||||
at = 0;
|
||||
|
||||
this->domain = jid.substr(at, slash - at);
|
||||
}
|
||||
|
||||
#include <iostream>
|
||||
|
||||
static constexpr size_t max_jid_part_len = 1023;
|
||||
|
||||
std::string jidprep(const std::string& original)
|
||||
{
|
||||
#ifdef LIBIDN_FOUND
|
||||
using CacheType = std::map<std::string, std::string>;
|
||||
static CacheType cache;
|
||||
std::pair<CacheType::iterator, bool> cached = cache.insert({original, {}});
|
||||
if (std::get<1>(cached) == false)
|
||||
{ // Insertion failed: the result is already in the cache, return it
|
||||
return std::get<0>(cached)->second;
|
||||
}
|
||||
|
||||
const std::string error_msg("Failed to convert " + original + " into a valid JID:");
|
||||
Jid jid(original);
|
||||
|
||||
char local[max_jid_part_len] = {};
|
||||
memcpy(local, jid.local.data(), jid.local.size());
|
||||
Stringprep_rc rc = static_cast<Stringprep_rc>(::stringprep(local, max_jid_part_len,
|
||||
static_cast<Stringprep_profile_flags>(0), stringprep_xmpp_nodeprep));
|
||||
if (rc != STRINGPREP_OK)
|
||||
{
|
||||
log_error(error_msg + stringprep_strerror(rc));
|
||||
return "";
|
||||
}
|
||||
|
||||
char domain[max_jid_part_len] = {};
|
||||
memcpy(domain, jid.domain.data(), jid.domain.size());
|
||||
rc = static_cast<Stringprep_rc>(::stringprep(domain, max_jid_part_len,
|
||||
static_cast<Stringprep_profile_flags>(0), stringprep_nameprep));
|
||||
if (rc != STRINGPREP_OK)
|
||||
{
|
||||
log_error(error_msg + stringprep_strerror(rc));
|
||||
return "";
|
||||
}
|
||||
|
||||
// If there is no resource, stop here
|
||||
if (jid.resource.empty())
|
||||
{
|
||||
std::get<0>(cached)->second = std::string(local) + "@" + domain;
|
||||
return std::get<0>(cached)->second;
|
||||
}
|
||||
|
||||
// Otherwise, also process the resource part
|
||||
char resource[max_jid_part_len] = {};
|
||||
memcpy(resource, jid.resource.data(), jid.resource.size());
|
||||
rc = static_cast<Stringprep_rc>(::stringprep(resource, max_jid_part_len,
|
||||
static_cast<Stringprep_profile_flags>(0), stringprep_xmpp_resourceprep));
|
||||
if (rc != STRINGPREP_OK)
|
||||
{
|
||||
log_error(error_msg + stringprep_strerror(rc));
|
||||
return "";
|
||||
}
|
||||
std::get<0>(cached)->second = std::string(local) + "@" + domain + "/" + resource;
|
||||
return std::get<0>(cached)->second;
|
||||
|
||||
#else
|
||||
(void)original;
|
||||
return "";
|
||||
#endif
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
#ifndef JID_INCLUDED
|
||||
# define JID_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* Parse a JID into its different subart
|
||||
*/
|
||||
class Jid
|
||||
{
|
||||
public:
|
||||
explicit Jid(const std::string& jid);
|
||||
|
||||
std::string domain;
|
||||
std::string local;
|
||||
std::string resource;
|
||||
|
||||
private:
|
||||
Jid(const Jid&) = delete;
|
||||
Jid(Jid&&) = delete;
|
||||
Jid& operator=(const Jid&) = delete;
|
||||
Jid& operator=(Jid&&) = delete;
|
||||
};
|
||||
|
||||
/**
|
||||
* Prepare the given UTF-8 string according to the XMPP node stringprep
|
||||
* identifier profile. This is used to send properly-formed JID to the XMPP
|
||||
* server.
|
||||
*
|
||||
* If the stringprep library is not found, we return an empty string. When
|
||||
* this function is used, the result must always be checked for an empty
|
||||
* value, and if this is the case it must not be used as a JID.
|
||||
*/
|
||||
std::string jidprep(const std::string& original);
|
||||
|
||||
#endif // JID_INCLUDED
|
||||
@@ -1,169 +0,0 @@
|
||||
#include <xmpp/xmpp_parser.hpp>
|
||||
#include <xmpp/xmpp_stanza.hpp>
|
||||
|
||||
#include <logger/logger.hpp>
|
||||
|
||||
/**
|
||||
* Expat handlers. Called by the Expat library, never by ourself.
|
||||
* They just forward the call to the XmppParser corresponding methods.
|
||||
*/
|
||||
|
||||
static void start_element_handler(void* user_data, const XML_Char* name, const XML_Char** atts)
|
||||
{
|
||||
static_cast<XmppParser*>(user_data)->start_element(name, atts);
|
||||
}
|
||||
|
||||
static void end_element_handler(void* user_data, const XML_Char* name)
|
||||
{
|
||||
static_cast<XmppParser*>(user_data)->end_element(name);
|
||||
}
|
||||
|
||||
static void character_data_handler(void *user_data, const XML_Char *s, int len)
|
||||
{
|
||||
static_cast<XmppParser*>(user_data)->char_data(s, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* XmppParser class
|
||||
*/
|
||||
|
||||
XmppParser::XmppParser():
|
||||
level(0),
|
||||
current_node(nullptr)
|
||||
{
|
||||
this->init_xml_parser();
|
||||
}
|
||||
|
||||
void XmppParser::init_xml_parser()
|
||||
{
|
||||
// Create the expat parser
|
||||
this->parser = XML_ParserCreateNS("UTF-8", ':');
|
||||
XML_SetUserData(this->parser, static_cast<void*>(this));
|
||||
|
||||
// Install Expat handlers
|
||||
XML_SetElementHandler(this->parser, &start_element_handler, &end_element_handler);
|
||||
XML_SetCharacterDataHandler(this->parser, &character_data_handler);
|
||||
}
|
||||
|
||||
XmppParser::~XmppParser()
|
||||
{
|
||||
if (this->current_node)
|
||||
delete this->current_node;
|
||||
XML_ParserFree(this->parser);
|
||||
}
|
||||
|
||||
int XmppParser::feed(const char* data, const int len, const bool is_final)
|
||||
{
|
||||
int res = XML_Parse(this->parser, data, len, is_final);
|
||||
if (res == XML_STATUS_ERROR &&
|
||||
(XML_GetErrorCode(this->parser) != XML_ERROR_FINISHED))
|
||||
log_error("Xml_Parse encountered an error: " <<
|
||||
XML_ErrorString(XML_GetErrorCode(this->parser)))
|
||||
return res;
|
||||
}
|
||||
|
||||
int XmppParser::parse(const int len, const bool is_final)
|
||||
{
|
||||
int res = XML_ParseBuffer(this->parser, len, is_final);
|
||||
if (res == XML_STATUS_ERROR)
|
||||
log_error("Xml_Parsebuffer encountered an error: " <<
|
||||
XML_ErrorString(XML_GetErrorCode(this->parser)));
|
||||
return res;
|
||||
}
|
||||
|
||||
void XmppParser::reset()
|
||||
{
|
||||
XML_ParserFree(this->parser);
|
||||
this->init_xml_parser();
|
||||
if (this->current_node)
|
||||
delete this->current_node;
|
||||
this->current_node = nullptr;
|
||||
this->level = 0;
|
||||
}
|
||||
|
||||
void* XmppParser::get_buffer(const size_t size) const
|
||||
{
|
||||
return XML_GetBuffer(this->parser, static_cast<int>(size));
|
||||
}
|
||||
|
||||
void XmppParser::start_element(const XML_Char* name, const XML_Char** attribute)
|
||||
{
|
||||
level++;
|
||||
|
||||
XmlNode* new_node = new XmlNode(name, this->current_node);
|
||||
if (this->current_node)
|
||||
this->current_node->add_child(new_node);
|
||||
this->current_node = new_node;
|
||||
for (size_t i = 0; attribute[i]; i += 2)
|
||||
this->current_node->set_attribute(attribute[i], attribute[i+1]);
|
||||
if (this->level == 1)
|
||||
this->stream_open_event(*this->current_node);
|
||||
}
|
||||
|
||||
void XmppParser::end_element(const XML_Char* name)
|
||||
{
|
||||
(void)name;
|
||||
level--;
|
||||
this->current_node->close();
|
||||
if (level == 1)
|
||||
{
|
||||
this->stanza_event(*this->current_node);
|
||||
}
|
||||
if (level == 0)
|
||||
{
|
||||
this->stream_close_event(*this->current_node);
|
||||
delete this->current_node;
|
||||
this->current_node = nullptr;
|
||||
}
|
||||
else
|
||||
this->current_node = this->current_node->get_parent();
|
||||
if (level == 1)
|
||||
this->current_node->delete_all_children();
|
||||
}
|
||||
|
||||
void XmppParser::char_data(const XML_Char* data, int len)
|
||||
{
|
||||
if (this->current_node->has_children())
|
||||
this->current_node->get_last_child()->add_to_tail(std::string(data, len));
|
||||
else
|
||||
this->current_node->add_to_inner(std::string(data, len));
|
||||
}
|
||||
|
||||
void XmppParser::stanza_event(const Stanza& stanza) const
|
||||
{
|
||||
for (const auto& callback: this->stanza_callbacks)
|
||||
{
|
||||
try {
|
||||
callback(stanza);
|
||||
} catch (const std::exception& e) {
|
||||
log_debug("Unhandled exception: " << e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void XmppParser::stream_open_event(const XmlNode& node) const
|
||||
{
|
||||
for (const auto& callback: this->stream_open_callbacks)
|
||||
callback(node);
|
||||
}
|
||||
|
||||
void XmppParser::stream_close_event(const XmlNode& node) const
|
||||
{
|
||||
for (const auto& callback: this->stream_close_callbacks)
|
||||
callback(node);
|
||||
}
|
||||
|
||||
void XmppParser::add_stanza_callback(std::function<void(const Stanza&)>&& callback)
|
||||
{
|
||||
this->stanza_callbacks.emplace_back(std::move(callback));
|
||||
}
|
||||
|
||||
void XmppParser::add_stream_open_callback(std::function<void(const XmlNode&)>&& callback)
|
||||
{
|
||||
this->stream_open_callbacks.emplace_back(std::move(callback));
|
||||
}
|
||||
|
||||
void XmppParser::add_stream_close_callback(std::function<void(const XmlNode&)>&& callback)
|
||||
{
|
||||
this->stream_close_callbacks.emplace_back(std::move(callback));
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
#ifndef XMPP_PARSER_INCLUDED
|
||||
# define XMPP_PARSER_INCLUDED
|
||||
|
||||
#include <xmpp/xmpp_stanza.hpp>
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <expat.h>
|
||||
|
||||
/**
|
||||
* A SAX XML parser that builds XML nodes and spawns events when a complete
|
||||
* stanza is received (an element of level 2), or when the document is
|
||||
* opened/closed (an element of level 1)
|
||||
*
|
||||
* After a stanza_event has been spawned, we delete the whole stanza. This
|
||||
* means that even with a very long document (in XMPP the document is
|
||||
* potentially infinite), the memory is never exhausted as long as each
|
||||
* stanza is reasonnably short.
|
||||
*
|
||||
* The element names generated by expat contain the namespace of the
|
||||
* element, a colon (':') and then the actual name of the element. To get
|
||||
* an element "x" with a namespace of "http://jabber.org/protocol/muc", you
|
||||
* just look for an XmlNode named "http://jabber.org/protocol/muc:x"
|
||||
*
|
||||
* TODO: enforce the size-limit for the stanza (limit the number of childs
|
||||
* it can contain). For example forbid the parser going further than level
|
||||
* 20 (arbitrary number here), and each XML node to have more than 15 childs
|
||||
* (arbitrary number again).
|
||||
*/
|
||||
class XmppParser
|
||||
{
|
||||
public:
|
||||
explicit XmppParser();
|
||||
~XmppParser();
|
||||
|
||||
public:
|
||||
/**
|
||||
* Init the XML parser and install the callbacks
|
||||
*/
|
||||
void init_xml_parser();
|
||||
/**
|
||||
* Feed the parser with some XML data
|
||||
*/
|
||||
int feed(const char* data, const int len, const bool is_final);
|
||||
/**
|
||||
* Parse the data placed in the parser buffer
|
||||
*/
|
||||
int parse(const int size, const bool is_final);
|
||||
/**
|
||||
* Reset the parser, so it can be used from scratch afterward
|
||||
*/
|
||||
void reset();
|
||||
/**
|
||||
* Get a buffer provided by the xml parser.
|
||||
*/
|
||||
void* get_buffer(const size_t size) const;
|
||||
/**
|
||||
* Add one callback for the various events that this parser can spawn.
|
||||
*/
|
||||
void add_stanza_callback(std::function<void(const Stanza&)>&& callback);
|
||||
void add_stream_open_callback(std::function<void(const XmlNode&)>&& callback);
|
||||
void add_stream_close_callback(std::function<void(const XmlNode&)>&& callback);
|
||||
|
||||
/**
|
||||
* Called when a new XML element has been opened. We instanciate a new
|
||||
* XmlNode and set it as our current node. The parent of this new node is
|
||||
* the previous "current" node. We have all the element's attributes in
|
||||
* this event.
|
||||
*
|
||||
* We spawn a stream_event with this node if this is a level-1 element.
|
||||
*/
|
||||
void start_element(const XML_Char* name, const XML_Char** attribute);
|
||||
/**
|
||||
* Called when an XML element has been closed. We close the current_node,
|
||||
* set our current_node as the parent of the current_node, and if that was
|
||||
* a level-2 element we spawn a stanza_event with this node.
|
||||
*
|
||||
* And we then delete the stanza (and everything under it, its children,
|
||||
* attribute, etc).
|
||||
*/
|
||||
void end_element(const XML_Char* name);
|
||||
/**
|
||||
* Some inner or tail data has been parsed
|
||||
*/
|
||||
void char_data(const XML_Char* data, int len);
|
||||
/**
|
||||
* Calls all the stanza_callbacks one by one.
|
||||
*/
|
||||
void stanza_event(const Stanza& stanza) const;
|
||||
/**
|
||||
* Calls all the stream_open_callbacks one by one. Note: the passed node is not
|
||||
* closed yet.
|
||||
*/
|
||||
void stream_open_event(const XmlNode& node) const;
|
||||
/**
|
||||
* Calls all the stream_close_callbacks one by one.
|
||||
*/
|
||||
void stream_close_event(const XmlNode& node) const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Expat structure.
|
||||
*/
|
||||
XML_Parser parser;
|
||||
/**
|
||||
* The current depth in the XML document
|
||||
*/
|
||||
size_t level;
|
||||
/**
|
||||
* The deepest XML node opened but not yet closed (to which we are adding
|
||||
* new children, inner or tail)
|
||||
*/
|
||||
XmlNode* current_node;
|
||||
/**
|
||||
* A list of callbacks to be called on an *_event, receiving the
|
||||
* concerned Stanza/XmlNode.
|
||||
*/
|
||||
std::vector<std::function<void(const Stanza&)>> stanza_callbacks;
|
||||
std::vector<std::function<void(const XmlNode&)>> stream_open_callbacks;
|
||||
std::vector<std::function<void(const XmlNode&)>> stream_close_callbacks;
|
||||
|
||||
XmppParser(const XmppParser&) = delete;
|
||||
XmppParser& operator=(const XmppParser&) = delete;
|
||||
};
|
||||
|
||||
#endif // XMPP_PARSER_INCLUDED
|
||||
@@ -1,274 +0,0 @@
|
||||
#include <xmpp/xmpp_stanza.hpp>
|
||||
|
||||
#include <utils/encoding.hpp>
|
||||
#include <utils/split.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):
|
||||
parent(parent),
|
||||
closed(false)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
}
|
||||
|
||||
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 std::string& xmlns) const
|
||||
{
|
||||
for (auto& child: this->children)
|
||||
{
|
||||
if (child->name == name && child->get_tag("xmlns") == xmlns)
|
||||
return child;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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::string res("<");
|
||||
res += this->name;
|
||||
for (const auto& it: this->attributes)
|
||||
res += " " + it.first + "='" + sanitize(it.second) + "'";
|
||||
if (this->closed && !this->has_children() && this->inner.empty())
|
||||
res += "/>";
|
||||
else
|
||||
{
|
||||
res += ">" + sanitize(this->inner);
|
||||
for (const auto& child: this->children)
|
||||
res += child->to_string();
|
||||
if (this->closed)
|
||||
{
|
||||
res += "</" + this->get_name() + ">";
|
||||
}
|
||||
}
|
||||
res += sanitize(this->tail);
|
||||
return res;
|
||||
}
|
||||
|
||||
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")));
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
#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),
|
||||
closed(node.closed),
|
||||
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),
|
||||
closed(node.closed),
|
||||
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;
|
||||
/**
|
||||
* Mark this node as closed, nothing else
|
||||
*/
|
||||
void close();
|
||||
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;
|
||||
bool closed;
|
||||
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
|
||||
Reference in New Issue
Block a user