diff options
Diffstat (limited to 'louloulibs/xmpp/xmpp_component.cpp')
-rw-r--r-- | louloulibs/xmpp/xmpp_component.cpp | 672 |
1 files changed, 0 insertions, 672 deletions
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; -} |