diff --git a/src/xmpp/biboumi_component.cpp b/src/xmpp/biboumi_component.cpp index 204927e..8777043 100644 --- a/src/xmpp/biboumi_component.cpp +++ b/src/xmpp/biboumi_component.cpp @@ -576,9 +576,10 @@ void BiboumiComponent::handle_iq(const Stanza& stanza) } else if (iid.type == Iid::Type::Server) { - if (node.empty()) + const std::string expected_node_name = std::string(NODE_NAME) + "#" + get_irc_server_ver(to_str); + if (node.empty() || node == expected_node_name) { - this->send_irc_server_disco_info(id, from, to_str); + this->send_irc_server_disco_info(id, from, to_str, node); stanza_error.disable(); } } @@ -1074,28 +1075,35 @@ void BiboumiComponent::send_self_disco_info(const std::string& id, const std::st this->send_stanza(iq); } -void BiboumiComponent::send_irc_server_disco_info(const std::string& id, const std::string& jid_to, const std::string& jid_from) +void BiboumiComponent::send_irc_server_disco_info(const std::string& id, const std::string& jid_to, + const std::string& jid_from, const std::string& node) +{ + Stanza iq = get_irc_server_disco_info(jid_from, node); + iq["id"] = id; + iq["to"] = jid_to; + iq["from"] = jid_from; + this->send_stanza(iq); +} + +Stanza BiboumiComponent::get_irc_server_disco_info(const std::string& jid_from, const std::string& node) { Jid from(jid_from); Stanza iq("iq"); - { - iq["type"] = "result"; - iq["id"] = id; - iq["to"] = jid_to; - iq["from"] = jid_from; - XmlSubNode query(iq, "query"); - query["xmlns"] = DISCO_INFO_NS; - XmlSubNode identity(query, "identity"); - identity["category"] = "gateway"; - identity["type"] = "irc"; - identity["name"] = "IRC server " + from.local + " over Biboumi"; - for (const char *ns: {DISCO_INFO_NS, ADHOC_NS, PING_NS, VERSION_NS}) - { - XmlSubNode feature(query, "feature"); - feature["var"] = ns; - } - } - this->send_stanza(iq); + iq["type"] = "result"; + XmlSubNode query(iq, "query"); + query["xmlns"] = DISCO_INFO_NS; + if (!node.empty()) + query["node"] = node; + XmlSubNode identity(query, "identity"); + identity["category"] = "gateway"; + identity["type"] = "irc"; + identity["name"] = "IRC server " + from.local + " over Biboumi"; + for (const char *ns: {DISCO_INFO_NS, CAPS_NS, ADHOC_NS, PING_NS, VERSION_NS}) + { + XmlSubNode feature(query, "feature"); + feature["var"] = ns; + } + return iq; } void BiboumiComponent::send_irc_channel_muc_traffic_info(const std::string& id, const std::string& jid_to, const std::string& jid_from) @@ -1303,13 +1311,24 @@ void BiboumiComponent::send_presence_to_contact(const std::string& from, const s if (!id.empty()) presence["id"] = id; - if (type.empty() && from == this->served_hostname) { - XmlSubNode c(presence, "c"); - c["xmlns"] = CAPS_NS; - c["hash"] = "sha-1"; - c["node"] = NODE_NAME; - c["ver"] = this->get_self_ver(); - } + if (type.empty()) + { + Jid from_jid(from); + const Iid from_iid(from_jid.local, {'&', '#', '+', '!'}); + const std::string fixed_irc_server = Config::get("fixed_irc_server", ""); + const bool is_server_jid = !from_jid.local.empty() && from_jid.resource.empty() && + ((fixed_irc_server.empty() && from_iid.type == Iid::Type::Server) || + (!fixed_irc_server.empty() && from_jid.local == fixed_irc_server)); + if (from == this->served_hostname || + (from_jid.domain == this->served_hostname && is_server_jid)) + { + XmlSubNode c(presence, "c"); + c["xmlns"] = CAPS_NS; + c["hash"] = "sha-1"; + c["node"] = NODE_NAME; + c["ver"] = from == this->served_hostname ? this->get_self_ver() : get_irc_server_ver(from); + } + } this->send_stanza(presence); } diff --git a/src/xmpp/biboumi_component.hpp b/src/xmpp/biboumi_component.hpp index 06a5585..03c4a86 100644 --- a/src/xmpp/biboumi_component.hpp +++ b/src/xmpp/biboumi_component.hpp @@ -68,7 +68,8 @@ public: /** * Send a result IQ with the disco informations regarding IRC server JIDs. */ - void send_irc_server_disco_info(const std::string& id, const std::string& jid_to, const std::string& jid_from); + void send_irc_server_disco_info(const std::string& id, const std::string& jid_to, + const std::string& jid_from, const std::string& node); /** * Sends the allowed namespaces in MUC message, according to * http://xmpp.org/extensions/xep-0045.html#impl-service-traffic @@ -93,6 +94,9 @@ private: void send_invitation_from_fulljid(const std::string& room_target, const std::string& jid_to, const std::string& from); static Stanza get_self_disco_info(const std::string &node); static std::string get_self_ver() { return Xmpp::hash_disco_sha1(get_self_disco_info("")); }; + static Stanza get_irc_server_disco_info(const std::string& jid_from, const std::string& node); + static std::string get_irc_server_ver(const std::string& jid_from) + { return Xmpp::hash_disco_sha1(get_irc_server_disco_info(jid_from, "")); } #ifdef CURL_FOUND std::optional handle_iq_gateway(const Stanza *stanza, const Jid *from, const std::string to, const std::string id); #endif diff --git a/tests/end_to_end/scenarios/irc_server_caps.py b/tests/end_to_end/scenarios/irc_server_caps.py new file mode 100644 index 0000000..0642ac7 --- /dev/null +++ b/tests/end_to_end/scenarios/irc_server_caps.py @@ -0,0 +1,23 @@ +from scenarios import * + +scenario = ( + # IRC server JIDs advertise caps for their own disco info + send_stanza(""), + expect_stanza("/iq[@from='{irc_server_one}'][@to='{jid_one}/{resource_one}'][@type='result']/disco_info:query/disco_info:identity[@category='gateway'][@type='irc']", + "/iq/disco_info:query/disco_info:feature[@var='http://jabber.org/protocol/disco#info']", + "/iq/disco_info:query/disco_info:feature[@var='http://jabber.org/protocol/caps']" + ), + send_stanza(""), + sequences.connection(), + scenarios.simple_channel_join.expect_self_join_presence(), + send_stanza(""), + expect_stanza("/presence[@from='{irc_server_one}'][@to='{jid_one}'][@type='subscribed']"), + expect_stanza("/presence[@from='{irc_server_one}'][@to='{jid_one}']/caps:c[@hash='sha-1'][@node][@ver]", + after = ( + save_value("server_ver", extract_attribute("/presence/caps:c[@hash='sha-1'][@node][@ver]", "ver")), + save_value("server_node", extract_attribute("/presence/caps:c[@hash='sha-1'][@node][@ver]", "node")), + ) + ), + send_stanza(""), + expect_stanza("/iq[@from='{irc_server_one}'][@type='result']/disco_info:query[@node='{server_node}#{server_ver}']"), +)