1 #pragma once 2 3 #include <bridge/result_set_management.hpp> 4 #include <bridge/list_element.hpp> 5 #include <bridge/history_limit.hpp> 6 7 #include <irc/irc_message.hpp> 8 #include <irc/irc_client.hpp> 9 #include <bridge/colors.hpp> 10 #include <irc/irc_user.hpp> 11 #include <irc/iid.hpp> 12 13 #include <unordered_map> 14 #include <functional> 15 #include <exception> 16 #include <string> 17 #include <memory> 18 19 #include <biboumi.h> 20 21 class BiboumiComponent; 22 class Poller; 23 struct ResultSetInfo; 24 25 /** 26 * A callback called for each IrcMessage we receive. If the message triggers 27 * a response, it must send ore or more iq and return true (in that case it 28 * is removed from the list), otherwise it must do nothing and just return 29 * false. 30 */ 31 using irc_responder_callback_t = std::function<bool(const std::string& irc_hostname, const IrcMessage& message)>; 32 33 /** 34 * One bridge is spawned for each XMPP user that uses the component. The 35 * bridge spawns IrcClients when needed (when the user wants to join a 36 * channel on a new server) and does the translation between the two 37 * protocols. 38 */ 39 class Bridge 40 { 41 public: 42 explicit Bridge(std::string user_jid, BiboumiComponent& xmpp, std::shared_ptr<Poller>& poller); 43 ~Bridge() = default; 44 45 Bridge(const Bridge&) = delete; 46 Bridge(Bridge&& other) = delete; 47 Bridge& operator=(const Bridge&) = delete; 48 Bridge& operator=(Bridge&&) = delete; 49 /** 50 * QUIT all connected IRC servers. 51 */ 52 void shutdown(const std::string& exit_message); 53 /** 54 * PART the given resource from all the channels 55 */ 56 void remove_resource(const std::string& resource, const std::string& part_message); 57 /** 58 * Remove all inactive IrcClients 59 */ 60 void clean(); 61 /** 62 * Return the jid of the XMPP user using this bridge 63 */ 64 const std::string& get_jid() const; 65 std::string get_bare_jid() const; 66 67 static Xmpp::body make_xmpp_body(const std::string& str, const std::string& encoding = "ISO-8859-1"); 68 /*** 69 ** 70 ** From XMPP to IRC. 71 ** 72 **/ 73 74 /** 75 * Try to join an irc_channel. 76 */ 77 bool join_irc_channel(const Iid& iid, std::string nickname, 78 const std::string& password, 79 const std::string& resource, 80 HistoryLimit history_limit); 81 82 void send_channel_message(const Iid& iid, const std::string& body, std::string id, std::vector<XmlNode> nodes_to_reflect); 83 void send_private_message(const Iid& iid, const std::string& body, const std::string& type="PRIVMSG"); 84 void send_raw_message(const std::string& hostname, const std::string& body); 85 void leave_irc_channel(Iid&& iid, const std::string& status_message, const std::string& resource); 86 void send_irc_nick_change(const Iid& iid, const std::string& new_nick, const std::string& requesting_resource); 87 void send_irc_kick(const Iid& iid, const std::string& target, const std::string& reason, 88 const std::string& iq_id, const std::string& to_jid); 89 void set_channel_topic(const Iid& iid, std::string subject); 90 void send_xmpp_version_to_irc(const Iid& iid, const std::string& name, const std::string& version, 91 const std::string& os); 92 void send_irc_ping_result(const Iid& iid, const std::string& id); 93 void send_irc_version_request(const std::string& irc_hostname, const std::string& target, 94 const std::string& iq_id, const std::string& to_jid, 95 const std::string& from_jid); 96 void send_irc_channel_list_request(const Iid& iid, const std::string& iq_id, const std::string& to_jid, 97 ResultSetInfo rs_info); 98 /** 99 * Check if the channel list contains what is needed to answer the RSM request, 100 * if it does, send the iq result. If the list is complete but does not contain 101 * everything, send the result anyway (because there are no more available 102 * channels that could complete the list). 103 * 104 * Returns true if we sent the answer. 105 */ 106 bool send_matching_channel_list(const ChannelList& channel_list, 107 const ResultSetInfo& rs_info, const std::string& id, const std::string& to_jid, 108 const std::string& from); 109 void forward_affiliation_role_change(const Iid& iid, const std::string& from, const std::string& nick, 110 const std::string& affiliation, const std::string& role, const std::string& id); 111 /** 112 * Directly send a CTCP PING request to the IRC user 113 */ 114 void send_irc_user_ping_request(const std::string& irc_hostname, const std::string& nick, 115 const std::string& iq_id, const std::string& to_jid, 116 const std::string& from_jid); 117 /** 118 * First check if the participant is in the room, before sending a direct 119 * CTCP PING request to the IRC user 120 */ 121 void send_irc_participant_ping_request(const Iid& iid, const std::string& nick, 122 const std::string& iq_id, const std::string& to_jid, 123 const std::string& from_jid); 124 /** 125 * Directly send back a result if it's a gateway ping or if we are 126 * connected to the given IRC server, an error otherwise. 127 */ 128 void on_gateway_ping(const std::string& irc_hostname, const std::string& iq_id, const std::string& to_jid, 129 const std::string& from_jid); 130 131 void send_irc_invitation(const Iid& iid, const std::string& to); 132 133 /*** 134 ** 135 ** From IRC to XMPP. 136 ** 137 **/ 138 139 /** 140 * Send a message corresponding to a server NOTICE, the from attribute 141 * should be juste the server hostname. 142 */ 143 void send_xmpp_message(const std::string& from, const std::string& author, const std::string& msg); 144 /** 145 * Send the presence of a new user in the MUC. 146 */ 147 void send_user_join(const std::string& hostname, const std::string& chan_name, 148 const IrcUser* user, const char user_mode, 149 const bool self, const std::string& resource); 150 void send_user_join(const std::string& hostname, const std::string& chan_name, 151 const IrcUser* user, const char user_mode, 152 const bool self); 153 154 /** 155 * Send the topic of the MUC to the user 156 */ 157 void send_topic(const std::string& hostname, const std::string& chan_name, const std::string& topic, const std::string& who); 158 void send_topic(const std::string& hostname, const std::string& chan_name, const std::string& topic, const std::string& who, const std::string& resource); 159 /** 160 * Send the MUC history to the user 161 */ 162 void send_room_history(const std::string& hostname, const std::string& chan_name, const HistoryLimit& history_limit); 163 void send_room_history(const std::string& hostname, std::string chan_name, const std::string& resource, const HistoryLimit& history_limit); 164 /** 165 * Send a message from a MUC participant or a direct message 166 */ 167 void send_message(const Iid& iid, const std::string& nick, const std::string& body, const bool muc, const bool log=true); 168 /** 169 * Send a presence of type error, from a room. 170 */ 171 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); 172 /** 173 * Send an unavailable presence from this participant 174 */ 175 void send_muc_leave(const Iid& iid, const IrcUser& nick, 176 const std::string& message, const bool self, 177 const bool user_requested, 178 const std::string& resource, 179 const IrcClient* client); 180 /** 181 * Send presences to indicate that an user old_nick (ourself if self == 182 * true) changed his nick to new_nick. The user_mode is needed because 183 * the xmpp presence needs ton contain the role and affiliation of the 184 * user. 185 */ 186 void send_nick_change(Iid&& iid, 187 const std::string& old_nick, 188 const std::string& new_nick, 189 const char user_mode, 190 const bool self); 191 void kick_muc_user(Iid&& iid, const std::string& target, const std::string& reason, const std::string& author, 192 const bool self); 193 void send_nickname_conflict_error(const Iid& iid, const std::string& nickname); 194 /** 195 * Send a role/affiliation change, matching the change of mode for that user 196 */ 197 void send_affiliation_role_change(const Iid& iid, const std::string& target, const char mode); 198 /** 199 * Send an iq version request coming from nick!hostname@ 200 */ 201 void send_iq_version_request(const std::string& nick, const std::string& hostname); 202 /** 203 * Send an iq ping request coming from nick!hostname@ 204 */ 205 void send_xmpp_ping_request(const std::string& nick, const std::string& hostname, 206 const std::string& id); 207 void send_xmpp_invitation(const Iid& iid, const std::string& author); 208 void on_irc_client_connected(const std::string& hostname); 209 void on_irc_client_disconnected(const std::string& hostname); 210 211 /** 212 * Misc 213 */ 214 std::string get_own_nick(const Iid& iid); 215 /** 216 * Get the number of server to which this bridge is connected or connecting. 217 */ 218 size_t active_clients() const; 219 /** 220 * Add (or replace the existing) <nick, jid> into the preferred_user_from map 221 */ 222 void set_preferred_from_jid(const std::string& nick, const std::string& full_jid); 223 /** 224 * Remove the preferred jid for the given IRC nick 225 */ 226 void remove_preferred_from_jid(const std::string& nick); 227 /** 228 * Given a channel_name, remove all preferred from_jid that come 229 * from this chan. 230 */ 231 void remove_all_preferred_from_jid_of_room(const std::string& channel_name); 232 /** 233 * Add a callback to the waiting list of irc callbacks. 234 */ 235 void add_waiting_irc(irc_responder_callback_t&& callback); 236 /** 237 * Iter over all the waiting_iq, call the iq_responder_filter_t for each, 238 * whenever one of them returns true: call the corresponding 239 * iq_responder_callback_t and remove the callback from the list. 240 */ 241 void trigger_on_irc_message(const std::string& irc_hostname, const IrcMessage& message); 242 std::unordered_map<std::string, std::unique_ptr<IrcClient>>& get_irc_clients(); 243 const std::unordered_map<std::string, std::unique_ptr<IrcClient>>& get_irc_clients() const; 244 std::set<char> get_chantypes(const std::string& hostname) const; 245 #ifdef USE_DATABASE 246 void set_record_history(const bool val); 247 #endif 248 249 private: 250 /** 251 * Returns the client for the given hostname, create one (and use the 252 * username in this case) if none is found, and connect that newly-created 253 * client immediately. 254 */ 255 IrcClient* make_irc_client(const std::string& hostname, const std::string& nickname); 256 /** 257 * This version does not create the IrcClient if it does not exist, throws 258 * a IRCServerNotConnected error in that case. 259 */ 260 IrcClient* get_irc_client(const std::string& hostname); 261 public: 262 /** 263 * Idem, but returns nullptr if the server does not exist. 264 */ 265 IrcClient* find_irc_client(const std::string& hostname) const; 266 private: 267 /** 268 * The bare JID of the user associated with this bridge. Messages from/to this 269 * JID are only managed by this bridge. 270 */ 271 const std::string user_jid; 272 /** 273 * One IrcClient for each IRC server we need to be connected to. 274 * The pointer is shared by the bridge and the poller. 275 */ 276 std::unordered_map<std::string, std::unique_ptr<IrcClient>> irc_clients; 277 /** 278 * To communicate back with the XMPP component 279 */ 280 BiboumiComponent& xmpp; 281 /** 282 * Poller, to give it the IrcClients that we spawn, to make it manage 283 * their sockets. 284 */ 285 std::shared_ptr<Poller> poller; 286 /** 287 * A map of <nick, full_jid>. For example if this map contains <"toto", 288 * "#somechan%server@biboumi/ToTo">, whenever a private message is 289 * received from the user "toto", instead of forwarding it to XMPP with 290 * from='toto!server@biboumi', we use instead 291 * from='#somechan%server@biboumi/ToTo' 292 */ 293 std::unordered_map<std::string, std::string> preferred_user_from; 294 /** 295 * A list of callbacks that are waiting for some IrcMessage to trigger a 296 * response. We add callbacks in this list whenever we received an IQ 297 * request and we need a response from IRC to be able to provide the 298 * response iq. 299 */ 300 std::vector<irc_responder_callback_t> waiting_irc; 301 /** 302 * Resources to IRC channel/server mapping: 303 */ 304 using Resource = std::string; 305 using ChannelName = std::string; 306 using IrcHostname = std::string; 307 using ChannelKey = std::tuple<ChannelName, IrcHostname>; 308 public: 309 std::map<ChannelKey, std::set<Resource>> resources_in_chan; 310 std::map<IrcHostname, std::set<Resource>> resources_in_server; 311 private: 312 /** 313 * Manage which resource is in which channel 314 */ 315 void add_resource_to_chan(const ChannelKey& channel, const std::string& resource); 316 void remove_resource_from_chan(const ChannelKey& channel, const std::string& resource); 317 public: 318 bool is_resource_in_chan(const ChannelKey& channel, const std::string& resource) const; 319 private: 320 void remove_all_resources_from_chan(const ChannelKey& channel); 321 std::size_t number_of_resources_in_chan(const ChannelKey& channel) const; 322 323 void add_resource_to_server(const IrcHostname& irc_hostname, const std::string& resource); 324 void remove_resource_from_server(const IrcHostname& irc_hostname, const std::string& resource); 325 size_t number_of_channels_the_resource_is_in(const std::string& irc_hostname, const std::string& resource) const; 326 327 /** 328 * Generate all the stanzas to be sent to this resource, simulating a join on this channel. 329 * This means sending the whole user list, the topic, etc 330 * TODO: send message history 331 */ 332 void generate_channel_join_for_resource(const Iid& iid, const std::string& resource); 333 /** 334 * A cache of the channels list (as returned by the server on a LIST 335 * request), to be re-used on a subsequent XMPP list request that 336 * uses result-set-management. 337 */ 338 std::map<IrcHostname, ChannelList> channel_list_cache; 339 340 #ifdef USE_DATABASE 341 bool record_history { true }; 342 #endif 343 }; 344 345 struct IRCNotConnected: public std::exception 346 { IRCNotConnectedIRCNotConnected347 IRCNotConnected(const std::string& hostname): 348 hostname(hostname) {} 349 const std::string hostname; 350 }; 351 352 353