bridge.hpp 14 KB
Newer Older
1 2
#pragma once

3 4
#include <bridge/result_set_management.hpp>
#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 12 13
#include <irc/iid.hpp>

#include <unordered_map>
14
#include <functional>
15
#include <exception>
16 17 18
#include <string>
#include <memory>

19 20
#include <biboumi.h>

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 28
 * 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
29 30
 * false.
 */
31
using irc_responder_callback_t = std::function<bool(const std::string& irc_hostname, const IrcMessage& message)>;
32

33 34 35 36 37 38 39 40 41
/**
 * 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:
louiz’'s avatar
louiz’ committed
42
  explicit Bridge(std::string  user_jid, BiboumiComponent& xmpp, std::shared_ptr<Poller>& poller);
louiz’'s avatar
louiz’ committed
43 44 45 46 47 48
  ~Bridge() = default;

  Bridge(const Bridge&) = delete;
  Bridge(Bridge&& other) = delete;
  Bridge& operator=(const Bridge&) = delete;
  Bridge& operator=(Bridge&&) = delete;
louiz’'s avatar
louiz’ committed
49 50 51
  /**
   * QUIT all connected IRC servers.
   */
52
  void shutdown(const std::string& exit_message);
53 54 55 56
  /**
   * PART the given resource from all the channels
   */
  void remove_resource(const std::string& resource, const std::string& part_message);
louiz’'s avatar
louiz’ committed
57 58 59 60
  /**
   * Remove all inactive IrcClients
   */
  void clean();
61 62 63 64
  /**
   * Return the jid of the XMPP user using this bridge
   */
  const std::string& get_jid() const;
louiz’'s avatar
louiz’ committed
65
  std::string get_bare_jid() const;
66

Link Mauve's avatar
Link Mauve committed
67
  static Xmpp::body make_xmpp_body(const std::string& str, const std::string& encoding = "ISO-8859-1");
68 69 70 71 72 73
  /***
   **
   ** From XMPP to IRC.
   **
   **/

louiz’'s avatar
louiz’ committed
74 75 76 77
  /**
   * Try to join an irc_channel, does nothing and return true if the channel
   * was already joined.
   */
78
  bool join_irc_channel(const Iid& iid, std::string nickname, const std::string& password, const std::string& resource, HistoryLimit history_limit);
79

louiz’'s avatar
louiz’ committed
80
  void send_channel_message(const Iid& iid, const std::string& body, std::string id);
louiz’'s avatar
louiz’ committed
81
  void send_private_message(const Iid& iid, const std::string& body, const std::string& type="PRIVMSG");
louiz’'s avatar
louiz’ committed
82
  void send_raw_message(const std::string& hostname, const std::string& body);
83
  void leave_irc_channel(Iid&& iid, const std::string& status_message, const std::string& resource);
84
  void send_irc_nick_change(const Iid& iid, const std::string& new_nick, const std::string& requesting_resource);
85 86
  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);
louiz’'s avatar
louiz’ committed
87
  void set_channel_topic(const Iid& iid, std::string subject);
88 89
  void send_xmpp_version_to_irc(const Iid& iid, const std::string& name, const std::string& version,
                                const std::string& os);
90
  void send_irc_ping_result(const Iid& iid, const std::string& id);
91 92 93
  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);
94 95 96 97 98 99 100 101 102 103 104 105 106
  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);
107 108
  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);
109 110 111 112 113 114
  /**
   * 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);
115 116 117 118 119 120 121
  /**
   * 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);
122 123 124 125 126 127
  /**
   * 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);
128

129
  void send_irc_invitation(const Iid& iid, const std::string& to);
130

131 132 133 134 135 136 137 138
  /***
   **
   ** From IRC to XMPP.
   **
   **/

  /**
   * Send a message corresponding to a server NOTICE, the from attribute
139
   * should be juste the server hostname.
140 141 142 143 144
   */
  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.
   */
145 146 147 148 149
  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,
150
                      const bool self);
151

152 153 154
  /**
   * Send the topic of the MUC to the user
   */
155
  void send_topic(const std::string& hostname, const std::string& chan_name, const std::string& topic, const std::string& who);
156
  void send_topic(const std::string& hostname, const std::string& chan_name, const std::string& topic, const std::string& who, const std::string& resource);
157 158 159
  /**
   * Send the MUC history to the user
   */
160 161
  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);
louiz’'s avatar
louiz’ committed
162 163 164
  /**
   * Send a MUC message from some participant
   */
louiz’'s avatar
louiz’ committed
165
  void send_message(const Iid& iid, const std::string& nick, const std::string& body, const bool muc);
166
  /**
167
   * Send a presence of type error, from a room.
168
   */
169
  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);
louiz’'s avatar
louiz’ committed
170 171 172
  /**
   * Send an unavailable presence from this participant
   */
173
  void send_muc_leave(const Iid& iid, const IrcUser& nick,
174 175
                      const std::string& message, const bool self,
                      const bool user_requested,
176 177
                      const std::string& resource,
                      const IrcClient* client);
