summaryrefslogtreecommitdiff
path: root/src/bridge/bridge.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/bridge/bridge.hpp')
-rw-r--r--src/bridge/bridge.hpp293
1 files changed, 293 insertions, 0 deletions
diff --git a/src/bridge/bridge.hpp b/src/bridge/bridge.hpp
new file mode 100644
index 0000000..69b7bd5
--- /dev/null
+++ b/src/bridge/bridge.hpp
@@ -0,0 +1,293 @@
+#pragma once
+
+
+#include <irc/irc_message.hpp>
+#include <irc/irc_client.hpp>
+#include <bridge/colors.hpp>
+#include <irc/irc_user.hpp>
+#include <irc/iid.hpp>
+
+#include <unordered_map>
+#include <functional>
+#include <exception>
+#include <string>
+#include <memory>
+
+class BiboumiComponent;
+class Poller;
+
+/**
+ * A callback called for each IrcMessage we receive. If the message triggers
+ * a response, it must send ore or more iq and return true (in that case it
+ * is removed from the list), otherwise it must do nothing and just return
+ * false.
+ */
+using irc_responder_callback_t = std::function<bool(const std::string& irc_hostname, const IrcMessage& message)>;
+
+/**
+ * One bridge is spawned for each XMPP user that uses the component. The
+ * bridge spawns IrcClients when needed (when the user wants to join a
+ * channel on a new server) and does the translation between the two
+ * protocols.
+ */
+class Bridge
+{
+public:
+ explicit Bridge(const std::string& user_jid, BiboumiComponent& xmpp, std::shared_ptr<Poller> poller);
+ ~Bridge() = default;
+
+ Bridge(const Bridge&) = delete;
+ Bridge(Bridge&& other) = delete;
+ Bridge& operator=(const Bridge&) = delete;
+ Bridge& operator=(Bridge&&) = delete;
+ /**
+ * QUIT all connected IRC servers.
+ */
+ void shutdown(const std::string& exit_message);
+ /**
+ * Remove all inactive IrcClients
+ */
+ void clean();
+ /**
+ * Return the jid of the XMPP user using this bridge
+ */
+ const std::string& get_jid() const;
+ std::string get_bare_jid() const;
+
+ static Xmpp::body make_xmpp_body(const std::string& str, const std::string& encoding = "ISO-8859-1");
+ /***
+ **
+ ** From XMPP to IRC.
+ **
+ **/
+
+ /**
+ * Try to join an irc_channel, does nothing and return true if the channel
+ * was already joined.
+ */
+ bool join_irc_channel(const Iid& iid, const std::string& nickname, const std::string& password, const std::string& resource);
+
+ void send_channel_message(const Iid& iid, const std::string& body);
+ void send_private_message(const Iid& iid, const std::string& body, const std::string& type="PRIVMSG");
+ void send_raw_message(const std::string& hostname, const std::string& body);
+ void leave_irc_channel(Iid&& iid, std::string&& status_message, const std::string& resource);
+ void send_irc_nick_change(const Iid& iid, const std::string& new_nick);
+ void send_irc_kick(const Iid& iid, const std::string& target, const std::string& reason,
+ const std::string& iq_id, const std::string& to_jid);
+ void set_channel_topic(const Iid& iid, const std::string& subject);
+ void send_xmpp_version_to_irc(const Iid& iid, const std::string& name, const std::string& version,
+ const std::string& os);
+ void send_irc_ping_result(const Iid& iid, const std::string& id);
+ 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);
+ /**
+ * Directly send a CTCP PING request to the IRC user
+ */
+ void send_irc_user_ping_request(const std::string& irc_hostname, const std::string& nick,
+ const std::string& iq_id, const std::string& to_jid,
+ const std::string& from_jid);
+ /**
+ * First check if the participant is in the room, before sending a direct
+ * CTCP PING request to the IRC user
+ */
+ void send_irc_participant_ping_request(const Iid& iid, const std::string& nick,
+ const std::string& iq_id, const std::string& to_jid,
+ const std::string& from_jid);
+ /**
+ * Directly send back a result if it's a gateway ping or if we are
+ * connected to the given IRC server, an error otherwise.
+ */
+ void on_gateway_ping(const std::string& irc_hostname, const std::string& iq_id, const std::string& to_jid,
+ const std::string& from_jid);
+
+ /***
+ **
+ ** From IRC to XMPP.
+ **
+ **/
+
+ /**
+ * Send a message corresponding to a server NOTICE, the from attribute
+ * should be juste the server hostname.
+ */
+ void send_xmpp_message(const std::string& from, const std::string& author, const std::string& msg);
+ /**
+ * Send the presence of a new user in the MUC.
+ */
+ void send_user_join(const std::string& hostname, const std::string& chan_name,
+ const IrcUser* user, const char user_mode,
+ const bool self, const std::string& resource);
+ void send_user_join(const std::string& hostname, const std::string& chan_name,
+ const IrcUser* user, const char user_mode,
+ const bool self);
+
+ /**
+ * Send the topic of the MUC to the user
+ */
+ void send_topic(const std::string& hostname, const std::string& chan_name, const std::string& topic, const std::string& who);
+ void send_topic(const std::string& hostname, const std::string& chan_name, const std::string& topic, const std::string& who, const std::string& resource);
+ /**
+ * Send a MUC message from some participant
+ */
+ void send_message(const Iid& iid, const std::string& nick, const std::string& body, const bool muc);
+ /**
+ * Send a presence of type error, from a room.
+ */
+ void send_presence_error(const Iid& iid, const std::string& nick, const std::string& type, const std::string& condition, const std::string& error_code, const std::string& text);
+ /**
+ * Send an unavailable presence from this participant
+ */
+ void send_muc_leave(Iid&& iid, std::string&& nick, const std::string& message, const bool self, const std::string& resource="");
+ /**
+ * Send presences to indicate that an user old_nick (ourself if self ==
+ * true) changed his nick to new_nick. The user_mode is needed because
+ * the xmpp presence needs ton contain the role and affiliation of the
+ * user.
+ */
+ void send_nick_change(Iid&& iid,
+ const std::string& old_nick,
+ const std::string& new_nick,
+ const char user_mode,
+ const bool self);
+ void kick_muc_user(Iid&& iid, const std::string& target, const std::string& reason, const std::string& author);
+ void send_nickname_conflict_error(const Iid& iid, const std::string& nickname);
+ /**
+ * Send a role/affiliation change, matching the change of mode for that user
+ */
+ void send_affiliation_role_change(const Iid& iid, const std::string& target, const char mode);
+ /**
+ * Send an iq version request coming from nick!hostname@
+ */
+ void send_iq_version_request(const std::string& nick, const std::string& hostname);
+ /**
+ * Send an iq ping request coming from nick!hostname@
+ */
+ void send_xmpp_ping_request(const std::string& nick, const std::string& hostname,
+ const std::string& id);
+ /**
+ * Misc
+ */
+ std::string get_own_nick(const Iid& iid);
+ /**
+ * Get the number of server to which this bridge is connected or connecting.
+ */
+ size_t active_clients() const;
+ /**
+ * Add (or replace the existing) <nick, jid> into the preferred_user_from map
+ */
+ void set_preferred_from_jid(const std::string& nick, const std::string& full_jid);
+ /**
+ * Remove the preferred jid for the given IRC nick
+ */
+ void remove_preferred_from_jid(const std::string& nick);
+ /**
+ * Given a channel_name, remove all preferred from_jid that come
+ * from this chan.
+ */
+ void remove_all_preferred_from_jid_of_room(const std::string& channel_name);
+ /**
+ * Add a callback to the waiting list of irc callbacks.
+ */
+ void add_waiting_irc(irc_responder_callback_t&& callback);
+ /**
+ * Iter over all the waiting_iq, call the iq_responder_filter_t for each,
+ * whenever one of them returns true: call the corresponding
+ * iq_responder_callback_t and remove the callback from the list.
+ */
+ void trigger_on_irc_message(const std::string& irc_hostname, const IrcMessage& message);
+ std::unordered_map<std::string, std::shared_ptr<IrcClient>>& get_irc_clients();
+
+private:
+ /**
+ * Returns the client for the given hostname, create one (and use the
+ * username in this case) if none is found, and connect that newly-created
+ * client immediately.
+ */
+ IrcClient* make_irc_client(const std::string& hostname, const std::string& nickname);
+ /**
+ * This version does not create the IrcClient if it does not exist, throws
+ * a IRCServerNotConnected error in that case.
+ */
+ IrcClient* get_irc_client(const std::string& hostname);
+ /**
+ * Idem, but returns nullptr if the server does not exist.
+ */
+ IrcClient* find_irc_client(const std::string& hostname);
+ /**
+ * The bare JID of the user associated with this bridge. Messages from/to this
+ * JID are only managed by this bridge.
+ */
+ const std::string user_jid;
+ /**
+ * One IrcClient for each IRC server we need to be connected to.
+ * The pointer is shared by the bridge and the poller.
+ */
+ std::unordered_map<std::string, std::shared_ptr<IrcClient>> irc_clients;
+ /**
+ * To communicate back with the XMPP component
+ */
+ BiboumiComponent& xmpp;
+ /**
+ * Poller, to give it the IrcClients that we spawn, to make it manage
+ * their sockets.
+ */
+ std::shared_ptr<Poller> poller;
+ /**
+ * A map of <nick, full_jid>. For example if this map contains <"toto",
+ * "#somechan%server@biboumi/ToTo">, whenever a private message is
+ * received from the user "toto", instead of forwarding it to XMPP with
+ * from='toto!server@biboumi', we use instead
+ * from='#somechan%server@biboumi/ToTo'
+ */
+ std::unordered_map<std::string, std::string> preferred_user_from;
+ /**
+ * A list of callbacks that are waiting for some IrcMessage to trigger a
+ * response. We add callbacks in this list whenever we received an IQ
+ * request and we need a response from IRC to be able to provide the
+ * response iq.
+ */
+ std::vector<irc_responder_callback_t> waiting_irc;
+
+ /**
+ * Resources to IRC channel/server mapping:
+ */
+ using Resource = std::string;
+ using ChannelName = std::string;
+ using IrcHostname = std::string;
+ using ChannelKey = std::tuple<ChannelName, IrcHostname>;
+ std::map<ChannelKey, std::set<Resource>> resources_in_chan;
+ std::map<IrcHostname, std::set<Resource>> resources_in_server;
+ /**
+ * Manage which resource is in which channel
+ */
+ void add_resource_to_chan(const ChannelKey& channel_key, const std::string& resource);
+ void remove_resource_from_chan(const ChannelKey& channel_key, const std::string& resource);
+ bool is_resource_in_chan(const ChannelKey& channel_key, const std::string& resource) const;
+ std::size_t number_of_resources_in_chan(const ChannelKey& channel_key) const;
+
+ void add_resource_to_server(const IrcHostname& irc_hostname, const std::string& resource);
+ void remove_resource_from_server(const IrcHostname& irc_hostname, const std::string& resource);
+ bool is_resource_in_server(const IrcHostname& irc_hostname, const std::string& resource) const;
+ size_t number_of_channels_the_resource_is_in(const std::string& irc_hostname, const std::string& resource) const;
+
+ /**
+ * Generate all the stanzas to be sent to this resource, simulating a join on this channel.
+ * This means sending the whole user list, the topic, etc
+ * TODO: send message history
+ */
+ void generate_channel_join_for_resource(const Iid& iid, const std::string& resource);
+};
+
+struct IRCNotConnected: public std::exception
+{
+ IRCNotConnected(const std::string& hostname):
+ hostname(hostname) {}
+ const std::string hostname;
+};
+
+