irc_client.hpp 12.3 KB
Newer Older
1 2
#pragma once

3

4
#include <irc/irc_message.hpp>
5 6
#include <irc/irc_channel.hpp>
#include <irc/iid.hpp>
louiz’'s avatar
louiz’ committed
7

8
#include <network/tcp_socket_handler.hpp>
louiz’'s avatar
louiz’ committed
9
#include <network/resolver.hpp>
10

11
#include <unordered_map>
12
#include <utility>
13
#include <memory>
louiz’'s avatar
louiz’ committed
14
#include <vector>
15
#include <string>
louiz’'s avatar
louiz’ committed
16
#include <stack>
louiz’'s avatar
louiz’ committed
17
#include <map>
18
#include <set>
19

20 21
class Bridge;

22 23 24 25
/**
 * Represent one IRC client, i.e. an endpoint connected to a single IRC
 * server, through a TCP socket, receiving and sending commands to it.
 */
26
class IrcClient: public TCPSocketHandler
27 28
{
public:
29 30
  explicit IrcClient(std::shared_ptr<Poller> poller, const std::string& hostname,
                     const std::string& nickname, const std::string& username,
louiz’'s avatar
louiz’ committed
31
                     const std::string& realname, const std::string& user_hostname,
32
                     Bridge& bridge);
33
  ~IrcClient();
louiz’'s avatar
louiz’ committed
34 35 36 37 38 39

  IrcClient(const IrcClient&) = delete;
  IrcClient(IrcClient&&) = delete;
  IrcClient& operator=(const IrcClient&) = delete;
  IrcClient& operator=(IrcClient&&) = delete;

40 41 42 43
  /**
   * Connect to the IRC server
   */
  void start();
44 45 46 47
  /**
   * Called when the connection to the server cannot be established
   */
  void on_connection_failed(const std::string& reason) override final;
louiz’'s avatar
louiz’ committed
48 49 50
  /**
   * Called when successfully connected to the server
   */
louiz’'s avatar
louiz’ committed
51
  void on_connected() override final;
52 53 54
  /**
   * Close the connection, remove us from the poller
   */
55
  void on_connection_close(const std::string& error) override final;
56 57 58 59
  /**
   * Parse the data we have received so far and try to get one or more
   * complete messages from it.
   */
60
  void parse_in_buffer(const size_t) override final;
61 62 63
#ifdef BOTAN_FOUND
  virtual bool abort_on_invalid_cert() const override final;
#endif
64 65 66 67
  /**
   * Return the channel with this name, create it if it does not yet exist
   */
  IrcChannel* get_channel(const std::string& name);
louiz’'s avatar
louiz’ committed
68 69 70 71
  /**
   * Returns true if the channel is joined
   */
  bool is_channel_joined(const std::string& name);
louiz’'s avatar
louiz’ committed
72 73 74 75
  /**
   * Return our own nick
   */
  std::string get_own_nick() const;
louiz’'s avatar
louiz’ committed
76 77 78 79 80 81
  /**
   * Serialize the given message into a line, and send that into the socket
   * (actually, into our out_buf and signal the poller that we want to wach
   * for send events to be ready)
   */
  void send_message(IrcMessage&& message);
louiz’'s avatar
louiz’ committed
82
  void send_raw(const std::string& txt);
83 84 85
  /**
   * Send the PONG irc command
   */
louiz’'s avatar
louiz’ committed
86
  void send_pong_command(const IrcMessage& message);
87 88 89 90 91
  /**
   * Do nothing when we receive a PONG command (but also do not log that no
   * handler exist)
   */
  void on_pong(const IrcMessage& message);
92
  void send_ping_command();
louiz’'s avatar
louiz’ committed
93 94 95 96 97 98 99 100
  /**
   * Send the USER irc command
   */
  void send_user_command(const std::string& username, const std::string& realname);
  /**
   * Send the NICK irc command
   */
  void send_nick_command(const std::string& username);
101
  void send_pass_command(const std::string& password);
louiz’'s avatar
louiz’ committed
102
  void send_webirc_command(const std::string& password, const std::string& user_ip);
louiz’'s avatar
louiz’ committed
103
  /**
louiz’'s avatar
louiz’ committed
104
   * Send the JOIN irc command.
louiz’'s avatar
louiz’ committed
105
   */
106
  void send_join_command(const std::string& chan_name, const std::string& password);
louiz’'s avatar
louiz’ committed
107 108 109 110 111
  /**
   * Send a PRIVMSG command for a channel
   * Return true if the message was actually sent
   */
  bool send_channel_message(const std::string& chan_name, const std::string& body);
louiz’'s avatar
louiz’ committed
112 113 114
  /**
   * Send a PRIVMSG command for an user
   */
louiz’'s avatar
louiz’ committed
115
  void send_private_message(const std::string& username, const std::string& body, const std::string& type);
louiz’'s avatar
louiz’ committed
116 117 118 119
  /**
   * Send the PART irc command
   */
  void send_part_command(const std::string& chan_name, const std::string& status_message);
louiz’'s avatar
louiz’ committed
120 121 122 123
  /**
   * Send the MODE irc command
   */
  void send_mode_command(const std::string& chan_name, const std::vector<std::string>& arguments);
124 125 126 127
  /**
   * Send the KICK irc command
   */
  void send_kick_command(const std::string& chan_name, const std::string& target, const std::string& reason);
128 129 130 131
  /**
   * Send the LIST irc command
   */
  void send_list_command();
132
  void send_topic_command(const std::string& chan_name, const std::string& topic);
louiz’'s avatar
louiz’ committed
133 134 135
  /**
   * Send the QUIT irc command
   */
136
  void send_quit_command(const std::string& reason);
137 138 139 140 141 142
  /**
   * Send a message to the gateway user, not generated by the IRC server,
   * but that might be useful because we want to be verbose (for example we
   * might want to notify the user about the connexion state)
   */
  void send_gateway_message(const std::string& message, const std::string& from="");
143 144 145 146
  /**
   * Forward the server message received from IRC to the XMPP component
   */
  void forward_server_message(const IrcMessage& message);
louiz’'s avatar
louiz’ committed
147 148 149 150 151
  /**
   * When receiving the isupport informations.  See
   * http://www.irc.org/tech_docs/draft-brocklesby-irc-isupport-03.txt
   */
  void on_isupport_message(const IrcMessage& message);
louiz’'s avatar
louiz’ committed
152 153 154 155
  /**
   * Does nothing yet. Isn’t that duplicating features from 005?
   */
  void on_server_myinfo(const IrcMessage& message);
156 157 158 159 160 161 162 163 164 165 166 167
  /**
   * Just empty the motd we kept as a string
   */
  void empty_motd(const IrcMessage& message);
  /**
   * Send the MOTD string as one single "big" message
   */
  void send_motd(const IrcMessage& message);
  /**
   * Append this line to the MOTD
   */
  void on_motd_line(const IrcMessage& message);
168 169 170 171 172
  /**
   * Forward the join of an other user into an IRC channel, and save the
   * IrcUsers in the IrcChannel
   */
  void set_and_forward_user_list(const IrcMessage& message);
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
  /**
   * Signal the start of the LIST response. The RFC says its obsolete and
   * “not used”, but I we receive it on some servers, so just ignore it.
   */
  void on_rpl_liststart(const IrcMessage& message);
  /**
   * A single LIST response line (one channel)
   *
   * The command is handled in a wait_irc callback. This general handler is
   * empty and just used to avoid sending a message stanza for each received
   * channel.
   */
  void on_rpl_list(const IrcMessage& message);
  /**
   * Signal the end of the LIST response, ignore.
   */
  void on_rpl_listend(const IrcMessage& message);
190 191 192 193
  /**
   * Remember our nick and host, when we are joined to the channel. The list
   * of user comes after so we do not send the self-presence over XMPP yet.
   */
louiz’'s avatar
louiz’ committed
194
  void on_channel_join(const IrcMessage& message);
louiz’'s avatar
louiz’ committed
195 196 197 198
  /**
   * When a channel message is received
   */
  void on_channel_message(const IrcMessage& message);
199 200 201 202
  /**
   * A notice is received
   */
  void on_notice(const IrcMessage& message);
203 204 205 206
  /**
   * Save the topic in the IrcChannel
   */
  void on_topic_received(const IrcMessage& message);
207 208 209 210
  /**
   * Save the topic author in the IrcChannel
   */
  void on_topic_who_time_received(const IrcMessage& message);
louiz’'s avatar
louiz’ committed
211 212 213 214
  /**
   * Empty the topic
   */
  void on_empty_topic(const IrcMessage& message);
215 216 217 218 219
  /**
   * The channel has been completely joined (self presence, topic, all names
   * received etc), send the self presence and topic to the XMPP user.
   */
  void on_channel_completely_joined(const IrcMessage& message);
220 221 222 223
  /**
   * Save our own host, as reported by the server
   */
  void on_own_host_received(const IrcMessage& message);
224 225 226 227
  /**
   * We tried to set an invalid nickname
   */
  void on_erroneous_nickname(const IrcMessage& message);
228 229 230 231 232
  /**
   * When the IRC servers denies our nickname because of a conflict.  Send a
   * presence conflict from all channels, because the name is server-wide.
   */
  void on_nickname_conflict(const IrcMessage& message);
233 234 235 236
  /**
   * Idem, but for when the user changes their nickname too quickly
   */
  void on_nickname_change_too_fast(const IrcMessage& message);
237 238 239 240
  /**
   * Handles most errors from the server by just forwarding the message to the user.
   */
  void on_generic_error(const IrcMessage& message);
louiz’'s avatar
louiz’ committed
241 242 243 244
  /**
   * When a message 001 is received, join the rooms we wanted to join, and set our actual nickname
   */
  void on_welcome_message(const IrcMessage& message);
louiz’'s avatar
louiz’ committed
245
  void on_part(const IrcMessage& message);
louiz’'s avatar
louiz’ committed
246
  void on_error(const IrcMessage& message);
louiz’'s avatar
louiz’ committed
247
  void on_nick(const IrcMessage& message);
248
  void on_kick(const IrcMessage& message);
louiz’'s avatar
louiz’ committed
249
  void on_mode(const IrcMessage& message);
louiz’'s avatar
louiz’ committed
250
  /**
louiz’'s avatar
louiz’ committed
251 252 253
   * A mode towards our own user is received (note, that is different from a
   * channel mode towards or own nick, see
   * http://tools.ietf.org/html/rfc2812#section-3.1.5 VS #section-3.2.3)
louiz’'s avatar
louiz’ committed
254
   */
louiz’'s avatar
louiz’ committed
255
  void on_user_mode(const IrcMessage& message);
louiz’'s avatar
louiz’ committed
256
  /**
louiz’'s avatar
louiz’ committed
257 258
   * A mode towards a channel. Note that this can change the mode of the
   * channel itself or an IrcUser in it.
louiz’'s avatar
louiz’ committed
259
   */
louiz’'s avatar
louiz’ committed
260
  void on_channel_mode(const IrcMessage& message);
louiz’'s avatar
louiz’ committed
261
  void on_quit(const IrcMessage& message);
262
  void on_unknown_message(const IrcMessage& message);
263 264 265 266
  /**
   * Return the number of joined channels
   */
  size_t number_of_joined_channels() const;
louiz’'s avatar
louiz’ committed
267 268 269 270
  /**
   * Get a reference to the unique dummy channel
   */
  DummyIrcChannel& get_dummy_channel();
louiz’'s avatar
louiz’ committed
271 272 273 274 275
  /**
   * Leave the dummy channel: forward a message to the user to indicate that
   * he left it, and mark it as not joined.
   */
  void leave_dummy_channel(const std::string& exit_message);
louiz’'s avatar
louiz’ committed
276 277 278 279

  const std::string& get_hostname() const { return this->hostname; }
  std::string get_nick() const { return this->current_nick; }
  bool is_welcomed() const { return this->welcomed; }
280

louiz’'s avatar
louiz’ committed
281
  const Resolver& get_resolver() const { return this->dns_resolver; }
louiz’'s avatar
louiz’ committed
282

283
  const std::vector<char>& get_sorted_user_modes() const { return this->sorted_user_modes; }
284

285
  std::set<char> get_chantypes() const { return this->chantypes; }
286
private:
287 288 289 290
  /**
   * The hostname of the server we are connected to.
   */
  const std::string hostname;
291 292 293 294 295 296
  /**
   * Our own host, as reported by the IRC server.
   * By default (and if it is not overridden by the server), it is a
   * meaningless string, with the maximum allowed size
   */
  std::string own_host{63, '*'};
louiz’'s avatar
louiz’ committed
297 298 299 300 301
  /**
   * The hostname of the user.  This is used in the USER and the WEBIRC
   * commands, but only the one in WEBIRC will be used by the IRC server.
   */
  const std::string user_hostname;
302
  /**
303
   * The username used in the USER irc command
304
   */
305 306 307 308 309
  std::string username;
  /**
   * The realname used in the USER irc command
   */
  std::string realname;
louiz’'s avatar
louiz’ committed
310 311 312 313
  /**
   * Our current nickname on the server
   */
  std::string current_nick;
314
  /**
315
   * To communicate back with the bridge
316
   */
317
  Bridge& bridge;
318 319 320 321
  /**
   * The list of joined channels, indexed by name
   */
  std::unordered_map<std::string, std::unique_ptr<IrcChannel>> channels;
louiz’'s avatar
louiz’ committed
322 323 324 325 326
  /**
   * A single channel with a iid of the form "hostname" (normal channel have
   * an iid of the form "chan%hostname".
   */
  DummyIrcChannel dummy_channel;
louiz’'s avatar
louiz’ committed
327
  /**
328 329 330 331 332
   * A list of chan we want to join (tuples with the channel name and the
   * password, if any), but we need a response 001 from the server before
   * sending the actual JOIN commands. So we just keep the channel names in
   * a list, and send the JOIN commands for each of them whenever the
   * WELCOME message is received.
louiz’'s avatar
louiz’ committed
333
   */
334
  std::vector<std::tuple<std::string, std::string>> channels_to_join;
louiz’'s avatar
louiz’ committed
335 336 337 338
  /**
   * This flag indicates that the server is completely joined (connection
   * has been established, we are authentified and we have a nick)
   */
louiz’'s avatar
louiz’ committed
339
  bool welcomed;
louiz’'s avatar
louiz’ committed
340 341 342 343 344 345
  /**
   * See http://www.irc.org/tech_docs/draft-brocklesby-irc-isupport-03.txt section 3.3
   * We store the possible chanmodes in this object.
   * chanmodes[0] contains modes of type A, [1] of type B etc
   */
  std::vector<std::string> chanmodes;
346 347 348 349 350
  /**
   * See http://www.irc.org/tech_docs/draft-brocklesby-irc-isupport-03.txt
   * section 3.5
   */
  std::set<char> chantypes;
351 352 353 354 355
  /**
   * Each motd line received is appended to this string, which we send when
   * the motd is completely received
   */
  std::string motd;
louiz’'s avatar
louiz’ committed
356 357 358
  /**
   * See http://www.irc.org/tech_docs/draft-brocklesby-irc-isupport-03.txt section 3.14
   * The example given would be transformed into
359
   * modes_to_prefix = {{'&', 'a'}, {'*', 'b'}}
louiz’'s avatar
louiz’ committed
360 361
   */
  std::map<char, char> prefix_to_mode;
362 363 364 365 366
  /**
   * Available user modes, sorted from most significant to least significant
   * (for example 'ahov' is a common order).
   */
  std::vector<char> sorted_user_modes;
louiz’'s avatar
louiz’ committed
367 368 369 370 371 372
  /**
   * A list of ports to which we will try to connect, in reverse. Each port
   * is associated with a boolean telling if we should use TLS or not if the
   * connection succeeds on that port.
   */
  std::stack<std::pair<std::string, bool>> ports_to_try;
373 374 375 376
  /**
   * A set of (lowercase) nicknames to which we sent a private message.
   */
  std::set<std::string> nicks_to_treat_as_private;
louiz’'s avatar
louiz’ committed
377 378 379 380 381
  /**
   * DNS resolver, used to resolve the hostname of the user if we are using
   * the WebIRC protocole.
   */
  Resolver dns_resolver;
382 383
};

384