~singpolyma/biboumi

ref: 212e8e59897c65e50f9d89949824a32d383201f4 biboumi/src/bridge/bridge.hpp -rw-r--r-- 14.0 KiB
212e8e59 — louiz’ Merge remote-tracking branch 'linkmauve/die-gc1.0-die' 3 years ago
                                                                                
81f8f45b louiz’
76a8189b louiz’
fcaffb9e louiz’
bf7b05ef Florent Le Coz
2117838c Florent Le Coz
bf7b05ef Florent Le Coz
e6f20d3c Florent Le Coz
b11126a1 Florent Le Coz
bf7b05ef Florent Le Coz
26ffc8fe Florent Le Coz
5475d16b Florent Le Coz
bf7b05ef Florent Le Coz
cfebca4d louiz’
d600a284 Florent Le Coz
bf7b05ef Florent Le Coz
b2922560 louiz’
bf7b05ef Florent Le Coz
26ffc8fe Florent Le Coz
e1d69806 Florent Le Coz
26ffc8fe Florent Le Coz
e1d69806 Florent Le Coz
26ffc8fe Florent Le Coz
bf7b05ef Florent Le Coz
1a09c965 louiz’
af420738 louiz’
3afb63a6 Florent Le Coz
12c8b1ae Florent Le Coz
e8e592d1 Florent Le Coz
5406de35 louiz’
e8e592d1 Florent Le Coz
c2311b28 Florent Le Coz
f1de6d03 Florent Le Coz
12c8b1ae Florent Le Coz
350d48a5 Emmanuel Gil Peyrot
bf7b05ef Florent Le Coz
096a4e3b Florent Le Coz
14fe9711 Emmanuel Gil Peyrot
096a4e3b Florent Le Coz
e2fc3cf6 louiz’
14fe9711 Emmanuel Gil Peyrot
1aa2c2d8 Florent Le Coz
e9670889 louiz’
579ca4bd Florent Le Coz
0a6b673b Florent Le Coz
5406de35 louiz’
eca31ce8 louiz’
2117838c Florent Le Coz
68d6b829 louiz’
545ab11f Florent Le Coz
d8da7984 Florent Le Coz
545ab11f Florent Le Coz
76a8189b louiz’
e397fc83 louiz’
d8da7984 Florent Le Coz
4a8bcd3c Florent Le Coz
12eeb4d1 Florent Le Coz
bf7b05ef Florent Le Coz
07e22095 louiz’
663d4ad5 louiz’
bf7b05ef Florent Le Coz
79cdf170 Florent Le Coz
bf7b05ef Florent Le Coz
2d11a5f4 louiz’
acf769d8 Florent Le Coz
2d11a5f4 louiz’
bf7b05ef Florent Le Coz
04d28f96 louiz’
2d11a5f4 louiz’
a418b6ed Florent Le Coz
d1626c92 louiz’
fcaffb9e louiz’
d1626c92 louiz’
c6749ecc Jonas Schäfer
a418b6ed Florent Le Coz
0b8738d8 louiz’
7c671499 Florent Le Coz
e3ea0d62 Florent Le Coz
6c2d03da Florent Le Coz
e3ea0d62 Florent Le Coz
6c2d03da Florent Le Coz
7c671499 Florent Le Coz
bb596582 louiz’
9fa1852c louiz’
faa33c1a louiz’
096a4e3b Florent Le Coz
cf9f3a1f Florent Le Coz
096a4e3b Florent Le Coz
cf9f3a1f Florent Le Coz
8ac8d2b2 louiz’
43cc60e4 Florent Le Coz
e840704b Florent Le Coz
5ec05cb0 Florent Le Coz
56eb5df2 Florent Le Coz
5ec05cb0 Florent Le Coz
d8da7984 Florent Le Coz
4c8fb9a0 louiz’
f9a6f973 louiz’
4c8fb9a0 louiz’
096a4e3b Florent Le Coz
61ca40fa Florent Le Coz
99aba566 Florent Le Coz
61ca40fa Florent Le Coz
99aba566 Florent Le Coz
04de62a4 Florent Le Coz
26ffc8fe Florent Le Coz
272c0e49 louiz’
e1d69806 Florent Le Coz
26ffc8fe Florent Le Coz
e1d69806 Florent Le Coz
26ffc8fe Florent Le Coz
e1d69806 Florent Le Coz
248e25c2 louiz’
0d2dd71d louiz’
cfebca4d louiz’
7c671499 Florent Le Coz
bf7b05ef Florent Le Coz
1aa2c2d8 Florent Le Coz
bf7b05ef Florent Le Coz
5475d16b Florent Le Coz
a418b6ed Florent Le Coz
7376831b louiz’
a418b6ed Florent Le Coz
5475d16b Florent Le Coz
0d2dd71d louiz’
7376831b louiz’
5475d16b Florent Le Coz
507d0c2c louiz’
bf7b05ef Florent Le Coz
99aba566 Florent Le Coz
bf7b05ef Florent Le Coz
248e25c2 louiz’
bf7b05ef Florent Le Coz
7e242714 Florent Le Coz
bf7b05ef Florent Le Coz
7e242714 Florent Le Coz
bf7b05ef Florent Le Coz
5507adbe Florent Le Coz
04de62a4 Florent Le Coz
26ffc8fe Florent Le Coz
66609cfb louiz’
507d0c2c louiz’
7376831b louiz’
507d0c2c louiz’
f9a6f973 louiz’
507d0c2c louiz’
1a09c965 louiz’
21a79b7b louiz’
1a09c965 louiz’
21a79b7b louiz’
1a09c965 louiz’
507d0c2c louiz’
0ce75ab5 louiz’
507d0c2c louiz’
711861d4 louiz’
507d0c2c louiz’
711861d4 louiz’
507d0c2c louiz’
76a8189b louiz’
cfebca4d louiz’
bf7b05ef Florent Le Coz
5475d16b Florent Le Coz
81f8f45b louiz’
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
#pragma once

