summaryrefslogtreecommitdiff
path: root/louloulibs/xmpp
diff options
context:
space:
mode:
Diffstat (limited to 'louloulibs/xmpp')
-rw-r--r--louloulibs/xmpp/adhoc_command.cpp80
-rw-r--r--louloulibs/xmpp/adhoc_command.hpp44
-rw-r--r--louloulibs/xmpp/adhoc_commands_handler.cpp111
-rw-r--r--louloulibs/xmpp/adhoc_commands_handler.hpp71
-rw-r--r--louloulibs/xmpp/adhoc_session.cpp35
-rw-r--r--louloulibs/xmpp/adhoc_session.hpp88
-rw-r--r--louloulibs/xmpp/auth.cpp8
-rw-r--r--louloulibs/xmpp/auth.hpp6
-rw-r--r--louloulibs/xmpp/body.hpp12
-rw-r--r--louloulibs/xmpp/jid.cpp153
-rw-r--r--louloulibs/xmpp/jid.hpp49
-rw-r--r--louloulibs/xmpp/xmpp_component.cpp672
-rw-r--r--louloulibs/xmpp/xmpp_component.hpp245
-rw-r--r--louloulibs/xmpp/xmpp_parser.cpp172
-rw-r--r--louloulibs/xmpp/xmpp_parser.hpp133
-rw-r--r--louloulibs/xmpp/xmpp_stanza.cpp229
-rw-r--r--louloulibs/xmpp/xmpp_stanza.hpp160
17 files changed, 0 insertions, 2268 deletions
diff --git a/louloulibs/xmpp/adhoc_command.cpp b/louloulibs/xmpp/adhoc_command.cpp
deleted file mode 100644
index 825cc92..0000000
--- a/louloulibs/xmpp/adhoc_command.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-#include <xmpp/adhoc_command.hpp>
-#include <xmpp/xmpp_component.hpp>
-#include <utils/reload.hpp>
-
-using namespace std::string_literals;
-
-AdhocCommand::AdhocCommand(std::vector<AdhocStep>&& callbacks, const std::string& name, const bool admin_only):
- name(name),
- callbacks(std::move(callbacks)),
- admin_only(admin_only)
-{
-}
-
-bool AdhocCommand::is_admin_only() const
-{
- return this->admin_only;
-}
-
-void PingStep1(XmppComponent&, AdhocSession&, XmlNode& command_node)
-{
- XmlSubNode note(command_node, "note");
- note["type"] = "info";
- note.set_inner("Pong");
-}
-
-void HelloStep1(XmppComponent&, AdhocSession&, XmlNode& command_node)
-{
- XmlSubNode x(command_node, "jabber:x:data:x");
- x["type"] = "form";
- XmlSubNode title(x, "title");
- title.set_inner("Configure your name.");
- XmlSubNode instructions(x, "instructions");
- instructions.set_inner("Please provide your name.");
- XmlSubNode name_field(x, "field");
- name_field["var"] = "name";
- name_field["type"] = "text-single";
- name_field["label"] = "Your name";
- XmlSubNode required(name_field, "required");
-}
-
-void HelloStep2(XmppComponent&, AdhocSession& session, XmlNode& command_node)
-{
- // Find out if the name was provided in the form.
- if (const XmlNode* x = command_node.get_child("x", "jabber:x:data"))
- {
- const XmlNode* name_field = nullptr;
- for (const XmlNode* field: x->get_children("field", "jabber:x:data"))
- if (field->get_tag("var") == "name")
- {
- name_field = field;
- break;
- }
- if (name_field)
- {
- if (const XmlNode* value = name_field->get_child("value", "jabber:x:data"))
- {
- const std::string value_str = value->get_inner();
- command_node.delete_all_children();
- XmlSubNode note(command_node, "note");
- note["type"] = "info";
- note.set_inner("Hello "s + value_str + "!"s);
- return;
- }
- }
- }
- command_node.delete_all_children();
- XmlSubNode error(command_node, ADHOC_NS":error");
- error["type"] = "modify";
- XmlSubNode condition(error, STANZA_NS":bad-request");
- session.terminate();
-}
-
-void Reload(XmppComponent&, AdhocSession&, XmlNode& command_node)
-{
- ::reload_process();
- command_node.delete_all_children();
- XmlSubNode note(command_node, "note");
- note["type"] = "info";
- note.set_inner("Configuration reloaded.");
-}
diff --git a/louloulibs/xmpp/adhoc_command.hpp b/louloulibs/xmpp/adhoc_command.hpp
deleted file mode 100644
index 7c4de47..0000000
--- a/louloulibs/xmpp/adhoc_command.hpp
+++ /dev/null
@@ -1,44 +0,0 @@
-#pragma once
-
-/**
- * Describe an ad-hoc command.
- *
- * Can only have zero or one step for now. When execution is requested, it
- * can return a result immediately, or provide a form to be filled, and
- * provide a result once the filled form is received.
- */
-
-#include <xmpp/adhoc_session.hpp>
-
-#include <functional>
-#include <string>
-
-class AdhocCommand
-{
- friend class AdhocSession;
-public:
- AdhocCommand(std::vector<AdhocStep>&& callback, const std::string& name, const bool admin_only);
- ~AdhocCommand() = default;
- AdhocCommand(const AdhocCommand&) = default;
- AdhocCommand(AdhocCommand&&) = default;
- AdhocCommand& operator=(AdhocCommand&&) = delete;
- AdhocCommand& operator=(const AdhocCommand&) = delete;
-
- const std::string name;
-
- bool is_admin_only() const;
-
-private:
- /**
- * A command may have one or more steps. Each step is a different
- * callback, inserting things into a <command/> XmlNode and calling
- * methods of an AdhocSession.
- */
- std::vector<AdhocStep> callbacks;
- const bool admin_only;
-};
-
-void PingStep1(XmppComponent&, AdhocSession& session, XmlNode& command_node);
-void HelloStep1(XmppComponent&, AdhocSession& session, XmlNode& command_node);
-void HelloStep2(XmppComponent&, AdhocSession& session, XmlNode& command_node);
-void Reload(XmppComponent&, AdhocSession& session, XmlNode& command_node);
diff --git a/louloulibs/xmpp/adhoc_commands_handler.cpp b/louloulibs/xmpp/adhoc_commands_handler.cpp
deleted file mode 100644
index 040d0ff..0000000
--- a/louloulibs/xmpp/adhoc_commands_handler.cpp
+++ /dev/null
@@ -1,111 +0,0 @@
-#include <xmpp/adhoc_commands_handler.hpp>
-#include <xmpp/xmpp_component.hpp>
-
-#include <utils/timed_events.hpp>
-#include <logger/logger.hpp>
-#include <config/config.hpp>
-#include <xmpp/jid.hpp>
-
-#include <iostream>
-
-using namespace std::string_literals;
-
-const std::map<const std::string, const AdhocCommand>& AdhocCommandsHandler::get_commands() const
-{
- return this->commands;
-}
-
-void AdhocCommandsHandler::add_command(std::string name, AdhocCommand command)
-{
- const auto found = this->commands.find(name);
- if (found != this->commands.end())
- throw std::runtime_error("Trying to add an ad-hoc command that already exist: "s + name);
- this->commands.emplace(std::make_pair(std::move(name), std::move(command)));
-}
-
-XmlNode AdhocCommandsHandler::handle_request(const std::string& executor_jid, const std::string& to, XmlNode command_node)
-{
- std::string action = command_node.get_tag("action");
- if (action.empty())
- action = "execute";
- command_node.del_tag("action");
-
- Jid jid(executor_jid);
-
- const std::string node = command_node.get_tag("node");
- auto command_it = this->commands.find(node);
- if (command_it == this->commands.end())
- {
- XmlSubNode error(command_node, ADHOC_NS":error");
- error["type"] = "cancel";
- XmlSubNode condition(error, STANZA_NS":item-not-found");
- }
- else if (command_it->second.is_admin_only() &&
- Config::get("admin", "") != jid.local + "@" + jid.domain)
- {
- XmlSubNode error(command_node, ADHOC_NS":error");
- error["type"] = "cancel";
- XmlSubNode condition(error, STANZA_NS":forbidden");
- }
- else
- {
- std::string sessionid = command_node.get_tag("sessionid");
- if (sessionid.empty())
- { // create a new session, with a new id
- sessionid = XmppComponent::next_id();
- command_node["sessionid"] = sessionid;
- this->sessions.emplace(std::piecewise_construct,
- std::forward_as_tuple(sessionid, executor_jid),
- std::forward_as_tuple(command_it->second, executor_jid, to));
- TimedEventsManager::instance().add_event(TimedEvent(std::chrono::steady_clock::now() + 3600s,
- std::bind(&AdhocCommandsHandler::remove_session, this, sessionid, executor_jid),
- "adhocsession"s + sessionid + executor_jid));
- }
- auto session_it = this->sessions.find(std::make_pair(sessionid, executor_jid));
- if ((session_it != this->sessions.end()) &&
- (action == "execute" || action == "next" || action == "complete"))
- {
- // execute the step
- AdhocSession& session = session_it->second;
- const AdhocStep& step = session.get_next_step();
- step(this->xmpp_component, session, command_node);
- if (session.remaining_steps() == 0 ||
- session.is_terminated())
- {
- this->sessions.erase(session_it);
- command_node["status"] = "completed";
- TimedEventsManager::instance().cancel("adhocsession"s + sessionid + executor_jid);
- }
- else
- {
- command_node["status"] = "executing";
- XmlSubNode actions(command_node, "actions");
- XmlSubNode next(actions, "next");
- }
- }
- else if (action == "cancel")
- {
- this->sessions.erase(session_it);
- command_node["status"] = "canceled";
- TimedEventsManager::instance().cancel("adhocsession"s + sessionid + executor_jid);
- }
- else // unsupported action
- {
- XmlSubNode error(command_node, ADHOC_NS":error");
- error["type"] = "modify";
- XmlSubNode condition(error, STANZA_NS":bad-request");
- }
- }
- return command_node;
-}
-
-void AdhocCommandsHandler::remove_session(const std::string& session_id, const std::string& initiator_jid)
-{
- auto session_it = this->sessions.find(std::make_pair(session_id, initiator_jid));
- if (session_it != this->sessions.end())
- {
- this->sessions.erase(session_it);
- return ;
- }
- log_error("Tried to remove ad-hoc session for [", session_id, ", ", initiator_jid, "] but none found");
-}
diff --git a/louloulibs/xmpp/adhoc_commands_handler.hpp b/louloulibs/xmpp/adhoc_commands_handler.hpp
deleted file mode 100644
index e37d913..0000000
--- a/louloulibs/xmpp/adhoc_commands_handler.hpp
+++ /dev/null
@@ -1,71 +0,0 @@
-#pragma once
-
-/**
- * Manage a list of available AdhocCommands and the list of ongoing
- * AdhocCommandSessions.
- */
-
-#include <xmpp/adhoc_command.hpp>
-#include <xmpp/xmpp_stanza.hpp>
-
-#include <utility>
-#include <string>
-#include <map>
-
-class AdhocCommandsHandler
-{
-public:
- explicit AdhocCommandsHandler(XmppComponent& xmpp_component):
- xmpp_component(xmpp_component),
- commands{}
- { }
- ~AdhocCommandsHandler() = default;
-
- AdhocCommandsHandler(const AdhocCommandsHandler&) = delete;
- AdhocCommandsHandler(AdhocCommandsHandler&&) = delete;
- AdhocCommandsHandler& operator=(const AdhocCommandsHandler&) = delete;
- AdhocCommandsHandler& operator=(AdhocCommandsHandler&&) = delete;
-
- /**
- * Returns the list of available commands.
- */
- const std::map<const std::string, const AdhocCommand>& get_commands() const;
- /**
- * Add a command into the list, associated with the given name
- */
- void add_command(std::string name, AdhocCommand command);
- /**
- * Find the requested command, create a new session or use an existing
- * one, and process the request (provide a new form, an error, or a
- * result).
- *
- * Returns a (moved) XmlNode that will be inserted in the iq response. It
- * should be a <command/> node containing one or more useful children. If
- * it contains an <error/> node, the iq response will have an error type.
- *
- * Takes a copy of the <command/> node so we can actually edit it and use
- * it as our return value.
- */
- XmlNode handle_request(const std::string& executor_jid, const std::string& to, XmlNode command_node);
- /**
- * Remove the session from the list. This is done to avoid filling the
- * memory with waiting session (for example due to a client that starts
- * multi-steps command but never finishes them).
- */
- void remove_session(const std::string& session_id, const std::string& initiator_jid);
-private:
- /**
- * To access basically anything in the gateway.
- */
- XmppComponent& xmpp_component;
- /**
- * The list of all available commands.
- */
- std::map<const std::string, const AdhocCommand> commands;
- /**
- * The list of all currently on-going commands.
- *
- * Of the form: {{session_id, owner_jid}, session}.
- */
- std::map<std::pair<const std::string, const std::string>, AdhocSession> sessions;
-};
diff --git a/louloulibs/xmpp/adhoc_session.cpp b/louloulibs/xmpp/adhoc_session.cpp
deleted file mode 100644
index dda4bea..0000000
--- a/louloulibs/xmpp/adhoc_session.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-#include <xmpp/adhoc_session.hpp>
-#include <xmpp/adhoc_command.hpp>
-
-#include <assert.h>
-
-AdhocSession::AdhocSession(const AdhocCommand& command, const std::string& owner_jid,
- const std::string& to_jid):
- command(command),
- owner_jid(owner_jid),
- to_jid(to_jid),
- current_step(0),
- terminated(false)
-{
-}
-
-const AdhocStep& AdhocSession::get_next_step()
-{
- assert(this->current_step < this->command.callbacks.size());
- return this->command.callbacks[this->current_step++];
-}
-
-size_t AdhocSession::remaining_steps() const
-{
- return this->command.callbacks.size() - this->current_step;
-}
-
-bool AdhocSession::is_terminated() const
-{
- return this->terminated;
-}
-
-void AdhocSession::terminate()
-{
- this->terminated = true;
-}
diff --git a/louloulibs/xmpp/adhoc_session.hpp b/louloulibs/xmpp/adhoc_session.hpp
deleted file mode 100644
index 0de8d13..0000000
--- a/louloulibs/xmpp/adhoc_session.hpp
+++ /dev/null
@@ -1,88 +0,0 @@
-#pragma once
-
-#include <xmpp/xmpp_stanza.hpp>
-
-#include <functional>
-#include <string>
-#include <map>
-
-class XmppComponent;
-
-class AdhocCommand;
-class AdhocSession;
-
-/**
- * A function executed as an ad-hoc command step. It takes a <command/>
- * XmlNode and modifies it accordingly (inserting for example an <error/>
- * node, or a data form…).
- */
-using AdhocStep = std::function<void(XmppComponent&, AdhocSession&, XmlNode&)>;
-
-class AdhocSession
-{
-public:
- explicit AdhocSession(const AdhocCommand& command, const std::string& owner_jid,
- const std::string& to_jid);
- ~AdhocSession() = default;
-
- AdhocSession(const AdhocSession&) = delete;
- AdhocSession(AdhocSession&&) = delete;
- AdhocSession& operator=(const AdhocSession&) = delete;
- AdhocSession& operator=(AdhocSession&&) = delete;
-
- /**
- * Return the function to be executed, found in our AdhocCommand, for the
- * current_step. And increment the current_step.
- */
- const AdhocStep& get_next_step();
- /**
- * Return the number of remaining steps.
- */
- size_t remaining_steps() const;
- /**
- * This may be modified by an AdhocStep, to indicate that this session
- * should no longer exist, because we encountered an error, and we can't
- * execute any more step of it.
- */
- void terminate();
- bool is_terminated() const;
- std::string get_target_jid() const
- {
- return this->to_jid;
- }
- std::string get_owner_jid() const
- {
- return this->owner_jid;
- }
-
-private:
- /**
- * A reference of the command concerned by this session. Used for example
- * to get the next step of that command, things like that.
- */
- const AdhocCommand& command;
- /**
- * The full JID of the XMPP user that created this session by executing
- * the first step of a command. Only that JID must be allowed to access
- * this session.
- */
- const std::string& owner_jid;
- /**
- * The 'to' attribute in the request stanza. This is the target of the current session.
- */
- const std::string& to_jid;
- /**
- * The current step we are at. It starts at zero. It is used to index the
- * associated AdhocCommand::callbacks vector.
- */
- size_t current_step;
- bool terminated;
-
-public:
- /**
- * A map to store various things that we may want to remember between two
- * steps of the same session. A step can insert any value associated to
- * any key in there.
- */
- std::map<std::string, std::string> vars;
-};
diff --git a/louloulibs/xmpp/auth.cpp b/louloulibs/xmpp/auth.cpp
deleted file mode 100644
index 8a34a4e..0000000
--- a/louloulibs/xmpp/auth.cpp
+++ /dev/null
@@ -1,8 +0,0 @@
-#include <xmpp/auth.hpp>
-
-#include <utils/sha1.hpp>
-
-std::string get_handshake_digest(const std::string& stream_id, const std::string& secret)
-{
- return sha1(stream_id + secret);
-}
diff --git a/louloulibs/xmpp/auth.hpp b/louloulibs/xmpp/auth.hpp
deleted file mode 100644
index 34a2116..0000000
--- a/louloulibs/xmpp/auth.hpp
+++ /dev/null
@@ -1,6 +0,0 @@
-#pragma once
-
-#include <string>
-
-std::string get_handshake_digest(const std::string& stream_id, const std::string& secret);
-
diff --git a/louloulibs/xmpp/body.hpp b/louloulibs/xmpp/body.hpp
deleted file mode 100644
index 068d1a4..0000000
--- a/louloulibs/xmpp/body.hpp
+++ /dev/null
@@ -1,12 +0,0 @@
-#pragma once
-
-
-namespace Xmpp
-{
-// Contains:
-// - an XMPP-valid UTF-8 body
-// - an XML node representing the XHTML-IM body, or null
- using body = std::tuple<const std::string, std::unique_ptr<XmlNode>>;
-}
-
-
diff --git a/louloulibs/xmpp/jid.cpp b/louloulibs/xmpp/jid.cpp
deleted file mode 100644
index 46e01ea..0000000
--- a/louloulibs/xmpp/jid.cpp
+++ /dev/null
@@ -1,153 +0,0 @@
-#include <xmpp/jid.hpp>
-#include <algorithm>
-#include <cstring>
-#include <map>
-
-#include <louloulibs.h>
-#ifdef LIBIDN_FOUND
- #include <stringprep.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netdb.h>
- #include <utils/scopeguard.hpp>
- #include <set>
-#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);
-}
-
-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(), std::min(max_jid_part_len, 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(), std::min(max_jid_part_len, jid.domain.size()));
-
- {
- // Using getaddrinfo, check if the domain part is a valid IPv4 (then use
- // it as is), or IPv6 (surround it with []), or a domain name (run
- // nameprep)
- struct addrinfo hints;
- memset(&hints, 0, sizeof(hints));
- hints.ai_flags = AI_NUMERICHOST;
- hints.ai_family = AF_UNSPEC;
-
- struct addrinfo* addr_res = nullptr;
- const auto ret = ::getaddrinfo(domain, nullptr, &hints, &addr_res);
- auto addrinfo_deleter = utils::make_scope_guard([addr_res] { if (addr_res) freeaddrinfo(addr_res); });
- if (ret || !addr_res || (addr_res->ai_family != AF_INET && addr_res->ai_family != AF_INET6))
- { // Not an IP, run nameprep on it
- 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 "";
- }
-
- // Make sure it contains only allowed characters
- using std::begin;
- using std::end;
- char* domain_end = domain + ::strlen(domain);
- std::replace_if(std::begin(domain), domain + ::strlen(domain),
- [](const char c) -> bool
- {
- return !((c >= 'a' && c <= 'z') || c == '-' ||
- (c >= '0' && c <= '9') || c == '.');
- }, '-');
- // Make sure there are no doubled - or .
- std::set<char> special_chars{'-', '.'};
- domain_end = std::unique(begin(domain), domain + ::strlen(domain), [&special_chars](const char& a, const char& b) -> bool
- {
- return special_chars.count(a) && special_chars.count(b);
- });
- // remove leading and trailing -. if any
- if (domain_end != domain && special_chars.count(*(domain_end - 1)))
- --domain_end;
- if (domain_end != domain && special_chars.count(domain[0]))
- {
- std::memmove(domain, domain + 1, domain_end - domain + 1);
- --domain_end;
- }
- // And if the final result is an empty string, return a dummy hostname
- if (domain_end == domain)
- ::strcpy(domain, "empty");
- else
- *domain_end = '\0';
- }
- else if (addr_res->ai_family == AF_INET6)
- { // IPv6, surround it with []. The length is always enough:
- // the longest possible IPv6 is way shorter than max_jid_part_len
- ::memmove(domain + 1, domain, jid.domain.size());
- domain[0] = '[';
- domain[jid.domain.size() + 1] = ']';
- }
- }
-
-
- // 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(), std::min(max_jid_part_len, 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
-}
diff --git a/louloulibs/xmpp/jid.hpp b/louloulibs/xmpp/jid.hpp
deleted file mode 100644
index 85e835c..0000000
--- a/louloulibs/xmpp/jid.hpp
+++ /dev/null
@@ -1,49 +0,0 @@
-#pragma once
-
-
-#include <string>
-
-/**
- * Parse a JID into its different subart
- */
-class Jid
-{
-public:
- explicit Jid(const std::string& jid);
-
- Jid(const Jid&) = delete;
- Jid(Jid&&) = delete;
- Jid& operator=(const Jid&) = delete;
- Jid& operator=(Jid&&) = delete;
-
- std::string domain;
- std::string local;
- std::string resource;
-
- std::string bare() const
- {
- return this->local + "@" + this->domain;
- }
- std::string full() const
- {
- std::string res = this->domain;
- if (!this->local.empty())
- res = this->local + "@" + this->domain;
- if (!this->resource.empty())
- res += "/" + this->resource;
- return res;
- }
-};
-
-/**
- * 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);
-
-
diff --git a/louloulibs/xmpp/xmpp_component.cpp b/louloulibs/xmpp/xmpp_component.cpp
deleted file mode 100644
index 5d98e58..0000000
--- a/louloulibs/xmpp/xmpp_component.cpp
+++ /dev/null
@@ -1,672 +0,0 @@
-#include <utils/timed_events.hpp>
-#include <utils/scopeguard.hpp>
-#include <utils/tolower.hpp>
-#include <logger/logger.hpp>
-
-#include <xmpp/xmpp_component.hpp>
-#include <config/config.hpp>
-#include <utils/system.hpp>
-#include <utils/time.hpp>
-#include <xmpp/auth.hpp>
-#include <xmpp/jid.hpp>
-
-#include <stdexcept>
-#include <iostream>
-#include <set>
-
-#include <uuid/uuid.h>
-
-#include <cstdlib>
-#include <set>
-
-#include <louloulibs.h>
-#ifdef SYSTEMD_FOUND
-# include <systemd/sd-daemon.h>
-#endif
-
-using namespace std::string_literals;
-
-static std::set<std::string> kickable_errors{
- "gone",
- "internal-server-error",
- "item-not-found",
- "jid-malformed",
- "recipient-unavailable",
- "redirect",
- "remote-server-not-found",
- "remote-server-timeout",
- "service-unavailable",
- "malformed-error"
- };
-
-XmppComponent::XmppComponent(std::shared_ptr<Poller>& poller, const std::string& hostname, const std::string& secret):
- TCPClientSocketHandler(poller),
- ever_auth(false),
- first_connection_try(true),
- secret(secret),
- authenticated(false),
- doc_open(false),
- served_hostname(hostname),
- stanza_handlers{},
- adhoc_commands_handler(*this)
-{
- this->parser.add_stream_open_callback(std::bind(&XmppComponent::on_remote_stream_open, this,
- std::placeholders::_1));
- this->parser.add_stanza_callback(std::bind(&XmppComponent::on_stanza, this,
- std::placeholders::_1));
- this->parser.add_stream_close_callback(std::bind(&XmppComponent::on_remote_stream_close, this,
- std::placeholders::_1));
- this->stanza_handlers.emplace("handshake",
- std::bind(&XmppComponent::handle_handshake, this,std::placeholders::_1));
- this->stanza_handlers.emplace("error",
- std::bind(&XmppComponent::handle_error, this,std::placeholders::_1));
-}
-
-void XmppComponent::start()
-{
- this->connect(Config::get("xmpp_server_ip", "127.0.0.1"), Config::get("port", "5347"), false);
-}
-
-bool XmppComponent::is_document_open() const
-{
- return this->doc_open;
-}
-
-void XmppComponent::send_stanza(const Stanza& stanza)
-{
- std::string str = stanza.to_string();
- log_debug("XMPP SENDING: ", str);
- this->send_data(std::move(str));
-}
-
-void XmppComponent::on_connection_failed(const std::string& reason)
-{
- this->first_connection_try = false;
- log_error("Failed to connect to the XMPP server: ", reason);
-#ifdef SYSTEMD_FOUND
- sd_notifyf(0, "STATUS=Failed to connect to the XMPP server: %s", reason.data());
-#endif
-}
-
-void XmppComponent::on_connected()
-{
- log_info("connected to XMPP server");
- this->first_connection_try = true;
- auto data = "<stream:stream to='"s + this->served_hostname + \
- "' xmlns:stream='http://etherx.jabber.org/streams' xmlns='" COMPONENT_NS "'>";
- log_debug("XMPP SENDING: ", data);
- this->send_data(std::move(data));
- this->doc_open = true;
- // We may have some pending data to send: this happens when we try to send
- // some data before we are actually connected. We send that data right now, if any
- this->send_pending_data();
-}
-
-void XmppComponent::on_connection_close(const std::string& error)
-{
- if (error.empty())
- log_info("XMPP server closed connection");
- else
- log_info("XMPP server closed connection: ", error);
-}
-
-void XmppComponent::parse_in_buffer(const size_t size)
-{
- if (!this->in_buf.empty())
- { // This may happen if the parser could not allocate enough space for
- // us. We try to feed it the data that was read into our in_buf
- // instead. If this fails again we are in trouble.
- this->parser.feed(this->in_buf.data(), this->in_buf.size(), false);
- this->in_buf.clear();
- }
- else
- { // Just tell the parser to parse the data that was placed into the
- // buffer it provided to us with GetBuffer
- this->parser.parse(size, false);
- }
-}
-
-void XmppComponent::on_remote_stream_open(const XmlNode& node)
-{
- log_debug("XMPP RECEIVING: ", node.to_string());
- this->stream_id = node.get_tag("id");
- if (this->stream_id.empty())
- {
- log_error("Error: no attribute 'id' found");
- this->send_stream_error("bad-format", "missing 'id' attribute");
- this->close_document();
- return ;
- }
-
- // Try to authenticate
- auto data = "<handshake xmlns='" COMPONENT_NS "'>"s + get_handshake_digest(this->stream_id, this->secret) + "</handshake>";
- log_debug("XMPP SENDING: ", data);
- this->send_data(std::move(data));
-}
-
-void XmppComponent::on_remote_stream_close(const XmlNode& node)
-{
- log_debug("XMPP RECEIVING: ", node.to_string());
- this->doc_open = false;
-}
-
-void XmppComponent::reset()
-{
- this->parser.reset();
-}
-
-void XmppComponent::on_stanza(const Stanza& stanza)
-{
- log_debug("XMPP RECEIVING: ", stanza.to_string());
- std::function<void(const Stanza&)> handler;
- try
- {
- handler = this->stanza_handlers.at(stanza.get_name());
- }
- catch (const std::out_of_range& exception)
- {
- log_warning("No handler for stanza of type ", stanza.get_name());
- return;
- }
- handler(stanza);
-}
-
-void XmppComponent::send_stream_error(const std::string& name, const std::string& explanation)
-{
- Stanza node("stream:error");
- {
- XmlSubNode error(node, name);
- error["xmlns"] = STREAM_NS;
- if (!explanation.empty())
- error.set_inner(explanation);
- }
- this->send_stanza(node);
-}
-
-void XmppComponent::send_stanza_error(const std::string& kind, const std::string& to, const std::string& from,
- const std::string& id, const std::string& error_type,
- const std::string& defined_condition, const std::string& text,
- const bool fulljid)
-{
- Stanza node(kind);
- {
- if (!to.empty())
- node["to"] = to;
- if (!from.empty())
- {
- if (fulljid)
- node["from"] = from;
- else
- node["from"] = from + "@" + this->served_hostname;
- }
- if (!id.empty())
- node["id"] = id;
- node["type"] = "error";
- {
- XmlSubNode error(node, "error");
- error["type"] = error_type;
- {
- XmlSubNode inner_error(error, defined_condition);
- inner_error["xmlns"] = STANZA_NS;
- }
- if (!text.empty())
- {
- XmlSubNode text_node(error, "text");
- text_node["xmlns"] = STANZA_NS;
- text_node.set_inner(text);
- }
- }
- }
- this->send_stanza(node);
-}
-
-void XmppComponent::close_document()
-{
- log_debug("XMPP SENDING: </stream:stream>");
- this->send_data("</stream:stream>");
- this->doc_open = false;
-}
-
-void XmppComponent::handle_handshake(const Stanza&)
-{
- this->authenticated = true;
- this->ever_auth = true;
- log_info("Authenticated with the XMPP server");
-#ifdef SYSTEMD_FOUND
- sd_notify(0, "READY=1");
- // Install an event that sends a keepalive to systemd. If biboumi crashes
- // or hangs for too long, systemd will restart it.
- uint64_t usec;
- if (sd_watchdog_enabled(0, &usec) > 0)
- {
- TimedEventsManager::instance().add_event(TimedEvent(
- std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::microseconds(usec / 2)),
- []() { sd_notify(0, "WATCHDOG=1"); }));
- }
-#endif
- this->after_handshake();
-}
-
-void XmppComponent::handle_error(const Stanza& stanza)
-{
- const XmlNode* text = stanza.get_child("text", STREAMS_NS);
- std::string error_message("Unspecified error");
- if (text)
- error_message = text->get_inner();
- log_error("Stream error received from the XMPP server: ", error_message);
-#ifdef SYSTEMD_FOUND
- if (!this->ever_auth)
- sd_notifyf(0, "STATUS=Failed to authenticate to the XMPP server: %s", error_message.data());
-#endif
-
-}
-
-void* XmppComponent::get_receive_buffer(const size_t size) const
-{
- return this->parser.get_buffer(size);
-}
-
-void XmppComponent::send_message(const std::string& from, Xmpp::body&& body, const std::string& to,
- const std::string& type, const bool fulljid, const bool nocopy)
-{
- Stanza message("message");
- {
- message["to"] = to;
- if (fulljid)
- message["from"] = from;
- else
- message["from"] = from + "@" + this->served_hostname;
- if (!type.empty())
- message["type"] = type;
- XmlSubNode body_node(message, "body");
- body_node.set_inner(std::get<0>(body));
- if (std::get<1>(body))
- {
- XmlSubNode html(message, "html");
- html["xmlns"] = XHTMLIM_NS;
- // Pass the ownership of the pointer to this xmlnode
- html.add_child(std::move(std::get<1>(body)));
- }
- if (nocopy)
- {
- XmlSubNode private_node(message, "private");
- private_node["xmlns"] = "urn:xmpp:carbons:2";
- XmlSubNode nocopy(message, "no-copy");
- nocopy["xmlns"] = "urn:xmpp:hints";
- }
- }
- this->send_stanza(message);
-}
-
-void XmppComponent::send_user_join(const std::string& from,
- const std::string& nick,
- const std::string& realjid,
- const std::string& affiliation,
- const std::string& role,
- const std::string& to,
- const bool self)
-{
- Stanza presence("presence");
- {
- presence["to"] = to;
- presence["from"] = from + "@" + this->served_hostname + "/" + nick;
-
- XmlSubNode x(presence, "x");
- x["xmlns"] = MUC_USER_NS;
-
- XmlSubNode item(x, "item");
- if (!affiliation.empty())
- item["affiliation"] = affiliation;
- if (!role.empty())
- item["role"] = role;
- if (!realjid.empty())
- {
- const std::string preped_jid = jidprep(realjid);
- if (!preped_jid.empty())
- item["jid"] = preped_jid;
- }
-
- if (self)
- {
- XmlSubNode status(x, "status");
- status["code"] = "110";
- }
- }
- this->send_stanza(presence);
-}
-
-void XmppComponent::send_invalid_room_error(const std::string& muc_name,
- const std::string& nick,
- const std::string& to)
-{
- Stanza presence("presence");
- {
- if (!muc_name.empty ())
- presence["from"] = muc_name + "@" + this->served_hostname + "/" + nick;
- else
- presence["from"] = this->served_hostname;
- presence["to"] = to;
- presence["type"] = "error";
- XmlSubNode x(presence, "x");
- x["xmlns"] = MUC_NS;
- XmlSubNode error(presence, "error");
- error["by"] = muc_name + "@" + this->served_hostname;
- error["type"] = "cancel";
- XmlSubNode item_not_found(error, "item-not-found");
- item_not_found["xmlns"] = STANZA_NS;
- XmlSubNode text(error, "text");
- text["xmlns"] = STANZA_NS;
- text["xml:lang"] = "en";
- text.set_inner(muc_name +
- " is not a valid IRC channel name. A correct room jid is of the form: #<chan>%<server>@" +
- this->served_hostname);
- }
- this->send_stanza(presence);
-}
-
-void XmppComponent::send_topic(const std::string& from, Xmpp::body&& topic, const std::string& to, const std::string& who)
-{
- Stanza message("message");
- {
- message["to"] = to;
- if (who.empty())
- message["from"] = from + "@" + this->served_hostname;
- else
- message["from"] = from + "@" + this->served_hostname + "/" + who;
- message["type"] = "groupchat";
- XmlSubNode subject(message, "subject");
- subject.set_inner(std::get<0>(topic));
- }
- this->send_stanza(message);
-}
-
-void XmppComponent::send_muc_message(const std::string& muc_name, const std::string& nick, Xmpp::body&& xmpp_body, const std::string& jid_to)
-{
- Stanza message("message");
- message["to"] = jid_to;
- if (!nick.empty())
- message["from"] = muc_name + "@" + this->served_hostname + "/" + nick;
- else // Message from the room itself
- message["from"] = muc_name + "@" + this->served_hostname;
- message["type"] = "groupchat";
-
- {
- XmlSubNode body(message, "body");
- body.set_inner(std::get<0>(xmpp_body));
- }
-
- if (std::get<1>(xmpp_body))
- {
- XmlSubNode html(message, "html");
- html["xmlns"] = XHTMLIM_NS;
- // Pass the ownership of the pointer to this xmlnode
- html.add_child(std::move(std::get<1>(xmpp_body)));
- }
- this->send_stanza(message);
-}
-
-void XmppComponent::send_history_message(const std::string& muc_name, const std::string& nick, const std::string& body_txt, const std::string& jid_to, std::time_t timestamp)
-{
- Stanza message("message");
- message["to"] = jid_to;
- if (!nick.empty())
- message["from"] = muc_name + "@" + this->served_hostname + "/" + nick;
- else
- message["from"] = muc_name + "@" + this->served_hostname;
- message["type"] = "groupchat";
-
- {
- XmlSubNode body(message, "body");
- body.set_inner(body_txt);
- }
- {
- XmlSubNode delay(message, "delay");
- delay["xmlns"] = DELAY_NS;
- delay["from"] = muc_name + "@" + this->served_hostname;
- delay["stamp"] = utils::to_string(timestamp);
- }
-
- this->send_stanza(message);
-}
-
-void XmppComponent::send_muc_leave(const std::string& muc_name, std::string&& nick, Xmpp::body&& message, const std::string& jid_to, const bool self)
-{
- Stanza presence("presence");
- {
- presence["to"] = jid_to;
- presence["from"] = muc_name + "@" + this->served_hostname + "/" + nick;
- presence["type"] = "unavailable";
- const std::string message_str = std::get<0>(message);
- XmlSubNode x(presence, "x");
- x["xmlns"] = MUC_USER_NS;
- if (self)
- {
- XmlSubNode status(x, "status");
- status["code"] = "110";
- }
- if (!message_str.empty())
- {
- XmlSubNode status(presence, "status");
- status.set_inner(message_str);
- }
- }
- this->send_stanza(presence);
-}
-
-void XmppComponent::send_nick_change(const std::string& muc_name,
- const std::string& old_nick,
- const std::string& new_nick,
- const std::string& affiliation,
- const std::string& role,
- const std::string& jid_to,
- const bool self)
-{
- Stanza presence("presence");
- {
- presence["to"] = jid_to;
- presence["from"] = muc_name + "@" + this->served_hostname + "/" + old_nick;
- presence["type"] = "unavailable";
- XmlSubNode x(presence, "x");
- x["xmlns"] = MUC_USER_NS;
- XmlSubNode item(x, "item");
- item["nick"] = new_nick;
- XmlSubNode status(x, "status");
- status["code"] = "303";
- if (self)
- {
- XmlSubNode status(x, "status");
- status["code"] = "110";
- }
- }
- this->send_stanza(presence);
-
- this->send_user_join(muc_name, new_nick, "", affiliation, role, jid_to, self);
-}
-
-void XmppComponent::kick_user(const std::string& muc_name, const std::string& target, const std::string& txt,
- const std::string& author, const std::string& jid_to, const bool self)
-{
- Stanza presence("presence");
- {
- presence["from"] = muc_name + "@" + this->served_hostname + "/" + target;
- presence["to"] = jid_to;
- presence["type"] = "unavailable";
- XmlSubNode x(presence, "x");
- x["xmlns"] = MUC_USER_NS;
- XmlSubNode item(x, "item");
- item["affiliation"] = "none";
- item["role"] = "none";
- XmlSubNode actor(item, "actor");
- actor["nick"] = author;
- actor["jid"] = author; // backward compatibility with old clients
- XmlSubNode reason(item, "reason");
- reason.set_inner(txt);
- XmlSubNode status(x, "status");
- status["code"] = "307";
- if (self)
- {
- XmlSubNode status(x, "status");
- status["code"] = "110";
- }
- }
- this->send_stanza(presence);
-}
-
-void XmppComponent::send_presence_error(const std::string& muc_name,
- const std::string& nickname,
- const std::string& jid_to,
- const std::string& type,
- const std::string& condition,
- const std::string& error_code,
- const std::string& text)
-{
- Stanza presence("presence");
- {
- presence["from"] = muc_name + "@" + this->served_hostname + "/" + nickname;
- presence["to"] = jid_to;
- presence["type"] = "error";
- XmlSubNode x(presence, "x");
- x["xmlns"] = MUC_NS;
- XmlSubNode error(presence, "error");
- error["by"] = muc_name + "@" + this->served_hostname;
- error["type"] = type;
- if (!text.empty())
- {
- XmlSubNode text_node(error, "text");
- text_node["xmlns"] = STANZA_NS;
- text_node.set_inner(text);
- }
- if (!error_code.empty())
- error["code"] = error_code;
- XmlSubNode subnode(error, condition);
- subnode["xmlns"] = STANZA_NS;
- }
- this->send_stanza(presence);
-}
-
-void XmppComponent::send_affiliation_role_change(const std::string& muc_name,
- const std::string& target,
- const std::string& affiliation,
- const std::string& role,
- const std::string& jid_to)
-{
- Stanza presence("presence");
- {
- presence["from"] = muc_name + "@" + this->served_hostname + "/" + target;
- presence["to"] = jid_to;
- XmlSubNode x(presence, "x");
- x["xmlns"] = MUC_USER_NS;
- XmlSubNode item(x, "item");
- item["affiliation"] = affiliation;
- item["role"] = role;
- }
- this->send_stanza(presence);
-}
-
-void XmppComponent::send_version(const std::string& id, const std::string& jid_to, const std::string& jid_from,
- const std::string& version)
-{
- Stanza iq("iq");
- iq["type"] = "result";
- iq["id"] = id;
- iq["to"] = jid_to;
- iq["from"] = jid_from;
- {
- XmlSubNode query(iq, "query");
- query["xmlns"] = VERSION_NS;
- if (version.empty())
- {
- {
- XmlSubNode name(query, "name");
- name.set_inner("biboumi");
- }
- {
- XmlSubNode version(query, "version");
- version.set_inner(SOFTWARE_VERSION);
- }
- {
- XmlSubNode os(query, "os");
- os.set_inner(utils::get_system_name());
- }
- }
- else
- {
- XmlSubNode name(query, "name");
- name.set_inner(version);
- }
- }
- this->send_stanza(iq);
-}
-
-void XmppComponent::send_adhoc_commands_list(const std::string& id, const std::string& requester_jid,
- const std::string& from_jid,
- const bool with_admin_only, const AdhocCommandsHandler& adhoc_handler)
-{
- Stanza iq("iq");
- {
- iq["type"] = "result";
- iq["id"] = id;
- iq["to"] = requester_jid;
- iq["from"] = from_jid;
- XmlSubNode query(iq, "query");
- query["xmlns"] = DISCO_ITEMS_NS;
- query["node"] = ADHOC_NS;
- for (const auto &kv: adhoc_handler.get_commands())
- {
- if (kv.second.is_admin_only() && !with_admin_only)
- continue;
- XmlSubNode item(query, "item");
- item["jid"] = from_jid;
- item["node"] = kv.first;
- item["name"] = kv.second.name;
- }
- }
- this->send_stanza(iq);
-}
-
-void XmppComponent::send_iq_version_request(const std::string& from,
- const std::string& jid_to)
-{
- Stanza iq("iq");
- {
- iq["type"] = "get";
- iq["id"] = "version_"s + XmppComponent::next_id();
- iq["from"] = from + "@" + this->served_hostname;
- iq["to"] = jid_to;
- XmlSubNode query(iq, "query");
- query["xmlns"] = VERSION_NS;
- }
- this->send_stanza(iq);
-}
-
-void XmppComponent::send_iq_result_full_jid(const std::string& id, const std::string& to_jid, const std::string& from_full_jid)
-{
- Stanza iq("iq");
- iq["from"] = from_full_jid;
- iq["to"] = to_jid;
- iq["id"] = id;
- iq["type"] = "result";
- this->send_stanza(iq);
-}
-
-void XmppComponent::send_iq_result(const std::string& id, const std::string& to_jid, const std::string& from_local_part)
-{
- Stanza iq("iq");
- if (!from_local_part.empty())
- iq["from"] = from_local_part + "@" + this->served_hostname;
- else
- iq["from"] = this->served_hostname;
- iq["to"] = to_jid;
- iq["id"] = id;
- iq["type"] = "result";
- this->send_stanza(iq);
-}
-
-std::string XmppComponent::next_id()
-{
- char uuid_str[37];
- uuid_t uuid;
- uuid_generate(uuid);
- uuid_unparse(uuid, uuid_str);
- return uuid_str;
-}
diff --git a/louloulibs/xmpp/xmpp_component.hpp b/louloulibs/xmpp/xmpp_component.hpp
deleted file mode 100644
index 16d7480..0000000
--- a/louloulibs/xmpp/xmpp_component.hpp
+++ /dev/null
@@ -1,245 +0,0 @@
-#pragma once
-
-
-#include <xmpp/adhoc_commands_handler.hpp>
-#include <network/tcp_client_socket_handler.hpp>
-#include <xmpp/xmpp_parser.hpp>
-#include <xmpp/body.hpp>
-
-#include <unordered_map>
-#include <memory>
-#include <string>
-#include <ctime>
-#include <map>
-
-#define STREAM_NS "http://etherx.jabber.org/streams"
-#define COMPONENT_NS "jabber:component:accept"
-#define MUC_NS "http://jabber.org/protocol/muc"
-#define MUC_USER_NS MUC_NS"#user"
-#define MUC_ADMIN_NS MUC_NS"#admin"
-#define DISCO_NS "http://jabber.org/protocol/disco"
-#define DISCO_ITEMS_NS DISCO_NS"#items"
-#define DISCO_INFO_NS DISCO_NS"#info"
-#define XHTMLIM_NS "http://jabber.org/protocol/xhtml-im"
-#define STANZA_NS "urn:ietf:params:xml:ns:xmpp-stanzas"
-#define STREAMS_NS "urn:ietf:params:xml:ns:xmpp-streams"
-#define VERSION_NS "jabber:iq:version"
-#define ADHOC_NS "http://jabber.org/protocol/commands"
-#define PING_NS "urn:xmpp:ping"
-#define DELAY_NS "urn:xmpp:delay"
-#define MAM_NS "urn:xmpp:mam:1"
-#define FORWARD_NS "urn:xmpp:forward:0"
-#define CLIENT_NS "jabber:client"
-#define DATAFORM_NS "jabber:x:data"
-#define RSM_NS "http://jabber.org/protocol/rsm"
-#define MUC_TRAFFIC_NS "http://jabber.org/protocol/muc#traffic"
-
-/**
- * An XMPP component, communicating with an XMPP server using the protocole
- * described in XEP-0114: Jabber Component Protocol
- *
- * TODO: implement XEP-0225: Component Connections
- */
-class XmppComponent: public TCPClientSocketHandler
-{
-public:
- explicit XmppComponent(std::shared_ptr<Poller>& poller, const std::string& hostname, const std::string& secret);
- virtual ~XmppComponent() = default;
-
- XmppComponent(const XmppComponent&) = delete;
- XmppComponent(XmppComponent&&) = delete;
- XmppComponent& operator=(const XmppComponent&) = delete;
- XmppComponent& operator=(XmppComponent&&) = delete;
-
- void on_connection_failed(const std::string& reason) override final;
- void on_connected() override final;
- void on_connection_close(const std::string& error) override final;
- void parse_in_buffer(const size_t size) override final;
-
- /**
- * Returns a unique id, to be used in the 'id' element of our iq stanzas.
- */
- static std::string next_id();
- bool is_document_open() const;
- /**
- * Connect to the XMPP server.
- */
- void start();
- /**
- * Reset the component so we can use the component on a new XMPP stream
- */
- void reset();
- /**
- * Serialize the stanza and add it to the out_buf to be sent to the
- * server.
- */
- void send_stanza(const Stanza& stanza);
- /**
- * Handle the opening of the remote stream
- */
- void on_remote_stream_open(const XmlNode& node);
- /**
- * Handle the closing of the remote stream
- */
- void on_remote_stream_close(const XmlNode& node);
- /**
- * Handle received stanzas
- */
- void on_stanza(const Stanza& stanza);
- /**
- * Send an error stanza. Message being the name of the element inside the
- * stanza, and explanation being a short human-readable sentence
- * describing the error.
- */
- void send_stream_error(const std::string& message, const std::string& explanation);
- /**
- * Send error stanza, described in http://xmpp.org/rfcs/rfc6120.html#stanzas-error
- */
- void send_stanza_error(const std::string& kind, const std::string& to, const std::string& from,
- const std::string& id, const std::string& error_type,
- const std::string& defined_condition, const std::string& text,
- const bool fulljid=true);
- /**
- * Send the closing signal for our document (not closing the connection though).
- */
- void close_document();
- /**
- * Send a message from from@served_hostname, with the given body
- *
- * If fulljid is false, the provided 'from' doesn't contain the
- * server-part of the JID and must be added.
- */
- void send_message(const std::string& from, Xmpp::body&& body, const std::string& to,
- const std::string& type, const bool fulljid, const bool nocopy=false);
- /**
- * Send a join from a new participant
- */
- void send_user_join(const std::string& from,
- const std::string& nick,
- const std::string& realjid,
- const std::string& affiliation,
- const std::string& role,
- const std::string& to,
- const bool self);
- /**
- * Send an error to indicate that the user tried to join an invalid room
- */
- void send_invalid_room_error(const std::string& muc_jid,
- const std::string& nick,
- const std::string& to);
- /**
- * Send the MUC topic to the user
- */
- void send_topic(const std::string& from, Xmpp::body&& xmpp_topic, const std::string& to, const std::string& who);
- /**
- * Send a (non-private) message to the MUC
- */
- void send_muc_message(const std::string& muc_name, const std::string& nick, Xmpp::body&& body, const std::string& jid_to);
- /**
- * Send a message, with a <delay/> element, part of a MUC history
- */
- void send_history_message(const std::string& muc_name, const std::string& nick, const std::string& body,
- const std::string& jid_to, const std::time_t timestamp);
- /**
- * Send an unavailable presence for this nick
- */
- void send_muc_leave(const std::string& muc_name, std::string&& nick, Xmpp::body&& message, const std::string& jid_to, const bool self);
- /**
- * Indicate that a participant changed his nick
- */
- void send_nick_change(const std::string& muc_name,
- const std::string& old_nick,
- const std::string& new_nick,
- const std::string& affiliation,
- const std::string& role,
- const std::string& jid_to,
- const bool self);
- /**
- * An user is kicked from a room
- */
- void kick_user(const std::string& muc_name, const std::string& target, const std::string& reason,
- const std::string& author, const std::string& jid_to, const bool self);
- /**
- * Send a generic presence error
- */
- void send_presence_error(const std::string& muc_name,
- const std::string& nickname,
- const std::string& jid_to,
- const std::string& type,
- const std::string& condition,
- const std::string& error_code,
- const std::string& text);
- /**
- * Send a presence from the MUC indicating a change in the role and/or
- * affiliation of a participant
- */
- void send_affiliation_role_change(const std::string& muc_name,
- const std::string& target,
- const std::string& affiliation,
- const std::string& role,
- const std::string& jid_to);
- /**
- * Send a result IQ with the given version, or the gateway version if the
- * passed string is empty.
- */
- void send_version(const std::string& id, const std::string& jid_to, const std::string& jid_from,
- const std::string& version="");
- /**
- * Send the list of all available ad-hoc commands to that JID. The list is
- * different depending on what JID made the request.
- */
- void send_adhoc_commands_list(const std::string& id, const std::string& requester_jid, const std::string& from_jid,
- const bool with_admin_only, const AdhocCommandsHandler& adhoc_handler);
- /**
- * Send an iq version request
- */
- void send_iq_version_request(const std::string& from,
- const std::string& jid_to);
- /**
- * Send an empty iq of type result
- */
- void send_iq_result(const std::string& id, const std::string& to_jid, const std::string& from);
- void send_iq_result_full_jid(const std::string& id, const std::string& to_jid,
- const std::string& from_full_jid);
-
- void handle_handshake(const Stanza& stanza);
- void handle_error(const Stanza& stanza);
-
- virtual void after_handshake() {}
-
- const std::string& get_served_hostname() const
- { return this->served_hostname; }
-
- /**
- * Whether or not we ever succeeded our authentication to the XMPP server
- */
- bool ever_auth;
- /**
- * Whether or not this is the first consecutive try on connecting to the
- * XMPP server. We use this to delay the connection attempt for a few
- * seconds, if it is not the first try.
- */
- bool first_connection_try;
-
-private:
- /**
- * Return a buffer provided by the XML parser, to read data directly into
- * it, and avoiding some unnecessary copy.
- */
- void* get_receive_buffer(const size_t size) const override final;
- XmppParser parser;
- std::string stream_id;
- std::string secret;
- bool authenticated;
- /**
- * Whether or not OUR XMPP document is open
- */
- bool doc_open;
-protected:
- std::string served_hostname;
-
- std::unordered_map<std::string, std::function<void(const Stanza&)>> stanza_handlers;
- AdhocCommandsHandler adhoc_commands_handler;
-};
-
-
diff --git a/louloulibs/xmpp/xmpp_parser.cpp b/louloulibs/xmpp/xmpp_parser.cpp
deleted file mode 100644
index 0488be9..0000000
--- a/louloulibs/xmpp/xmpp_parser.cpp
+++ /dev/null
@@ -1,172 +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),
- root(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()
-{
- 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();
- this->current_node = nullptr;
- this->root.reset(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)
-{
- this->level++;
-
- auto new_node = std::make_unique<XmlNode>(name, this->current_node);
- auto new_node_ptr = new_node.get();
- if (this->current_node)
- this->current_node->add_child(std::move(new_node));
- else
- this->root = std::move(new_node);
- this->current_node = new_node_ptr;
- 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*)
-{
- this->level--;
- if (this->level == 0)
- { // End of the whole stream
- this->stream_close_event(*this->current_node);
- this->current_node = nullptr;
- this->root.reset();
- }
- else
- {
- auto parent = this->current_node->get_parent();
- if (this->level == 1)
- { // End of a stanza
- this->stanza_event(*this->current_node);
- // Note: deleting all the children of our parent deletes ourself,
- // so current_node is an invalid pointer after this line
- parent->delete_all_children();
- }
- this->current_node = parent;
- }
-}
-
-void XmppParser::char_data(const XML_Char* data, const size_t len)
-{
- if (this->current_node->has_children())
- this->current_node->get_last_child()->add_to_tail({data, len});
- else
- this->current_node->add_to_inner({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_error("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));
-}
diff --git a/louloulibs/xmpp/xmpp_parser.hpp b/louloulibs/xmpp/xmpp_parser.hpp
deleted file mode 100644
index 9d67228..0000000
--- a/louloulibs/xmpp/xmpp_parser.hpp
+++ /dev/null
@@ -1,133 +0,0 @@
-#pragma once
-
-
-#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();
- XmppParser(const XmppParser&) = delete;
- XmppParser& operator=(const XmppParser&) = delete;
- XmppParser(XmppParser&&) = delete;
- XmppParser& operator=(XmppParser&&) = delete;
-
-public:
- /**
- * 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, const size_t 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:
- /**
- * Init the XML parser and install the callbacks
- */
- void init_xml_parser();
-
- /**
- * 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;
- /**
- * The root node has no parent, so we keep it here: the XmppParser object
- * is its owner.
- */
- std::unique_ptr<XmlNode> root;
- /**
- * 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;
-};
-
-
diff --git a/louloulibs/xmpp/xmpp_stanza.cpp b/louloulibs/xmpp/xmpp_stanza.cpp
deleted file mode 100644
index ac6ce9b..0000000
--- a/louloulibs/xmpp/xmpp_stanza.cpp
+++ /dev/null
@@ -1,229 +0,0 @@
-#include <xmpp/xmpp_stanza.hpp>
-
-#include <utils/encoding.hpp>
-#include <utils/split.hpp>
-
-#include <stdexcept>
-#include <iostream>
-#include <sstream>
-
-#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 += "&amp;";
- break;
- case '<':
- res += "&lt;";
- break;
- case '>':
- res += "&gt;";
- break;
- case '\"':
- res += "&quot;";
- break;
- case '\'':
- res += "&apos;";
- break;
- default:
- res += data[pos];
- break;
- }
- }
- return res;
-}
-
-std::string sanitize(const std::string& data, const std::string& encoding)
-{
- 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, encoding.data())));
-}
-
-XmlNode::XmlNode(const std::string& name, XmlNode* parent):
- parent(parent)
-{
- // 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)
-{
-}
-
-void XmlNode::delete_all_children()
-{
- 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 = data;
-}
-
-void XmlNode::add_to_tail(const std::string& data)
-{
- this->tail += data;
-}
-
-void XmlNode::set_inner(const std::string& data)
-{
- this->inner = data;
-}
-
-void XmlNode::add_to_inner(const std::string& data)
-{
- this->inner += data;
-}
-
-std::string XmlNode::get_inner() const
-{
- return this->inner;
-}
-
-std::string XmlNode::get_tail() const
-{
- return this->tail;
-}
-
-const XmlNode* XmlNode::get_child(const std::string& name, const std::string& xmlns) const
-{
- for (const auto& child: this->children)
- {
- if (child->name == name && child->get_tag("xmlns") == xmlns)
- return child.get();
- }
- return nullptr;
-}
-
-std::vector<const XmlNode*> XmlNode::get_children(const std::string& name, const std::string& xmlns) const
-{
- std::vector<const XmlNode*> res;
- for (const auto& child: this->children)
- {
- if (child->name == name && child->get_tag("xmlns") == xmlns)
- res.push_back(child.get());
- }
- return res;
-}
-
-XmlNode* XmlNode::add_child(std::unique_ptr<XmlNode> child)
-{
- child->parent = this;
- auto ret = child.get();
- this->children.push_back(std::move(child));
- return ret;
-}
-
-XmlNode* XmlNode::add_child(XmlNode&& child)
-{
- auto new_node = std::make_unique<XmlNode>(std::move(child));
- return this->add_child(std::move(new_node));
-}
-
-XmlNode* XmlNode::add_child(const XmlNode& child)
-{
- auto new_node = std::make_unique<XmlNode>(child);
- return this->add_child(std::move(new_node));
-}
-
-XmlNode* XmlNode::get_last_child() const
-{
- return this->children.back().get();
-}
-
-XmlNode* XmlNode::get_parent() const
-{
- return this->parent;
-}
-
-void XmlNode::set_name(const std::string& name)
-{
- this->name = name;
-}
-
-void XmlNode::set_name(std::string&& name)
-{
- this->name = std::move(name);
-}
-
-const std::string XmlNode::get_name() const
-{
- return this->name;
-}
-
-std::string XmlNode::to_string() const
-{
- std::ostringstream res;
- res << "<" << this->name;
- for (const auto& it: this->attributes)
- res << " " << it.first << "='" << sanitize(it.second) + "'";
- if (!this->has_children() && this->inner.empty())
- res << "/>";
- else
- {
- res << ">" + sanitize(this->inner);
- for (const auto& child: this->children)
- res << child->to_string();
- res << "</" << this->get_name() << ">";
- }
- res << sanitize(this->tail);
- return res.str();
-}
-
-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)
- {
- static const std::string def{};
- return def;
- }
-}
-
-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::ostream& operator<<(std::ostream& os, const XmlNode& node)
-{
- return os << node.to_string();
-}
diff --git a/louloulibs/xmpp/xmpp_stanza.hpp b/louloulibs/xmpp/xmpp_stanza.hpp
deleted file mode 100644
index f4b3948..0000000
--- a/louloulibs/xmpp/xmpp_stanza.hpp
+++ /dev/null
@@ -1,160 +0,0 @@
-#pragma once
-
-
-#include <map>
-#include <string>
-#include <vector>
-#include <memory>
-
-std::string xml_escape(const std::string& data);
-std::string xml_unescape(const std::string& data);
-std::string sanitize(const std::string& data, const std::string& encoding = "ISO-8859-1");
-
-/**
- * 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);
- /**
- * The copy constructor does 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 (const auto& child: node.children)
- this->add_child(std::make_unique<XmlNode>(*child));
- }
-
- XmlNode(XmlNode&& node) = default;
- XmlNode& operator=(const XmlNode&) = delete;
- XmlNode& operator=(XmlNode&&) = delete;
-
- ~XmlNode() = default;
-
- 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
- */
- const 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<const 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(std::unique_ptr<XmlNode> child);
- XmlNode* add_child(XmlNode&& child);
- XmlNode* add_child(const 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);
- void set_name(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::map<std::string, std::string> attributes;
- std::vector<std::unique_ptr<XmlNode>> children;
- std::string inner;
- std::string tail;
-};
-
-std::ostream& operator<<(std::ostream& os, const XmlNode& node);
-
-/**
- * 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)
- */
-using Stanza = XmlNode;
-
-class XmlSubNode: public XmlNode
-{
-public:
- XmlSubNode(XmlNode& parent_ref, const std::string& name):
- XmlNode(name),
- parent_to_add(parent_ref)
- {}
-
- ~XmlSubNode()
- {
- this->parent_to_add.add_child(std::move(*this));
- }
-private:
- XmlNode& parent_to_add;
-}; \ No newline at end of file