#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include class BiboumiComponent; class Poller; struct ResultSetInfo; /** * 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; /** * 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(std::string user_jid, BiboumiComponent& xmpp, std::shared_ptr& 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); /** * PART the given resource from all the channels */ void remove_resource(const std::string& resource, const std::string& part_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, const std::string& status_message, const std::string& resource); void send_irc_nick_change(const Iid& iid, const std::string& new_nick, const std::string& requesting_resource); 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, 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, ResultSetInfo rs_info); /** * Check if the channel list contains what is needed to answer the RSM request, * if it does, send the iq result. If the list is complete but does not contain * everything, send the result anyway (because there are no more available * channels that could complete the list). * * Returns true if we sent the answer. */ bool send_matching_channel_list(const ChannelList& channel_list, const ResultSetInfo& rs_info, const std::string& id, const std::string& to_jid, const std::string& from); void forward_affiliation_role_change(const Iid& iid, const std::string& from, const std::string& nick, const std::string& affiliation, const std::string& role, const std::string& id); /** * 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); void send_irc_invitation(const Iid& iid, const std::string& to); /*** ** ** 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 the MUC history to the user */ void send_room_history(const std::string& hostname, const std::string& chan_name); void send_room_history(const std::string& hostname, std::string chan_name, 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(const Iid& iid, const 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, const bool self); 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); void send_xmpp_invitation(const Iid& iid, const std::string& author); /** * 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) 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>& get_irc_clients(); const std::unordered_map>& get_irc_clients() const; std::set get_chantypes(const std::string& hostname) const; #ifdef USE_DATABASE void set_record_history(const bool val); #endif /** * Start a timer that will send a QUIT command after the * configured linger time is expired. */ void quit_or_start_linger_timer(const std::string& irc_hostname); 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); public: /** * Idem, but returns nullptr if the server does not exist. */ IrcClient* find_irc_client(const std::string& hostname) const; private: /** * 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> 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; /** * A map of . 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 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 waiting_irc; /** * Resources to IRC channel/server mapping: */ using Resource = std::string; using ChannelName = std::string; using IrcHostname = std::string; using ChannelKey = std::tuple; public: std::map> resources_in_chan; private: std::map> resources_in_server; /** * Manage which resource is in which channel */ void add_resource_to_chan(const ChannelKey& channel, const std::string& resource); void remove_resource_from_chan(const ChannelKey& channel, const std::string& resource); bool is_resource_in_chan(const ChannelKey& channel, const std::string& resource) const; void remove_all_resources_from_chan(const ChannelKey& channel); std::size_t number_of_resources_in_chan(const ChannelKey& channel) 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); /** * A cache of the channels list (as returned by the server on a LIST * request), to be re-used on a subsequent XMPP list request that * uses result-set-management. */ std::map channel_list_cache; #ifdef USE_DATABASE bool record_history { true }; #endif }; struct IRCNotConnected: public std::exception { IRCNotConnected(const std::string& hostname): hostname(hostname) {} const std::string hostname; };