louiz’'s avatar
louiz’ committed
178 179
  /**
   * Send presences to indicate that an user old_nick (ourself if self ==
180 181 182
   * 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.
louiz’'s avatar
louiz’ committed
183
   */
184 185 186 187 188
  void send_nick_change(Iid&& iid,
                        const std::string& old_nick,
                        const std::string& new_nick,
                        const char user_mode,
                        const bool self);
189 190
  void kick_muc_user(Iid&& iid, const std::string& target, const std::string& reason, const std::string& author,
                       const bool self);
191
  void send_nickname_conflict_error(const Iid& iid, const std::string& nickname);
192 193 194 195
  /**
   * 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);
196
  /**
louiz’'s avatar
louiz’ committed
197
   * Send an iq version request coming from nick!hostname@
198 199
   */
  void send_iq_version_request(const std::string& nick, const std::string& hostname);
200 201 202 203 204
  /**
   * 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);
louiz’'s avatar
louiz’ committed
205
  void send_xmpp_invitation(const Iid& iid, const std::string& author);
206 207
  void on_irc_client_connected(const std::string& hostname);
  void on_irc_client_disconnected(const std::string& hostname);
louiz’'s avatar
louiz’ committed
208

louiz’'s avatar
louiz’ committed
209 210 211 212
  /**
   * Misc
   */
  std::string get_own_nick(const Iid& iid);
louiz’'s avatar
louiz’ committed
213
  /**
214
   * Get the number of server to which this bridge is connected or connecting.
louiz’'s avatar
louiz’ committed
215
   */
216
  size_t active_clients() const;
217 218 219 220 221 222 223 224
  /**
   * 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);
225 226 227 228 229
  /**
   * 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);
230
  /**
231
   * Add a callback to the waiting list of irc callbacks.
232
   */
233
  void add_waiting_irc(irc_responder_callback_t&& callback);
234 235 236 237 238
  /**
   * 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.
   */
239
  void trigger_on_irc_message(const std::string& irc_hostname, const IrcMessage& message);
240
  std::unordered_map<std::string, std::shared_ptr<IrcClient>>& get_irc_clients();
louiz’'s avatar
louiz’ committed
241
  const std::unordered_map<std::string, std::shared_ptr<IrcClient>>& get_irc_clients() const;
242
  std::set<char> get_chantypes(const std::string& hostname) const;
243 244 245
#ifdef USE_DATABASE
  void set_record_history(const bool val);
#endif
louiz’'s avatar
louiz’ committed
246

247 248 249 250 251 252
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.
   */
253
  IrcClient* make_irc_client(const std::string& hostname, const std::string& nickname);
louiz’'s avatar
louiz’ committed
254
  /**
255 256
   * This version does not create the IrcClient if it does not exist, throws
   * a IRCServerNotConnected error in that case.
louiz’'s avatar
louiz’ committed
257 258
   */
  IrcClient* get_irc_client(const std::string& hostname);
259
public:
260 261 262
  /**
   * Idem, but returns nullptr if the server does not exist.
   */
263
  IrcClient* find_irc_client(const std::string& hostname) const;
264
private:
265
  /**
266
   * The bare JID of the user associated with this bridge. Messages from/to this
267 268
   * JID are only managed by this bridge.
   */
269
  const std::string user_jid;
270 271 272 273 274 275
  /**
   * 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;
  /**
276
   * To communicate back with the XMPP component
277
   */
278
  BiboumiComponent& xmpp;
279 280 281 282
  /**
   * Poller, to give it the IrcClients that we spawn, to make it manage
   * their sockets.
   */
283
  std::shared_ptr<Poller> poller;
284 285 286 287 288 289 290 291
  /**
   * 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;
292 293 294 295 296 297
  /**
   * 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.
   */
louiz’'s avatar
louiz’ committed
298
  std::vector<irc_responder_callback_t> waiting_irc;
299 300 301 302 303 304 305
  /**
   * Resources to IRC channel/server mapping:
   */
  using Resource = std::string;
  using ChannelName = std::string;
  using IrcHostname = std::string;
  using ChannelKey = std::tuple<ChannelName, IrcHostname>;
306
public:
307 308
  std::map<ChannelKey, std::set<Resource>> resources_in_chan;
  std::map<IrcHostname, std::set<Resource>> resources_in_server;
309
private:
310 311 312
  /**
   * Manage which resource is in which channel
   */
louiz’'s avatar
louiz’ committed
313 314 315 316 317
  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;
318 319 320 321

  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;
322
  size_t number_of_channels_the_resource_is_in(const std::string& irc_hostname, const std::string& resource) const;
323

324
  /**
325 326 327
   * 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
328
   */
329
  void generate_channel_join_for_resource(const Iid& iid, const std::string& resource);
330 331 332 333 334 335 336
  /**
   * 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;

337 338 339
#ifdef USE_DATABASE
  bool record_history { true };
#endif
340 341
};

342 343 344 345 346 347 348
struct IRCNotConnected: public std::exception
{
  IRCNotConnected(const std::string& hostname):
    hostname(hostname) {}
  const std::string hostname;
};

349