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