1 #pragma once
2 
3 #include <irc/irc_message.hpp>
4 #include <irc/irc_channel.hpp>
5 #include <irc/capability.hpp>
6 
7 #include "biboumi.h"
8 
9 #ifdef WITH_SASL
10 # include <irc/sasl.hpp>
11 #endif
12 #include <irc/iid.hpp>
13 
14 #include <bridge/history_limit.hpp>
15 
16 #include <network/tcp_client_socket_handler.hpp>
17 #include <network/resolver.hpp>
18 
19 #include <unordered_map>
20 #include <utility>
21 #include <memory>
22 #include <vector>
23 #include <string>
24 #include <stack>
25 #include <deque>
26 #include <map>
27 #include <set>
28 #include <utils/tokens_bucket.hpp>
29 
30 class IrcClient;
31 
32 using MessageCallback = std::function<void(const IrcClient*, const IrcMessage&)>;
33 
34 class Bridge;
35 
36 /**
37  * Represent one IRC client, i.e. an endpoint connected to a single IRC
38  * server, through a TCP socket, receiving and sending commands to it.
39  */
40 class IrcClient: public TCPClientSocketHandler
41 {
42 public:
43   explicit IrcClient(std::shared_ptr<Poller>& poller, std::string hostname,
44                      std::string nickname, std::string username,
45                      std::string realname, std::string user_hostname,
46                      Bridge& bridge);
47   ~IrcClient();
48 
49   IrcClient(const IrcClient&) = delete;
50   IrcClient(IrcClient&&) = delete;
51   IrcClient& operator=(const IrcClient&) = delete;
52   IrcClient& operator=(IrcClient&&) = delete;
53 
54   /**
55    * Connect to the IRC server
56    */
57   void start();
58   /**
59    * Called when the connection to the server cannot be established
60    */
61   void on_connection_failed(const std::string& reason) override final;
62   /**
63    * Called when successfully connected to the server
64    */
65   void on_connected() override final;
66   /**
67    * Close the connection, remove us from the poller
68    */
69   void on_connection_close(const std::string& error_msg) override final;
70   /**
71    * Parse the data we have received so far and try to get one or more
72    * complete messages from it.
73    */
74   void parse_in_buffer(const size_t) override final;
75 #ifdef BOTAN_FOUND
76   virtual bool abort_on_invalid_cert() const override final;
77 #endif
78   /**
79    * Return the channel with this name, create it if it does not yet exist
80    */
81   IrcChannel* get_channel(const std::string& name);
82   /**
83    * Return the channel with this name. Nullptr if it is not found
84    */
85    const IrcChannel* find_channel(const std::string& name) const;
86   /**
87    * Returns true if the channel is joined
88    */
89   bool is_channel_joined(const std::string& name);
90   /**
91    * Return our own nick
92    */
93   std::string get_own_nick() const;
94   /**
95    * Serialize the given message into a line, and send that into the socket
96    * (actually, into our out_buf and signal the poller that we want to wach
97    * for send events to be ready)
98    */
99   void send_message(IrcMessage message, MessageCallback callback={}, bool throttle=true);
100   void send_raw(const std::string& txt);
101   void actual_send(std::pair<IrcMessage, MessageCallback>&& message_pair);
102   /**
103    * Send the PONG irc command
104    */
105   void send_pong_command(const IrcMessage& message);
106   /**
107    * Do nothing when we receive a PONG command (but also do not log that no
108    * handler exist)
109    */
110   void on_pong(const IrcMessage& message);
111   void send_ping_command();
112   /**
113    * Send the USER irc command
114    */
115   void send_user_command(const std::string& username, const std::string& realname);
116   /**
117    * Send the NICK irc command
118    */
119   void send_nick_command(const std::string& username);
120   void send_pass_command(const std::string& password);
121   void send_webirc_command(const std::string& password, const std::string& user_ip);
122   /**
123    * Send the JOIN irc command.
124    */
125   void send_join_command(const std::string& chan_name, const std::string& password);
126   /**
127    * Send a PRIVMSG command for a channel
128    * Return true if the message was actually sent
129    */
130   bool send_channel_message(const std::string& chan_name, const std::string& body,
131                             MessageCallback callback);
132   /**
133    * Send a PRIVMSG command for an user
134    */
135   void send_private_message(const std::string& username, const std::string& body, const std::string& type);
136   /**
137    * Send the PART irc command
138    */
139   void send_part_command(const std::string& chan_name, const std::string& status_message);
140   /**
141    * Send the MODE irc command
142    */
143   void send_mode_command(const std::string& chan_name, const std::vector<std::string>& arguments);
144   /**
145    * Send the KICK irc command
146    */
147   void send_kick_command(const std::string& chan_name, const std::string& target, const std::string& reason);
148   /**
149    * Send the LIST irc command
150    */
151   void send_list_command();
152   void send_invitation(const std::string& chan_name, const std::string& nick);
153   void send_topic_command(const std::string& chan_name, const std::string& topic);
154   /**
155    * Send the QUIT irc command
156    */
157   void send_quit_command(const std::string& reason);
158   /**
159    * Send a message to the gateway user, not generated by the IRC server,
160    * but that might be useful because we want to be verbose (for example we
161    * might want to notify the user about the connexion state)
162    */
163   void send_gateway_message(const std::string& message, const std::string& from="");
164   /**
165    * Forward the server message received from IRC to the XMPP component
166    */
167   void forward_server_message(const IrcMessage& message);
168   /**
169    * When receiving the isupport informations.  See
170    * http://www.irc.org/tech_docs/draft-brocklesby-irc-isupport-03.txt
171    */
172   void on_isupport_message(const IrcMessage& message);
173   /**
174    * Does nothing yet. Isn’t that duplicating features from 005?
175    */
176   void on_server_myinfo(const IrcMessage& message);
177   /**
178    * Just empty the motd we kept as a string
179    */
180   void empty_motd(const IrcMessage& message);
181   /**
182    * Send the MOTD string as one single "big" message
183    */
184   void send_motd(const IrcMessage& message);
185   /**
186    * Append this line to the MOTD
187    */
188   void on_motd_line(const IrcMessage& message);
189   /**
190    * Forward the join of an other user into an IRC channel, and save the
191    * IrcUsers in the IrcChannel
192    */
193   void set_and_forward_user_list(const IrcMessage& message);
194   /**
195    * Signal the start of the LIST response. The RFC says its obsolete and
196    * “not used”, but I we receive it on some servers, so just ignore it.
197    */
198   void on_rpl_liststart(const IrcMessage& message);
199   /**
200    * A single LIST response line (one channel)
201    *
202    * The command is handled in a wait_irc callback. This general handler is
203    * empty and just used to avoid sending a message stanza for each received
204    * channel.
205    */
206   void on_rpl_list(const IrcMessage& message);
207   /**
208    * Signal the end of the LIST response, ignore.
209    */
210   void on_rpl_listend(const IrcMessage& message);
211   /**
212    * Remember our nick and host, when we are joined to the channel. The list
213    * of user comes after so we do not send the self-presence over XMPP yet.
214    */
215   void on_channel_join(const IrcMessage& message);
216   /**
217    * When a channel message is received
218    */
219   void on_channel_message(const IrcMessage& message);
220   /**
221    * A notice is received
222    */
223   void on_notice(const IrcMessage& message);
224   /**
225    * Save the topic in the IrcChannel
226    */
227   void on_topic_received(const IrcMessage& message);
228   /**
229    * Save the topic author in the IrcChannel
230    */
231   void on_topic_who_time_received(const IrcMessage& message);
232   /**
233    * Empty the topic
234    */
235   void on_empty_topic(const IrcMessage& message);
236   /**
237    * The IRC server is confirming that the invitation has been forwarded
238    */
239   void on_invited(const IrcMessage& message);
240   /**
241    *  The IRC server sends a CAP message, as part of capabilities negociation. It could be a ACK,
242    *  NACK, or something else
243    */
244   void on_cap(const IrcMessage& message);
245 private:
246   void cap_end();
247 public:
248 #ifdef WITH_SASL
249   void on_authenticate(const IrcMessage& message);
250   void on_sasl_login(const IrcMessage& message);
251   void on_sasl_success(const IrcMessage& message);
252   void on_sasl_failure(const IrcMessage& message);
253 #endif
254   /**
255    * The channel has been completely joined (self presence, topic, all names
256    * received etc), send the self presence and topic to the XMPP user.
257    */
258   void on_channel_completely_joined(const IrcMessage& message);
259   void on_banlist(const IrcMessage& message);
260   void on_banlist_end(const IrcMessage& message);
261   /**
262    * Save our own host, as reported by the server
263    */
264   void on_own_host_received(const IrcMessage& message);
265   /**
266    * We tried to set an invalid nickname
267    */
268   void on_erroneous_nickname(const IrcMessage& message);
269   /**
270    * When the IRC servers denies our nickname because of a conflict.  Send a
271    * presence conflict from all channels, because the name is server-wide.
272    */
273   void on_nickname_conflict(const IrcMessage& message);
274   /**
275    * Idem, but for when the user changes their nickname too quickly
276    */
277   void on_nickname_change_too_fast(const IrcMessage& message);
278   /**
279    * An error when we try to invite a user already in the channel
280    */
281   void on_useronchannel(const IrcMessage& message);
282   /**
283    * Handles most errors from the server by just forwarding the message to the user.
284    */
285   void on_generic_error(const IrcMessage& message);
286   /**
287    * When a message 001 is received, join the rooms we wanted to join, and set our actual nickname
288    */
289   void on_welcome_message(const IrcMessage& message);
290   void on_part(const IrcMessage& message);
291   void on_error(const IrcMessage& message);
292   void on_invite(const IrcMessage& message);
293   void on_nick(const IrcMessage& message);
294   void on_kick(const IrcMessage& message);
295   void on_mode(const IrcMessage& message);
296   void on_channel_bad_key(const IrcMessage& message);
297   /**
298    * A mode towards our own user is received (note, that is different from a
299    * channel mode towards or own nick, see
300    * http://tools.ietf.org/html/rfc2812#section-3.1.5 VS #section-3.2.3)
301    */
302   void on_user_mode(const IrcMessage& message);
303   /**
304    * A mode towards a channel. Note that this can change the mode of the
305    * channel itself or an IrcUser in it.
306    */
307   void on_channel_mode(const IrcMessage& message);
308   void on_quit(const IrcMessage& message);
309   void on_unknown_message(const IrcMessage& message);
310   /**
311    * Return the number of joined channels
312    */
313   size_t number_of_joined_channels() const;
314 
get_hostname() const315   const std::string& get_hostname() const { return this->hostname; }
get_nick() const316   std::string get_nick() const { return this->current_nick; }
is_welcomed() const317   bool is_welcomed() const { return this->welcomed; }
318 
get_resolver() const319   const Resolver& get_resolver() const { return this->dns_resolver; }
320 
get_sorted_user_modes() const321   const std::vector<char>& get_sorted_user_modes() const { return this->sorted_user_modes; }
322 
get_chantypes() const323   std::set<char> get_chantypes() const { return this->chantypes; }
324   void set_throttle_limit(long int limit);
325   /**
326    * Store the history limit that the client asked when joining this room.
327    */
328   HistoryLimit history_limit;
329 private:
330   /**
331    * The hostname of the server we are connected to.
332    */
333   const std::string hostname;
334   /**
335    * Our own host, as reported by the IRC server.
336    * By default (and if it is not overridden by the server), it is a
337    * meaningless string, with the maximum allowed size
338    */
339   std::string own_host{63, '*'};
340   /**
341    * The hostname of the user.  This is used in the USER and the WEBIRC
342    * commands, but only the one in WEBIRC will be used by the IRC server.
343    */
344   const std::string user_hostname;
345   /**
346    * The username used in the USER irc command
347    */
348   std::string username;
349   /**
350    * The realname used in the USER irc command
351    */
352   std::string realname;
353   /**
354    * Our current nickname on the server
355    */
356   std::string current_nick;
357   /**
358    * To communicate back with the bridge
359    */
360   Bridge& bridge;
361   /**
362    * Where messaged are stored when they are throttled.
363    */
364   std::deque<std::pair<IrcMessage, MessageCallback>> message_queue{};
365   /**
366    * The list of joined channels, indexed by name
367    */
368   std::unordered_map<std::string, std::unique_ptr<IrcChannel>> channels;
369   /**
370    * A list of chan we want to join (tuples with the channel name and the
371    * password, if any), but we need a response 001 from the server before
372    * sending the actual JOIN commands. So we just keep the channel names in
373    * a list, and send the JOIN commands for each of them whenever the
374    * WELCOME message is received.
375    */
376   std::vector<std::tuple<std::string, std::string>> channels_to_join;
377   /**
378    * This flag indicates that the server is completely joined (connection
379    * has been established, we are authentified and we have a nick)
380    */
381   bool welcomed;
382 #ifdef WITH_SASL
383   /**
384    * Whether or not we are trying to authenticate using sasl. If this is true we need to wait for a
385    * successful auth
386    */
387   SaslState sasl_state{SaslState::unneeded};
388 #endif
389   std::map<std::string, Capability> capabilities;
390   /**
391    * See http://www.irc.org/tech_docs/draft-brocklesby-irc-isupport-03.txt section 3.3
392    * We store the possible chanmodes in this object.
393    * chanmodes[0] contains modes of type A, [1] of type B etc
394    */
395   std::vector<std::string> chanmodes;
396   /**
397    * See http://www.irc.org/tech_docs/draft-brocklesby-irc-isupport-03.txt
398    * section 3.5
399    */
400   std::set<char> chantypes;
401   /**
402    * Each motd line received is appended to this string, which we send when
403    * the motd is completely received
404    */
405   std::string motd;
406   /**
407    * See http://www.irc.org/tech_docs/draft-brocklesby-irc-isupport-03.txt section 3.14
408    * The example given would be transformed into
409    * modes_to_prefix = {{'&', 'a'}, {'*', 'b'}}
410    */
411   std::map<char, char> prefix_to_mode;
412   /**
413    * Available user modes, sorted from most significant to least significant
414    * (for example 'ahov' is a common order).
415    */
416   std::vector<char> sorted_user_modes;
417   /**
418    * A list of ports to which we will try to connect, in reverse. Each port
419    * is associated with a boolean telling if we should use TLS or not if the
420    * connection succeeds on that port.
421    */
422   std::stack<std::pair<std::string, bool>> ports_to_try;
423   /**
424    * A set of (lowercase) nicknames to which we sent a private message.
425    */
426   std::set<std::string> nicks_to_treat_as_private;
427   /**
428    * DNS resolver, used to resolve the hostname of the user if we are using
429    * the WebIRC protocole.
430    */
431   Resolver dns_resolver;
432   TokensBucket tokens_bucket;
433   long int get_throttle_limit() const;
434 };
435 
436 
437