#include <bridge/result_set_management.hpp>
#include <bridge/list_element.hpp>
#include <bridge/history_limit.hpp>

#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>

#include <biboumi.h>

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<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(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);
  /**
   * 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.
   */
  bool join_irc_channel(const Iid& iid, std::string nickname,
                        const std::string& password,
                        const std::string& resource,
                        HistoryLimit history_limit);

  void send_channel_message(const Iid& iid, const std::string& body, std::string id, std::vector<XmlNode> nodes_to_reflect);
  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, const HistoryLimit& history_limit);
  void send_room_history(const std::string& hostname, std::string chan_name, const std::string& resource, const HistoryLimit& history_limit);
  /**
   * Send a message from a MUC participant or a direct message
   */
  void send_message(const Iid& iid, const std::string& nick, const std::string& body, const bool muc, const bool log=true);
  /**
   * 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 IrcUser& nick,
                      const std::string& message, const bool self,
                      const bool user_requested,
                      const std::string& resource,
                      const IrcClient* client);
  /**
   * 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);
  void on_irc_client_connected(const std::string& hostname);
  void on_irc_client_disconnected(const std::string& hostname);

  /**
   * 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::unique_ptr<IrcClient>>& get_irc_clients();
  const std::unordered_map<std::string, std::unique_ptr<IrcClient>>& get_irc_clients() const;
  std::set<char> get_chantypes(const std::string& hostname) const;
#ifdef USE_DATABASE
  void set_record_history(const bool val);
#endif

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<std::string, std::unique_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>;
public:
  std::map<ChannelKey, std::set<Resource>> resources_in_chan;
  std::map<IrcHostname, std::set<Resource>> resources_in_server;
private:
  /**
   * 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);
public:
  bool is_resource_in_chan(const ChannelKey& channel, const std::string& resource) const;
private:
  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);
  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<IrcHostname, ChannelList> 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;
};