summaryrefslogtreecommitdiff
path: root/src/xmpp/xmpp_component.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/xmpp/xmpp_component.cpp')
-rw-r--r--src/xmpp/xmpp_component.cpp1194
1 files changed, 0 insertions, 1194 deletions
diff --git a/src/xmpp/xmpp_component.cpp b/src/xmpp/xmpp_component.cpp
deleted file mode 100644
index 0e2531d..0000000
--- a/src/xmpp/xmpp_component.cpp
+++ /dev/null
@@ -1,1194 +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 <bridge/list_element.hpp>
-#include <config/config.hpp>
-#include <xmpp/jid.hpp>
-#include <utils/sha1.hpp>
-
-#include <stdexcept>
-#include <iostream>
-
-#include <stdio.h>
-
-#include <config.h>
-
-#include <uuid.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):
- TCPSocketHandler(poller),
- ever_auth(false),
- first_connection_try(true),
- served_hostname(hostname),
- secret(secret),
- authenticated(false),
- doc_open(false),
- 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("presence",
- std::bind(&XmppComponent::handle_presence, this,std::placeholders::_1));
- this->stanza_handlers.emplace("message",
- std::bind(&XmppComponent::handle_message, this,std::placeholders::_1));
- this->stanza_handlers.emplace("iq",
- std::bind(&XmppComponent::handle_iq, this,std::placeholders::_1));
- this->stanza_handlers.emplace("error",
- std::bind(&XmppComponent::handle_error, this,std::placeholders::_1));
-}
-
-XmppComponent::~XmppComponent()
-{
-}
-
-void XmppComponent::start()
-{
- this->connect("localhost", 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;
- XmlNode node("", nullptr);
- node.set_name("stream:stream");
- node["xmlns"] = COMPONENT_NS;
- node["xmlns:stream"] = STREAM_NS;
- node["to"] = this->served_hostname;
- this->send_stanza(node);
- 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::shutdown()
-{
- for (auto it = this->bridges.begin(); it != this->bridges.end(); ++it)
- {
- it->second->shutdown("Gateway shutdown");
- }
-}
-
-void XmppComponent::clean()
-{
- auto it = this->bridges.begin();
- while (it != this->bridges.end())
- {
- it->second->clean();
- if (it->second->active_clients() == 0)
- it = this->bridges.erase(it);
- else
- ++it;
- }
-}
-
-void XmppComponent::on_remote_stream_open(const XmlNode& node)
-{
- log_debug("XMPP DOCUMENT OPEN: " << 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
- char digest[HASH_LENGTH * 2 + 1];
- sha1nfo sha1;
- sha1_init(&sha1);
- sha1_write(&sha1, this->stream_id.data(), this->stream_id.size());
- sha1_write(&sha1, this->secret.data(), this->secret.size());
- const uint8_t* result = sha1_result(&sha1);
- for (int i=0; i < HASH_LENGTH; i++)
- sprintf(digest + (i*2), "%02x", result[i]);
- digest[HASH_LENGTH * 2] = '\0';
-
- Stanza handshake(COMPONENT_NS":handshake");
- handshake.set_inner(digest);
- handshake.close();
- this->send_stanza(handshake);
-}
-
-void XmppComponent::on_remote_stream_close(const XmlNode& node)
-{
- log_debug("XMPP DOCUMENT CLOSE " << 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)
-{
- XmlNode node("stream:error", nullptr);
- XmlNode error(name, nullptr);
- error["xmlns"] = STREAM_NS;
- if (!explanation.empty())
- error.set_inner(explanation);
- error.close();
- node.add_child(std::move(error));
- node.close();
- 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";
- XmlNode error("error");
- error["type"] = error_type;
- XmlNode inner_error(defined_condition);
- inner_error["xmlns"] = STANZA_NS;
- inner_error.close();
- error.add_child(std::move(inner_error));
- if (!text.empty())
- {
- XmlNode text_node("text");
- text_node["xmlns"] = STANZA_NS;
- text_node.set_inner(text);
- text_node.close();
- error.add_child(std::move(text_node));
- }
- error.close();
- node.add_child(std::move(error));
- node.close();
- 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& stanza)
-{
- (void)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
-}
-
-void XmppComponent::handle_presence(const Stanza& stanza)
-{
- std::string from = stanza.get_tag("from");
- std::string id = stanza.get_tag("id");
- std::string to_str = stanza.get_tag("to");
- std::string type = stanza.get_tag("type");
-
- // Check for mandatory tags
- if (from.empty())
- {
- log_warning("Received an invalid presence stanza: tag 'from' is missing.");
- return;
- }
- if (to_str.empty())
- {
- this->send_stanza_error("presence", from, this->served_hostname, id,
- "modify", "bad-request", "Missing 'to' tag");
- return;
- }
-
- Bridge* bridge = this->get_user_bridge(from);
- Jid to(to_str);
- Iid iid(to.local);
-
- // An error stanza is sent whenever we exit this function without
- // disabling this scopeguard. If error_type and error_name are not
- // changed, the error signaled is internal-server-error. Change their
- // value to signal an other kind of error. For example
- // feature-not-implemented, etc. Any non-error process should reach the
- // stanza_error.disable() call at the end of the function.
- std::string error_type("cancel");
- std::string error_name("internal-server-error");
- utils::ScopeGuard stanza_error([&](){
- this->send_stanza_error("presence", from, to_str, id,
- error_type, error_name, "");
- });
-
- if (iid.is_channel && !iid.get_server().empty())
- { // presence toward a MUC that corresponds to an irc channel, or a
- // dummy channel if iid.chan is empty
- if (type.empty())
- {
- const std::string own_nick = bridge->get_own_nick(iid);
- if (!own_nick.empty() && own_nick != to.resource)
- bridge->send_irc_nick_change(iid, to.resource);
- XmlNode* x = stanza.get_child("x", MUC_NS);
- XmlNode* password = x ? x->get_child("password", MUC_NS): nullptr;
- bridge->join_irc_channel(iid, to.resource,
- password ? password->get_inner() : "");
- }
- else if (type == "unavailable")
- {
- XmlNode* status = stanza.get_child("status", COMPONENT_NS);
- bridge->leave_irc_channel(std::move(iid), status ? std::move(status->get_inner()) : "");
- }
- }
- else
- {
- // An user wants to join an invalid IRC channel, return a presence error to him
- if (type.empty())
- this->send_invalid_room_error(to.local, to.resource, from);
- }
- stanza_error.disable();
-}
-
-void XmppComponent::handle_message(const Stanza& stanza)
-{
- std::string from = stanza.get_tag("from");
- std::string id = stanza.get_tag("id");
- std::string to_str = stanza.get_tag("to");
- std::string type = stanza.get_tag("type");
-
- if (from.empty())
- return;
- if (type.empty())
- type = "normal";
- Bridge* bridge = this->get_user_bridge(from);
- Jid to(to_str);
- Iid iid(to.local);
-
- std::string error_type("cancel");
- std::string error_name("internal-server-error");
- utils::ScopeGuard stanza_error([&](){
- this->send_stanza_error("message", from, to_str, id,
- error_type, error_name, "");
- });
- XmlNode* body = stanza.get_child("body", COMPONENT_NS);
- if (type == "groupchat" && iid.is_channel)
- {
- if (body && !body->get_inner().empty())
- {
- bridge->send_channel_message(iid, body->get_inner());
- }
- XmlNode* subject = stanza.get_child("subject", COMPONENT_NS);
- if (subject)
- bridge->set_channel_topic(iid, subject->get_inner());
- }
- else if (type == "error")
- {
- const XmlNode* error = stanza.get_child("error", COMPONENT_NS);
- // Only a set of errors are considered “fatal”. If we encounter one of
- // them, we purge (we disconnect the user from all the IRC servers).
- // We consider this to be true, unless the error condition is
- // specified and is not in the kickable_errors set
- bool kickable_error = true;
- if (error && error->has_children())
- {
- const XmlNode* condition = error->get_last_child();
- if (kickable_errors.find(condition->get_name()) == kickable_errors.end())
- kickable_error = false;
- }
- if (kickable_error)
- bridge->shutdown("Error from remote client");
- }
- else if (type == "chat")
- {
- if (body && !body->get_inner().empty())
- {
- // a message for nick!server
- if (iid.is_user && !iid.get_local().empty())
- {
- bridge->send_private_message(iid, body->get_inner());
- bridge->remove_preferred_from_jid(iid.get_local());
- }
- else if (!iid.is_user && !to.resource.empty())
- { // a message for chan%server@biboumi/Nick or
- // server@biboumi/Nick
- // Convert that into a message to nick!server
- Iid user_iid(utils::tolower(to.resource) + "!" + iid.get_server());
- bridge->send_private_message(user_iid, body->get_inner());
- bridge->set_preferred_from_jid(user_iid.get_local(), to_str);
- }
- }
- }
- else if (iid.is_user)
- this->send_invalid_user_error(to.local, from);
- stanza_error.disable();
-}
-
-// We MUST return an iq, whatever happens, except if the type is
-// "result".
-// To do this, we use a scopeguard. If an exception is raised somewhere, an
-// iq of type error "internal-server-error" is sent. If we handle the
-// request properly (by calling a function that registers an iq to be sent
-// later, or that directly sends an iq), we disable the ScopeGuard. If we
-// reach the end of the function without having disabled the scopeguard, we
-// send a "feature-not-implemented" iq as a result. If an other kind of
-// error is found (for example the feature is implemented in biboumi, but
-// the request is missing some attribute) we can just change the values of
-// error_type and error_name and return from the function (without disabling
-// the scopeguard); an iq error will be sent
-void XmppComponent::handle_iq(const Stanza& stanza)
-{
- std::string id = stanza.get_tag("id");
- std::string from = stanza.get_tag("from");
- std::string to_str = stanza.get_tag("to");
- std::string type = stanza.get_tag("type");
-
- if (from.empty())
- return;
- if (id.empty() || to_str.empty() || type.empty())
- {
- this->send_stanza_error("iq", from, this->served_hostname, id,
- "modify", "bad-request", "");
- return;
- }
-
- Bridge* bridge = this->get_user_bridge(from);
- Jid to(to_str);
-
- // These two values will be used in the error iq sent if we don't disable
- // the scopeguard.
- std::string error_type("cancel");
- std::string error_name("internal-server-error");
- utils::ScopeGuard stanza_error([&](){
- this->send_stanza_error("iq", from, to_str, id,
- error_type, error_name, "");
- });
- if (type == "set")
- {
- XmlNode* query;
- if ((query = stanza.get_child("query", MUC_ADMIN_NS)))
- {
- const XmlNode* child = query->get_child("item", MUC_ADMIN_NS);
- if (child)
- {
- std::string nick = child->get_tag("nick");
- std::string role = child->get_tag("role");
- std::string affiliation = child->get_tag("affiliation");
- if (!nick.empty())
- {
- Iid iid(to.local);
- if (role == "none")
- { // This is a kick
- std::string reason;
- XmlNode* reason_el = child->get_child("reason", MUC_ADMIN_NS);
- if (reason_el)
- reason = reason_el->get_inner();
- bridge->send_irc_kick(iid, nick, reason, id, from);
- }
- else
- bridge->forward_affiliation_role_change(iid, nick, affiliation, role);
- stanza_error.disable();
- }
- }
- }
- else if ((query = stanza.get_child("command", ADHOC_NS)))
- {
- Stanza response("iq");
- response["to"] = from;
- response["from"] = this->served_hostname;
- response["id"] = id;
- XmlNode inner_node = this->adhoc_commands_handler.handle_request(from, *query);
- if (inner_node.get_child("error", ADHOC_NS))
- response["type"] = "error";
- else
- response["type"] = "result";
- response.add_child(std::move(inner_node));
- response.close();
- this->send_stanza(response);
- stanza_error.disable();
- }
- }
- else if (type == "get")
- {
- XmlNode* query;
- if ((query = stanza.get_child("query", DISCO_INFO_NS)))
- { // Disco info
- if (to_str == this->served_hostname)
- {
- const std::string node = query->get_tag("node");
- if (node.empty())
- {
- // On the gateway itself
- this->send_self_disco_info(id, from);
- stanza_error.disable();
- }
- }
- }
- else if ((query = stanza.get_child("query", VERSION_NS)))
- {
- Iid iid(to.local);
- if (iid.is_user ||
- (iid.is_channel && !to.resource.empty()))
- {
- // Get the IRC user version
- std::string target;
- if (iid.is_user)
- target = iid.get_local();
- else
- target = to.resource;
- bridge->send_irc_version_request(iid.get_server(), target, id,
- from, to_str);
- }
- else
- {
- // On the gateway itself or on a channel
- this->send_version(id, from, to_str);
- }
- stanza_error.disable();
- }
- else if ((query = stanza.get_child("query", DISCO_ITEMS_NS)))
- {
- Iid iid(to.local);
- const std::string node = query->get_tag("node");
- if (node == ADHOC_NS)
- {
- this->send_adhoc_commands_list(id, from);
- stanza_error.disable();
- }
- else if (node.empty() && !iid.is_user && !iid.is_channel)
- { // Disco on an IRC server: get the list of channels
- bridge->send_irc_channel_list_request(iid, id, from);
- stanza_error.disable();
- }
- }
- else if ((query = stanza.get_child("ping", PING_NS)))
- {
- Iid iid(to.local);
- if (iid.is_user)
- { // Ping any user (no check on the nick done ourself)
- bridge->send_irc_user_ping_request(iid.get_server(),
- iid.get_local(), id, from, to_str);
- }
- else if (iid.is_channel && !to.resource.empty())
- { // Ping a room participant (we check if the nick is in the room)
- bridge->send_irc_participant_ping_request(iid,
- to.resource, id, from, to_str);
- }
- else
- { // Ping a channel, a server or the gateway itself
- bridge->on_gateway_ping(iid.get_server(),
- id, from, to_str);
- }
- stanza_error.disable();
- }
- }
- else if (type == "result")
- {
- stanza_error.disable();
- XmlNode* query;
- if ((query = stanza.get_child("query", VERSION_NS)))
- {
- XmlNode* name_node = query->get_child("name", VERSION_NS);
- XmlNode* version_node = query->get_child("version", VERSION_NS);
- XmlNode* os_node = query->get_child("os", VERSION_NS);
- std::string name;
- std::string version;
- std::string os;
- if (name_node)
- name = name_node->get_inner() + " (through the biboumi gateway)";
- if (version_node)
- version = version_node->get_inner();
- if (os_node)
- os = os_node->get_inner();
- const Iid iid(to.local);
- bridge->send_xmpp_version_to_irc(iid, name, version, os);
- }
- else
- {
- const auto it = this->waiting_iq.find(id);
- if (it != this->waiting_iq.end())
- {
- it->second(bridge, stanza);
- this->waiting_iq.erase(it);
- }
- }
- }
- error_type = "cancel";
- error_name = "feature-not-implemented";
-}
-
-void XmppComponent::handle_error(const Stanza& stanza)
-{
- 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
-
-}
-
-Bridge* XmppComponent::get_user_bridge(const std::string& user_jid)
-{
- try
- {
- return this->bridges.at(user_jid).get();
- }
- catch (const std::out_of_range& exception)
- {
- this->bridges.emplace(user_jid, std::make_unique<Bridge>(user_jid, this, this->poller));
- return this->bridges.at(user_jid).get();
- }
-}
-
-Bridge* XmppComponent::find_user_bridge(const std::string& user_jid)
-{
- try
- {
- return this->bridges.at(user_jid).get();
- }
- catch (const std::out_of_range& exception)
- {
- return nullptr;
- }
-}
-
-std::list<Bridge*> XmppComponent::get_bridges() const
-{
- std::list<Bridge*> res;
- for (auto it = this->bridges.begin(); it != this->bridges.end(); ++it)
- res.push_back(it->second.get());
- return res;
-}
-
-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)
-{
- XmlNode node("message");
- node["to"] = to;
- if (fulljid)
- node["from"] = from;
- else
- node["from"] = from + "@" + this->served_hostname;
- if (!type.empty())
- node["type"] = type;
- XmlNode body_node("body");
- body_node.set_inner(std::get<0>(body));
- body_node.close();
- node.add_child(std::move(body_node));
- if (std::get<1>(body))
- {
- XmlNode html("html");
- html["xmlns"] = XHTMLIM_NS;
- // Pass the ownership of the pointer to this xmlnode
- html.add_child(std::get<1>(body).release());
- html.close();
- node.add_child(std::move(html));
- }
- node.close();
- this->send_stanza(node);
-}
-
-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)
-{
- XmlNode node("presence");
- node["to"] = to;
- node["from"] = from + "@" + this->served_hostname + "/" + nick;
-
- XmlNode x("x");
- x["xmlns"] = MUC_USER_NS;
-
- XmlNode item("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;
- }
- item.close();
- x.add_child(std::move(item));
-
- if (self)
- {
- XmlNode status("status");
- status["code"] = "110";
- status.close();
- x.add_child(std::move(status));
- }
- x.close();
- node.add_child(std::move(x));
- node.close();
- this->send_stanza(node);
-}
-
-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";
- XmlNode x("x");
- x["xmlns"] = MUC_NS;
- x.close();
- presence.add_child(std::move(x));
- XmlNode error("error");
- error["by"] = muc_name + "@" + this->served_hostname;
- error["type"] = "cancel";
- XmlNode item_not_found("item-not-found");
- item_not_found["xmlns"] = STANZA_NS;
- item_not_found.close();
- error.add_child(std::move(item_not_found));
- XmlNode text("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);
- text.close();
- error.add_child(std::move(text));
- error.close();
- presence.add_child(std::move(error));
- presence.close();
- this->send_stanza(presence);
-}
-
-void XmppComponent::send_invalid_user_error(const std::string& user_name, const std::string& to)
-{
- Stanza message("message");
- message["from"] = user_name + "@" + this->served_hostname;
- message["to"] = to;
- message["type"] = "error";
- XmlNode x("x");
- x["xmlns"] = MUC_NS;
- x.close();
- message.add_child(std::move(x));
- XmlNode error("error");
- error["type"] = "cancel";
- XmlNode item_not_found("item-not-found");
- item_not_found["xmlns"] = STANZA_NS;
- item_not_found.close();
- error.add_child(std::move(item_not_found));
- XmlNode text("text");
- text["xmlns"] = STANZA_NS;
- text["xml:lang"] = "en";
- text.set_inner(user_name +
- " is not a valid IRC user name. A correct user jid is of the form: <nick>!<server>@" +
- this->served_hostname);
- text.close();
- error.add_child(std::move(text));
- error.close();
- message.add_child(std::move(error));
- message.close();
- this->send_stanza(message);
-}
-
-void XmppComponent::send_topic(const std::string& from, Xmpp::body&& topic, const std::string& to)
-{
- XmlNode message("message");
- message["to"] = to;
- message["from"] = from + "@" + this->served_hostname;
- message["type"] = "groupchat";
- XmlNode subject("subject");
- subject.set_inner(std::get<0>(topic));
- subject.close();
- message.add_child(std::move(subject));
- message.close();
- 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";
- XmlNode body("body");
- body.set_inner(std::get<0>(xmpp_body));
- body.close();
- message.add_child(std::move(body));
- if (std::get<1>(xmpp_body))
- {
- XmlNode html("html");
- html["xmlns"] = XHTMLIM_NS;
- // Pass the ownership of the pointer to this xmlnode
- html.add_child(std::get<1>(xmpp_body).release());
- html.close();
- message.add_child(std::move(html));
- }
- message.close();
- 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);
- XmlNode x("x");
- x["xmlns"] = MUC_USER_NS;
- if (self)
- {
- XmlNode status("status");
- status["code"] = "110";
- status.close();
- x.add_child(std::move(status));
- }
- x.close();
- presence.add_child(std::move(x));
- if (!message_str.empty())
- {
- XmlNode status("status");
- status.set_inner(message_str);
- status.close();
- presence.add_child(std::move(status));
- }
- presence.close();
- 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";
- XmlNode x("x");
- x["xmlns"] = MUC_USER_NS;
- XmlNode item("item");
- item["nick"] = new_nick;
- item.close();
- x.add_child(std::move(item));
- XmlNode status("status");
- status["code"] = "303";
- status.close();
- x.add_child(std::move(status));
- if (self)
- {
- XmlNode status2("status");
- status2["code"] = "110";
- status2.close();
- x.add_child(std::move(status2));
- }
- x.close();
- presence.add_child(std::move(x));
- presence.close();
- 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)
-{
- Stanza presence("presence");
- presence["from"] = muc_name + "@" + this->served_hostname + "/" + target;
- presence["to"] = jid_to;
- presence["type"] = "unavailable";
- XmlNode x("x");
- x["xmlns"] = MUC_USER_NS;
- XmlNode item("item");
- item["affiliation"] = "none";
- item["role"] = "none";
- XmlNode actor("actor");
- actor["nick"] = author;
- actor["jid"] = author; // backward compatibility with old clients
- actor.close();
- item.add_child(std::move(actor));
- XmlNode reason("reason");
- reason.set_inner(txt);
- reason.close();
- item.add_child(std::move(reason));
- item.close();
- x.add_child(std::move(item));
- XmlNode status("status");
- status["code"] = "307";
- status.close();
- x.add_child(std::move(status));
- x.close();
- presence.add_child(std::move(x));
- presence.close();
- 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";
- XmlNode x("x");
- x["xmlns"] = MUC_NS;
- x.close();
- presence.add_child(std::move(x));
- XmlNode error("error");
- error["by"] = muc_name + "@" + this->served_hostname;
- error["type"] = type;
- if (!error_code.empty())
- error["code"] = error_code;
- XmlNode subnode(condition);
- subnode["xmlns"] = STANZA_NS;
- subnode.close();
- error.add_child(std::move(subnode));
- error.close();
- presence.add_child(std::move(error));
- presence.close();
- 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;
- XmlNode x("x");
- x["xmlns"] = MUC_USER_NS;
- XmlNode item("item");
- item["affiliation"] = affiliation;
- item["role"] = role;
- item.close();
- x.add_child(std::move(item));
- x.close();
- presence.add_child(std::move(x));
- presence.close();
- this->send_stanza(presence);
-}
-
-void XmppComponent::send_self_disco_info(const std::string& id, const std::string& jid_to)
-{
- Stanza iq("iq");
- iq["type"] = "result";
- iq["id"] = id;
- iq["to"] = jid_to;
- iq["from"] = this->served_hostname;
- XmlNode query("query");
- query["xmlns"] = DISCO_INFO_NS;
- XmlNode identity("identity");
- identity["category"] = "conference";
- identity["type"] = "irc";
- identity["name"] = "Biboumi XMPP-IRC gateway";
- identity.close();
- query.add_child(std::move(identity));
- for (const std::string& ns: {DISCO_INFO_NS, MUC_NS, ADHOC_NS})
- {
- XmlNode feature("feature");
- feature["var"] = ns;
- feature.close();
- query.add_child(std::move(feature));
- }
- query.close();
- iq.add_child(std::move(query));
- iq.close();
- this->send_stanza(iq);
-}
-
-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;
- XmlNode query("query");
- query["xmlns"] = VERSION_NS;
- if (version.empty())
- {
- XmlNode name("name");
- name.set_inner("biboumi");
- name.close();
- query.add_child(std::move(name));
- XmlNode version("version");
- version.set_inner(BIBOUMI_VERSION);
- version.close();
- query.add_child(std::move(version));
- XmlNode os("os");
- os.set_inner(SYSTEM_NAME);
- os.close();
- query.add_child(std::move(os));
- }
- else
- {
- XmlNode name("name");
- name.set_inner(version);
- name.close();
- query.add_child(std::move(name));
- }
- query.close();
- iq.add_child(std::move(query));
- iq.close();
- this->send_stanza(iq);
-}
-
-void XmppComponent::send_adhoc_commands_list(const std::string& id, const std::string& requester_jid)
-{
- Stanza iq("iq");
- iq["type"] = "result";
- iq["id"] = id;
- iq["to"] = requester_jid;
- iq["from"] = this->served_hostname;
- XmlNode query("query");
- query["xmlns"] = DISCO_ITEMS_NS;
- query["node"] = ADHOC_NS;
- for (const auto& kv: this->adhoc_commands_handler.get_commands())
- {
- XmlNode item("item");
- item["jid"] = this->served_hostname;
- item["node"] = kv.first;
- item["name"] = kv.second.name;
- item.close();
- query.add_child(std::move(item));
- }
- query.close();
- iq.add_child(std::move(query));
- iq.close();
- 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;
- XmlNode query("query");
- query["xmlns"] = VERSION_NS;
- query.close();
- iq.add_child(std::move(query));
- iq.close();
- this->send_stanza(iq);
-}
-
-void XmppComponent::send_ping_request(const std::string& from,
- const std::string& jid_to,
- const std::string& id)
-{
- Stanza iq("iq");
- iq["type"] = "get";
- iq["id"] = id;
- iq["from"] = from + "@" + this->served_hostname;
- iq["to"] = jid_to;
- XmlNode ping("ping");
- ping["xmlns"] = PING_NS;
- ping.close();
- iq.add_child(std::move(ping));
- iq.close();
- this->send_stanza(iq);
-
- auto result_cb = [from, id](Bridge* bridge, const Stanza& stanza)
- {
- Jid to(stanza.get_tag("to"));
- if (to.local != from)
- {
- log_error("Received a corresponding ping result, but the 'to' from "
- "the response mismatches the 'from' of the request");
- }
- else
- bridge->send_irc_ping_result(from, id);
- };
- this->waiting_iq[id] = result_cb;
-}
-
-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";
- iq.close();
- this->send_stanza(iq);
-}
-
-void XmppComponent::send_iq_room_list_result(const std::string& id,
- const std::string to_jid,
- const std::string& from,
- const std::vector<ListElement>& rooms_list)
-{
- Stanza iq("iq");
- iq["from"] = from + "@" + this->served_hostname;
- iq["to"] = to_jid;
- iq["id"] = id;
- iq["type"] = "result";
- XmlNode query("query");
- query["xmlns"] = DISCO_ITEMS_NS;
- for (const auto& room: rooms_list)
- {
- XmlNode item("item");
- item["jid"] = room.channel + "%" + from + "@" + this->served_hostname;
- item.close();
- query.add_child(std::move(item));
- }
- query.close();
- iq.add_child(std::move(query));
- iq.close();
- 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;
-}