diff options
-rw-r--r-- | CMakeLists.txt | 8 | ||||
-rw-r--r-- | src/bridge/bridge.cpp | 38 | ||||
-rw-r--r-- | src/bridge/bridge.hpp | 2 | ||||
-rw-r--r-- | src/bridge/list_element.hpp | 19 | ||||
-rw-r--r-- | src/irc/irc_client.cpp | 5 | ||||
-rw-r--r-- | src/irc/irc_client.hpp | 4 | ||||
-rw-r--r-- | src/logger/logger.hpp | 2 | ||||
-rw-r--r-- | src/xmpp/adhoc_command.cpp | 2 | ||||
-rw-r--r-- | src/xmpp/xmpp_component.cpp | 40 | ||||
-rw-r--r-- | src/xmpp/xmpp_component.hpp | 8 | ||||
-rw-r--r-- | src/xmpp/xmpp_stanza.cpp | 15 | ||||
-rw-r--r-- | src/xmpp/xmpp_stanza.hpp | 1 |
12 files changed, 130 insertions, 14 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 6272811..df634b5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,8 +82,8 @@ if(LIBIDN_FOUND) include_directories(${LIBIDN_INCLUDE_DIRS}) endif() -if(SYSTEMDDAEMON_FOUND) - include_directories(${SYSTEMDDAEMON_INCLUDE_DIRS}) +if(SYSTEMD_FOUND) + include_directories(${SYSTEMD_INCLUDE_DIRS}) endif() if(BOTAN_FOUND) @@ -198,8 +198,8 @@ target_link_libraries(${PROJECT_NAME} bridge utils config) -if(SYSTEMDDAEMON_FOUND) - target_link_libraries(xmpp ${SYSTEMDDAEMON_LIBRARIES}) +if(SYSTEMD_FOUND) + target_link_libraries(xmpp ${SYSTEMD_LIBRARIES}) endif() diff --git a/src/bridge/bridge.cpp b/src/bridge/bridge.cpp index e312345..1a205bd 100644 --- a/src/bridge/bridge.cpp +++ b/src/bridge/bridge.cpp @@ -1,5 +1,6 @@ #include <bridge/bridge.hpp> #include <bridge/colors.hpp> +#include <bridge/list_element.hpp> #include <xmpp/xmpp_component.hpp> #include <xmpp/xmpp_stanza.hpp> #include <irc/irc_message.hpp> @@ -286,6 +287,43 @@ void Bridge::send_irc_nick_change(const Iid& iid, const std::string& new_nick) irc->send_nick_command(new_nick); } +void Bridge::send_irc_channel_list_request(const Iid& iid, const std::string& iq_id, + const std::string& to_jid) +{ + IrcClient* irc = this->get_irc_client(iid.get_server()); + + if (!irc) + return; + + irc->send_list_command(); + irc_responder_callback_t cb = [this, iid, iq_id, to_jid](const std::string& irc_hostname, + const IrcMessage& message) -> bool + { + static std::vector<ListElement> list; + + if (irc_hostname != iid.get_server()) + return false; + if (message.command == "263" || message.command == "RPL_TRYAGAIN") + { // TODO send an error iq + return true; + } + else if (message.command == "322" || message.command == "RPL_LIST") + { // Add element to list + if (message.arguments.size() == 4) + list.emplace_back(message.arguments[1], message.arguments[2], + message.arguments[3]); + return false; + } + else if (message.command == "323" || message.command == "RPL_LISTEND") + { // Send the iq response with the content of the list + this->xmpp->send_iq_room_list_result(iq_id, to_jid, std::to_string(iid), list); + return true; + } + return false; + }; + this->add_waiting_irc(std::move(cb)); +} + void Bridge::send_irc_kick(const Iid& iid, const std::string& target, const std::string& reason, const std::string& iq_id, const std::string& to_jid) { diff --git a/src/bridge/bridge.hpp b/src/bridge/bridge.hpp index b1f79d5..8f71846 100644 --- a/src/bridge/bridge.hpp +++ b/src/bridge/bridge.hpp @@ -72,6 +72,8 @@ public: void send_irc_version_request(const std::string& irc_hostname, const std::string& target, const std::string& iq_id, const std::string& to_jid, const std::string& from_jid); + void send_irc_channel_list_request(const Iid& iid, const std::string& iq_id, + const std::string& to_jid); void forward_affiliation_role_change(const Iid& iid, const std::string& nick, const std::string& affiliation, const std::string& role); /** diff --git a/src/bridge/list_element.hpp b/src/bridge/list_element.hpp new file mode 100644 index 0000000..bd28185 --- /dev/null +++ b/src/bridge/list_element.hpp @@ -0,0 +1,19 @@ +#ifndef LIST_ELEMENT_HPP_INCLUDED +#define LIST_ELEMENT_HPP_INCLUDED + +#include <string> + +struct ListElement +{ + ListElement(const std::string& channel, const std::string& nb_users, + const std::string& topic): + channel(channel), + nb_users(nb_users), + topic(topic){} + + std::string channel; + std::string nb_users; + std::string topic; +}; + +#endif /* LIST_ELEMENT_HPP_INCLUDED */ diff --git a/src/irc/irc_client.cpp b/src/irc/irc_client.cpp index f83b48c..dedb5de 100644 --- a/src/irc/irc_client.cpp +++ b/src/irc/irc_client.cpp @@ -196,6 +196,11 @@ void IrcClient::send_kick_command(const std::string& chan_name, const std::strin this->send_message(IrcMessage("KICK", {chan_name, target, reason})); } +void IrcClient::send_list_command() +{ + this->send_message(IrcMessage("LIST", {})); +} + void IrcClient::send_topic_command(const std::string& chan_name, const std::string& topic) { this->send_message(IrcMessage("TOPIC", {chan_name, topic})); diff --git a/src/irc/irc_client.hpp b/src/irc/irc_client.hpp index 578fce2..86edbab 100644 --- a/src/irc/irc_client.hpp +++ b/src/irc/irc_client.hpp @@ -104,6 +104,10 @@ public: * Send the KICK irc command */ void send_kick_command(const std::string& chan_name, const std::string& target, const std::string& reason); + /** + * Send the LIST irc command + */ + void send_list_command(); void send_topic_command(const std::string& chan_name, const std::string& topic); /** * Send the QUIT irc command diff --git a/src/logger/logger.hpp b/src/logger/logger.hpp index 7560505..b1ae20d 100644 --- a/src/logger/logger.hpp +++ b/src/logger/logger.hpp @@ -19,7 +19,7 @@ #include "config.h" -#ifdef SYSTEMDDAEMON_FOUND +#ifdef SYSTEMD_FOUND # include <systemd/sd-daemon.h> #else # define SD_DEBUG "[DEBUG]: " diff --git a/src/xmpp/adhoc_command.cpp b/src/xmpp/adhoc_command.cpp index c20976a..c4e8a44 100644 --- a/src/xmpp/adhoc_command.cpp +++ b/src/xmpp/adhoc_command.cpp @@ -200,7 +200,7 @@ void DisconnectUserStep2(XmppComponent* xmpp_component, AdhocSession& session, X session.terminate(); } -void Reload(XmppComponent*, AdhocSession& session, XmlNode& command_node) +void Reload(XmppComponent*, AdhocSession&, XmlNode& command_node) { ::reload_process(); command_node.delete_all_children(); diff --git a/src/xmpp/xmpp_component.cpp b/src/xmpp/xmpp_component.cpp index 1df1e5d..0e2531d 100644 --- a/src/xmpp/xmpp_component.cpp +++ b/src/xmpp/xmpp_component.cpp @@ -4,6 +4,7 @@ #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> @@ -17,7 +18,7 @@ #include <uuid.h> -#ifdef SYSTEMDDAEMON_FOUND +#ifdef SYSTEMD_FOUND # include <systemd/sd-daemon.h> #endif @@ -89,7 +90,7 @@ 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 SYSTEMDDAEMON_FOUND +#ifdef SYSTEMD_FOUND sd_notifyf(0, "STATUS=Failed to connect to the XMPP server: %s", reason.data()); #endif } @@ -279,7 +280,7 @@ void XmppComponent::handle_handshake(const Stanza& stanza) this->authenticated = true; this->ever_auth = true; log_info("Authenticated with the XMPP server"); -#ifdef SYSTEMDDAEMON_FOUND +#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. @@ -556,12 +557,18 @@ void XmppComponent::handle_iq(const Stanza& stanza) } 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))) { @@ -626,7 +633,7 @@ void XmppComponent::handle_error(const Stanza& stanza) if (text) error_message = text->get_inner(); log_error("Stream error received from the XMPP server: " << error_message); -#ifdef SYSTEMDDAEMON_FOUND +#ifdef SYSTEMD_FOUND if (!this->ever_auth) sd_notifyf(0, "STATUS=Failed to authenticate to the XMPP server: %s", error_message.data()); #endif @@ -1152,6 +1159,31 @@ void XmppComponent::send_iq_result(const std::string& id, const std::string& to_ 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]; diff --git a/src/xmpp/xmpp_component.hpp b/src/xmpp/xmpp_component.hpp index 951d5a3..a0b06a6 100644 --- a/src/xmpp/xmpp_component.hpp +++ b/src/xmpp/xmpp_component.hpp @@ -26,6 +26,8 @@ #define ADHOC_NS "http://jabber.org/protocol/commands" #define PING_NS "urn:xmpp:ping" +class ListElement; + /** * A callback called when the waited iq result is received (it is matched * against the iq id) @@ -229,6 +231,12 @@ public: */ void send_iq_result(const std::string& id, const std::string& to_jid, const std::string& from); /** + * Send the channels list in one big stanza + */ + void send_iq_room_list_result(const std::string& id, const std::string to_jid, + const std::string& from, + const std::vector<ListElement>& rooms_list); + /** * Handle the various stanza types */ void handle_handshake(const Stanza& stanza); diff --git a/src/xmpp/xmpp_stanza.cpp b/src/xmpp/xmpp_stanza.cpp index 4290fc7..df19105 100644 --- a/src/xmpp/xmpp_stanza.cpp +++ b/src/xmpp/xmpp_stanza.cpp @@ -218,13 +218,12 @@ std::string XmlNode::to_string() const std::string res("<"); res += this->name; for (const auto& it: this->attributes) - res += " " + utils::remove_invalid_xml_chars(it.first) + "='" + - utils::remove_invalid_xml_chars(it.second) + "'"; + res += " " + it.first + "='" + sanitize(it.second) + "'"; if (this->closed && !this->has_children() && this->inner.empty()) res += "/>"; else { - res += ">" + utils::remove_invalid_xml_chars(this->inner); + res += ">" + sanitize(this->inner); for (const auto& child: this->children) res += child->to_string(); if (this->closed) @@ -232,7 +231,7 @@ std::string XmlNode::to_string() const res += "</" + this->get_name() + ">"; } } - res += utils::remove_invalid_xml_chars(this->tail); + res += sanitize(this->tail); return res; } @@ -265,3 +264,11 @@ std::string& XmlNode::operator[](const std::string& name) { return this->attributes[name]; } + +std::string sanitize(const std::string& data) +{ + if (utils::is_valid_utf8(data.data())) + return xml_escape(utils::remove_invalid_xml_chars(data)); + else + return xml_escape(utils::remove_invalid_xml_chars(utils::convert_to_utf8(data, "ISO-8859-1"))); +} diff --git a/src/xmpp/xmpp_stanza.hpp b/src/xmpp/xmpp_stanza.hpp index 9229ae6..f1a6a0f 100644 --- a/src/xmpp/xmpp_stanza.hpp +++ b/src/xmpp/xmpp_stanza.hpp @@ -7,6 +7,7 @@ std::string xml_escape(const std::string& data); std::string xml_unescape(const std::string& data); +std::string sanitize(const std::string& data); /** * Represent an XML node. It has |