1 #include <utility>
2 #include <utils/timed_events.hpp>
3 #include <database/database.hpp>
4 #include <irc/irc_message.hpp>
5 #include <irc/irc_client.hpp>
6 #include <bridge/bridge.hpp>
7 #include <irc/irc_user.hpp>
8 #include <utils/base64.hpp>
9 
10 #include <logger/logger.hpp>
11 #include <config/config.hpp>
12 #include <utils/tolower.hpp>
13 #include <utils/split.hpp>
14 #include <utils/string.hpp>
15 
16 #include <sstream>
17 #include <iostream>
18 #include <stdexcept>
19 #include <algorithm>
20 #include <cstring>
21 
22 #include <chrono>
23 #include <string>
24 
25 #include "biboumi.h"
26 
27 using namespace std::string_literals;
28 using namespace std::chrono_literals;
29 
30 /**
31  * Define a map of functions to be called for each IRC command we can
32  * handle.
33  */
34 using IrcCallback = void (IrcClient::*)(const IrcMessage&);
35 
36 static const std::unordered_map<std::string,
37                                 std::pair<IrcCallback, std::pair<std::size_t, std::size_t>>> irc_callbacks = {
38   {"NOTICE", {&IrcClient::on_notice, {2, 0}}},
39   {"002", {&IrcClient::forward_server_message, {2, 0}}},
40   {"003", {&IrcClient::forward_server_message, {2, 0}}},
41   {"004", {&IrcClient::on_server_myinfo, {4, 0}}},
42   {"005", {&IrcClient::on_isupport_message, {0, 0}}},
43   {"RPL_LISTSTART", {&IrcClient::on_rpl_liststart, {0, 0}}},
44   {"321", {&IrcClient::on_rpl_liststart, {0, 0}}},
45   {"RPL_LIST", {&IrcClient::on_rpl_list, {0, 0}}},
46   {"322", {&IrcClient::on_rpl_list, {0, 0}}},
47   {"RPL_LISTEND", {&IrcClient::on_rpl_listend, {0, 0}}},
48   {"323", {&IrcClient::on_rpl_listend, {0, 0}}},
49   {"RPL_NOTOPIC", {&IrcClient::on_empty_topic, {0, 0}}},
50   {"331", {&IrcClient::on_empty_topic, {0, 0}}},
51   {"341", {&IrcClient::on_invited, {3, 0}}},
52   {"RPL_MOTDSTART", {&IrcClient::empty_motd, {0, 0}}},
53   {"375", {&IrcClient::empty_motd, {0, 0}}},
54   {"RPL_MOTD", {&IrcClient::on_motd_line, {2, 0}}},
55   {"372", {&IrcClient::on_motd_line, {2, 0}}},
56   {"RPL_MOTDEND", {&IrcClient::send_motd, {0, 0}}},
57   {"376", {&IrcClient::send_motd, {0, 0}}},
58   {"JOIN", {&IrcClient::on_channel_join, {1, 0}}},
59   {"PRIVMSG", {&IrcClient::on_channel_message, {2, 0}}},
60   {"353", {&IrcClient::set_and_forward_user_list, {4, 0}}},
61   {"332", {&IrcClient::on_topic_received, {2, 0}}},
62   {"TOPIC", {&IrcClient::on_topic_received, {2, 0}}},
63   {"333", {&IrcClient::on_topic_who_time_received, {4, 0}}},
64   {"RPL_TOPICWHOTIME", {&IrcClient::on_topic_who_time_received, {4, 0}}},
65   {"366", {&IrcClient::on_channel_completely_joined, {2, 0}}},
66   {"367", {&IrcClient::on_banlist, {3, 0}}},
67   {"368", {&IrcClient::on_banlist_end, {3, 0}}},
68   {"396", {&IrcClient::on_own_host_received, {2, 0}}},
69   {"432", {&IrcClient::on_erroneous_nickname, {2, 0}}},
70   {"433", {&IrcClient::on_nickname_conflict, {2, 0}}},
71   {"438", {&IrcClient::on_nickname_change_too_fast, {2, 0}}},
72   {"443", {&IrcClient::on_useronchannel, {3, 0}}},
73   {"475", {&IrcClient::on_channel_bad_key, {3, 0}}},
74   {"ERR_USERONCHANNEL", {&IrcClient::on_useronchannel, {3, 0}}},
75   {"001", {&IrcClient::on_welcome_message, {1, 0}}},
76   {"PART", {&IrcClient::on_part, {1, 0}}},
77   {"ERROR", {&IrcClient::on_error, {1, 0}}},
78   {"QUIT", {&IrcClient::on_quit, {0, 0}}},
79   {"NICK", {&IrcClient::on_nick, {1, 0}}},
80   {"MODE", {&IrcClient::on_mode, {1, 0}}},
81   {"PING", {&IrcClient::send_pong_command, {1, 0}}},
82   {"PONG", {&IrcClient::on_pong, {0, 0}}},
83   {"KICK", {&IrcClient::on_kick, {3, 0}}},
84   {"INVITE", {&IrcClient::on_invite, {2, 0}}},
85   {"CAP", {&IrcClient::on_cap, {3, 0}}},
86 #ifdef WITH_SASL
87   {"AUTHENTICATE", {&IrcClient::on_authenticate, {1, 0}}},
88   {"900", {&IrcClient::on_sasl_login, {3, 0}}},
89   {"902", {&IrcClient::on_sasl_failure, {2, 0}}},
90   {"903", {&IrcClient::on_sasl_success, {0, 0}}},
91   {"904", {&IrcClient::on_sasl_failure, {2, 0}}},
92   {"905", {&IrcClient::on_sasl_failure, {2, 0}}},
93   {"906", {&IrcClient::on_sasl_failure, {2, 0}}},
94   {"907", {&IrcClient::on_sasl_failure, {2, 0}}},
95   {"908", {&IrcClient::on_sasl_failure, {2, 0}}},
96 #endif
97   {"401", {&IrcClient::on_generic_error, {2, 0}}},
98   {"402", {&IrcClient::on_generic_error, {2, 0}}},
99   {"403", {&IrcClient::on_generic_error, {2, 0}}},
100   {"404", {&IrcClient::on_generic_error, {2, 0}}},
101   {"405", {&IrcClient::on_generic_error, {2, 0}}},
102   {"406", {&IrcClient::on_generic_error, {2, 0}}},
103   {"407", {&IrcClient::on_generic_error, {2, 0}}},
104   {"408", {&IrcClient::on_generic_error, {2, 0}}},
105   {"409", {&IrcClient::on_generic_error, {2, 0}}},
106   {"410", {&IrcClient::on_generic_error, {2, 0}}},
107   {"411", {&IrcClient::on_generic_error, {2, 0}}},
108   {"412", {&IrcClient::on_generic_error, {2, 0}}},
109   {"414", {&IrcClient::on_generic_error, {2, 0}}},
110   {"421", {&IrcClient::on_generic_error, {2, 0}}},
111   {"422", {&IrcClient::on_generic_error, {2, 0}}},
112   {"423", {&IrcClient::on_generic_error, {2, 0}}},
113   {"424", {&IrcClient::on_generic_error, {2, 0}}},
114   {"431", {&IrcClient::on_generic_error, {2, 0}}},
115   {"436", {&IrcClient::on_generic_error, {2, 0}}},
116   {"441", {&IrcClient::on_generic_error, {2, 0}}},
117   {"442", {&IrcClient::on_generic_error, {2, 0}}},
118   {"444", {&IrcClient::on_generic_error, {2, 0}}},
119   {"446", {&IrcClient::on_generic_error, {2, 0}}},
120   {"451", {&IrcClient::on_generic_error, {2, 0}}},
121   {"461", {&IrcClient::on_generic_error, {2, 0}}},
122   {"462", {&IrcClient::on_generic_error, {2, 0}}},
123   {"463", {&IrcClient::on_generic_error, {2, 0}}},
124   {"464", {&IrcClient::on_generic_error, {2, 0}}},
125   {"465", {&IrcClient::on_generic_error, {2, 0}}},
126   {"467", {&IrcClient::on_generic_error, {2, 0}}},
127   {"470", {&IrcClient::on_generic_error, {2, 0}}},
128   {"471", {&IrcClient::on_generic_error, {2, 0}}},
129   {"472", {&IrcClient::on_generic_error, {2, 0}}},
130   {"473", {&IrcClient::on_generic_error, {2, 0}}},
131   {"474", {&IrcClient::on_generic_error, {2, 0}}},
132   {"476", {&IrcClient::on_generic_error, {2, 0}}},
133   {"477", {&IrcClient::on_generic_error, {2, 0}}},
134   {"481", {&IrcClient::on_generic_error, {2, 0}}},
135   {"482", {&IrcClient::on_generic_error, {2, 0}}},
136   {"483", {&IrcClient::on_generic_error, {2, 0}}},
137   {"484", {&IrcClient::on_generic_error, {2, 0}}},
138   {"485", {&IrcClient::on_generic_error, {2, 0}}},
139   {"487", {&IrcClient::on_generic_error, {2, 0}}},
140   {"491", {&IrcClient::on_generic_error, {2, 0}}},
141   {"501", {&IrcClient::on_generic_error, {2, 0}}},
142   {"502", {&IrcClient::on_generic_error, {2, 0}}},
143 };
144 
IrcClient(std::shared_ptr<Poller> & poller,std::string hostname,std::string nickname,std::string username,std::string realname,std::string user_hostname,Bridge & bridge)145 IrcClient::IrcClient(std::shared_ptr<Poller>& poller, std::string hostname,
146                      std::string nickname, std::string username,
147                      std::string realname, std::string user_hostname,
148                      Bridge& bridge):
149   TCPClientSocketHandler(poller),
150   hostname(hostname),
151   user_hostname(std::move(user_hostname)),
152   username(std::move(username)),
153   realname(std::move(realname)),
154   current_nick(std::move(nickname)),
155   bridge(bridge),
156   welcomed(false),
157   chanmodes({"", "", "", ""}),
158   chantypes({'#', '&'}),
__anon56f2db640102() 159   tokens_bucket(this->get_throttle_limit(), 1s, [this]() {
160     if (message_queue.empty())
161       return true;
162     this->actual_send(std::move(this->message_queue.front()));
163     this->message_queue.pop_front();
164     return false;
165   }, "TokensBucket" + this->hostname + this->bridge.get_jid())
166 {
167 #ifdef USE_DATABASE
168   auto options = Database::get_irc_server_options(this->bridge.get_bare_jid(),
169                                                   this->get_hostname());
170   std::vector<std::string> ports = utils::split(options.col<Database::Ports>(), ';', false);
171   for (auto it = ports.rbegin(); it != ports.rend(); ++it)
172     this->ports_to_try.emplace(*it, false);
173 # ifdef BOTAN_FOUND
174   ports = utils::split(options.col<Database::TlsPorts>(), ';', false);
175   for (auto it = ports.rbegin(); it != ports.rend(); ++it)
176     this->ports_to_try.emplace(*it, true);
177 # endif // BOTAN_FOUND
178 
179 #else  // not USE_DATABASE
180   this->ports_to_try.emplace("6667", false); // standard non-encrypted port
181 # ifdef BOTAN_FOUND
182   this->ports_to_try.emplace("6670", true);  // non-standard but I want it for some servers
183   this->ports_to_try.emplace("6697", true);  // standard encrypted port
184 # endif // BOTAN_FOUND
185 #endif // USE_DATABASE
186 }
187 
~IrcClient()188 IrcClient::~IrcClient()
189 {
190   // This event may or may not exist (if we never got connected, it
191   // doesn't), but it's ok
192   TimedEventsManager::instance().cancel("PING" + this->hostname + this->bridge.get_jid());
193   TimedEventsManager::instance().cancel("TokensBucket" + this->hostname + this->bridge.get_jid());
194 }
195 
start()196 void IrcClient::start()
197 {
198   if (this->is_connecting() || this->is_connected())
199     return;
200   if (this->ports_to_try.empty())
201     {
202       this->bridge.send_xmpp_message(this->hostname, "", "Can not connect to IRC server: no port specified.");
203       return;
204     }
205   std::string port;
206   bool tls;
207   std::tie(port, tls) = this->ports_to_try.top();
208   this->ports_to_try.pop();
209   this->bind_addr = Config::get("outgoing_bind", "");
210   std::string address = this->hostname;
211 
212 #ifdef USE_DATABASE
213   auto options = Database::get_irc_server_options(this->bridge.get_bare_jid(),
214                                                   this->get_hostname());
215 # ifdef BOTAN_FOUND
216   this->credential_manager.set_trusted_fingerprint(options.col<Database::TrustedFingerprint>());
217 # endif
218   if (Config::get("fixed_irc_server", "").empty() &&
219       !options.col<Database::Address>().empty())
220     address = options.col<Database::Address>();
221 #endif
222   this->bridge.send_xmpp_message(this->hostname, "", "Connecting to " +
223                                   address + ":" + port + " (" +
224                                   (tls ? "encrypted" : "not encrypted") + ")");
225   this->connect(address, port, tls);
226 }
227 
on_connection_failed(const std::string & reason)228 void IrcClient::on_connection_failed(const std::string& reason)
229 {
230   this->bridge.send_xmpp_message(this->hostname, "",
231                                   "Connection failed: " + reason);
232 
233   if (this->hostname_resolution_failed)
234     while (!this->ports_to_try.empty())
235       this->ports_to_try.pop();
236 
237   if (this->ports_to_try.empty())
238     {
239       // Send an error message for all room that the user wanted to join
240       for (const auto& tuple: this->channels_to_join)
241         {
242           Iid iid(std::get<0>(tuple) + "%" + this->hostname, this->chantypes);
243           this->bridge.send_presence_error(iid, this->current_nick,
244                                             "cancel", "item-not-found",
245                                             "", reason);
246         }
247       this->channels_to_join.clear();
248     }
249   else                          // try the next port
250     this->start();
251 }
252 
on_connected()253 void IrcClient::on_connected()
254 {
255   const auto webirc_password = Config::get("webirc_password", "");
256   static std::string resolved_ip;
257 
258   if (!webirc_password.empty())
259     {
260       if (!resolved_ip.empty())
261         this->send_webirc_command(webirc_password, resolved_ip);
262       else
263         {  // Start resolving the hostname of the user, and call
264            // on_connected again when it’s done
265           this->dns_resolver.resolve(this->user_hostname, "5222",
266                                      [this](const struct addrinfo* addr)
267                                      {
268                                        resolved_ip = addr_to_string(addr);
269                                        // Only continue the process if we
270                                        // didn’t get connected while we were
271                                        // resolving
272                                        if (this->is_connected())
273                                          this->on_connected();
274                                      },
275                                      [this](const char* error_msg)
276                                      {
277                                        if (this->is_connected())
278                                          {
279                                            this->on_connection_close("Could not resolve hostname " + this->user_hostname +
280                                                                      ": " + error_msg);
281                                            this->send_quit_command("");
282                                          }
283                                      });
284           return;
285         }
286     }
287 
288   this->send_gateway_message("Connected to IRC server"s + (this->use_tls ? " (encrypted)": "") + ".");
289 
290   this->capabilities["multi-prefix"] = {[]{}, []{}};
291 
292 #ifdef USE_DATABASE
293   auto options = Database::get_irc_server_options(this->bridge.get_bare_jid(),
294                                                   this->get_hostname());
295 
296   const auto& server_password = options.col<Database::Pass>();
297 
298   if (!server_password.empty())
299     this->send_pass_command(options.col<Database::Pass>());
300 #endif
301 
302 #ifdef WITH_SASL
303   const auto& sasl_password = options.col<Database::SaslPassword>();
304   if (!sasl_password.empty())
305     {
306       this->capabilities["sasl"] = {
307           [this]
308           {
309             this->send_message({"AUTHENTICATE", {"PLAIN"}});
310             log_warning("negociating SASL now...");
311           },
312           []
313           {
314             log_warning("SASL not supported by the server, disconnecting.");
315           }
316       };
317       this->sasl_state = SaslState::needed;
318     }
319 #endif
320 
321   {
322     for (const auto &pair : this->capabilities)
323       this->send_message({ "CAP", {"REQ", pair.first}});
324   }
325 
326   this->send_nick_command(this->current_nick);
327 #ifdef USE_DATABASE
328   if (Config::get("realname_customization", "true") == "true")
329     {
330       if (!options.col<Database::Username>().empty())
331         this->username = options.col<Database::Username>();
332       if (!options.col<Database::Realname>().empty())
333         this->realname = options.col<Database::Realname>();
334       this->send_user_command(username, realname);
335     }
336   else
337 #endif
338   this->send_user_command(this->username, this->realname);
339 }
340 
on_connection_close(const std::string & error_msg)341 void IrcClient::on_connection_close(const std::string& error_msg)
342 {
343   std::string message = "Connection closed";
344   if (!error_msg.empty())
345     message += ": " + error_msg;
346   else
347     message += ".";
348   const IrcMessage error{"ERROR", {message}};
349   this->on_error(error);
350   log_warning(message);
351   this->bridge.on_irc_client_disconnected(this->get_hostname());
352 }
353 
get_channel(const std::string & n)354 IrcChannel* IrcClient::get_channel(const std::string& n)
355 {
356   const std::string name = utils::tolower(n);
357   try
358     {
359       return this->channels.at(name).get();
360     }
361   catch (const std::out_of_range& exception)
362     {
363       return this->channels.emplace(name, std::make_unique<IrcChannel>()).first->second.get();
364     }
365 }
366 
find_channel(const std::string & n) const367 const IrcChannel* IrcClient::find_channel(const std::string& n) const
368 {
369   const std::string name = utils::tolower(n);
370   try
371     {
372       return this->channels.at(name).get();
373     }
374   catch (const std::out_of_range& exception)
375     {
376       return nullptr;
377     }
378 }
379 
is_channel_joined(const std::string & name)380 bool IrcClient::is_channel_joined(const std::string& name)
381 {
382   IrcChannel* channel = this->get_channel(name);
383   return channel->joined;
384 }
385 
get_own_nick() const386 std::string IrcClient::get_own_nick() const
387 {
388   return this->current_nick;
389 }
390 
parse_in_buffer(const size_t)391 void IrcClient::parse_in_buffer(const size_t)
392 {
393   while (true)
394     {
395       auto pos = this->in_buf.find("\r\n");
396       if (pos == std::string::npos)
397         break ;
398       IrcMessage message(this->in_buf.substr(0, pos));
399       this->consume_in_buffer(pos + 2);
400       log_debug("IRC RECEIVING: (", this->get_hostname(), ") ", message);
401 
402       // Call the standard callback (if any), associated with the command
403       // name that we just received.
404       auto it = irc_callbacks.find(message.command);
405       if (it != irc_callbacks.end())
406         {
407           const auto& limits = it->second.second;
408           // Check that the Message is well formed before actually calling
409           // the callback.
410           const auto args_size = message.arguments.size();
411           const auto min = limits.first;
412           const auto max = limits.second;
413           if (args_size < min ||
414               (max > 0 && args_size > max))
415             log_warning("Invalid number of arguments for IRC command “", message.command,
416                         "”: ", args_size);
417           else
418             {
419               const auto& cb = it->second.first;
420               try {
421                 (this->*(cb))(message);
422               } catch (const std::exception& e) {
423                 log_error("Unhandled exception: ", e.what());
424               }
425             }
426         }
427       else
428         {
429           log_info("No handler for command ", message.command,
430                    ", forwarding the arguments to the user");
431           this->on_unknown_message(message);
432         }
433       // Try to find a waiting_iq, which response will be triggered by this IrcMessage
434       this->bridge.trigger_on_irc_message(this->hostname, message);
435     }
436 }
437 
actual_send(std::pair<IrcMessage,MessageCallback> && message_pair)438 void IrcClient::actual_send(std::pair<IrcMessage, MessageCallback>&& message_pair)
439 {
440   const IrcMessage& message = message_pair.first;
441   const MessageCallback& callback = message_pair.second;
442    log_debug("IRC SENDING: (", this->get_hostname(), ") ", message);
443     std::string res;
444     if (!message.prefix.empty())
445       res += ":" + message.prefix + " ";
446     res += message.command;
447     for (const std::string& arg: message.arguments)
448       {
449         if (arg.find(' ') != std::string::npos
450             || (!arg.empty() && arg[0] == ':'))
451           {
452             res += " :" + arg;
453             break;
454           }
455         res += " " + arg;
456       }
457     res += "\r\n";
458     this->send_data(std::move(res));
459 
460     if (callback)
461       callback(this, message);
462  }
463 
send_message(IrcMessage message,MessageCallback callback,bool throttle)464 void IrcClient::send_message(IrcMessage message, MessageCallback callback, bool throttle)
465 {
466   auto message_pair = std::make_pair(std::move(message), std::move(callback));
467   if (this->tokens_bucket.use_token() || !throttle)
468     this->actual_send(std::move(message_pair));
469   else
470     message_queue.push_back(std::move(message_pair));
471 }
472 
send_raw(const std::string & txt)473 void IrcClient::send_raw(const std::string& txt)
474 {
475   log_debug("IRC SENDING (raw): (", this->get_hostname(), ") ", txt);
476   this->send_data(txt + "\r\n");
477 }
478 
send_user_command(const std::string & username,const std::string & realname)479 void IrcClient::send_user_command(const std::string& username, const std::string& realname)
480 {
481   this->send_message(IrcMessage("USER", {username, this->user_hostname, "ignored", realname}));
482 }
483 
send_nick_command(const std::string & nick)484 void IrcClient::send_nick_command(const std::string& nick)
485 {
486   this->send_message(IrcMessage("NICK", {nick}));
487 }
488 
send_pass_command(const std::string & password)489 void IrcClient::send_pass_command(const std::string& password)
490 {
491   this->send_message(IrcMessage("PASS", {password}));
492 }
493 
send_webirc_command(const std::string & password,const std::string & user_ip)494 void IrcClient::send_webirc_command(const std::string& password, const std::string& user_ip)
495 {
496   this->send_message(IrcMessage("WEBIRC", {password, "biboumi", this->user_hostname, user_ip}));
497 }
498 
send_kick_command(const std::string & chan_name,const std::string & target,const std::string & reason)499 void IrcClient::send_kick_command(const std::string& chan_name, const std::string& target, const std::string& reason)
500 {
501   this->send_message(IrcMessage("KICK", {chan_name, target, reason}));
502 }
503 
send_list_command()504 void IrcClient::send_list_command()
505 {
506   this->send_message(IrcMessage("LIST", {"*"}));
507 }
508 
send_invitation(const std::string & chan_name,const std::string & nick)509 void IrcClient::send_invitation(const std::string& chan_name, const std::string& nick)
510 {
511   this->send_message(IrcMessage("INVITE", {nick, chan_name}));
512 }
513 
send_topic_command(const std::string & chan_name,const std::string & topic)514 void IrcClient::send_topic_command(const std::string& chan_name, const std::string& topic)
515 {
516   this->send_message(IrcMessage("TOPIC", {chan_name, topic}));
517 }
518 
send_quit_command(const std::string & reason)519 void IrcClient::send_quit_command(const std::string& reason)
520 {
521   this->send_message(IrcMessage("QUIT", {reason}), {}, false);
522 }
523 
send_join_command(const std::string & chan_name,const std::string & password)524 void IrcClient::send_join_command(const std::string& chan_name, const std::string& password)
525 {
526   if (!this->welcomed)
527     {
528       const auto it = std::find_if(begin(this->channels_to_join), end(this->channels_to_join),
529                                    [&chan_name](const auto& pair) { return std::get<0>(pair) == chan_name; });
530       if (it == end(this->channels_to_join))
531         this->channels_to_join.emplace_back(chan_name, password);
532     }
533   else if (password.empty())
534     this->send_message(IrcMessage("JOIN", {chan_name}));
535   else
536     this->send_message(IrcMessage("JOIN", {chan_name, password}));
537   this->start();
538 }
539 
send_channel_message(const std::string & chan_name,const std::string & body,MessageCallback callback)540 bool IrcClient::send_channel_message(const std::string& chan_name, const std::string& body,
541                                      MessageCallback callback)
542 {
543   IrcChannel* channel = this->get_channel(chan_name);
544   if (!channel->joined)
545     {
546       log_warning("Cannot send message to channel ", chan_name, ", it is not joined");
547       return false;
548     }
549   // The max size is 512, taking into account the whole message, not just
550   // the text we send.
551   // This includes our own nick, constants for username and host (because these
552   // are notoriously hard to know what the server will use), in addition to the basic
553   // components of the message we send (command name, chan name, \r\n etc.)
554   //  : + NICK + ! + USER + @ + HOST + <space> + PRIVMSG + <space> + CHAN + <space> + : + \r\n
555   // 63 is the maximum hostname length defined by the protocol.  10 seems to be
556   // the username limit.
557   constexpr auto max_username_size = 10;
558   constexpr auto max_hostname_size = 63;
559   const auto line_size = 512 -
560                          this->current_nick.size() - max_username_size - max_hostname_size -
561                          ::strlen(":!@ PRIVMSG ") - chan_name.length() - ::strlen(" :\r\n");
562   const auto lines = cut(body, line_size);
563   for (const auto& line: lines)
564     this->send_message(IrcMessage("PRIVMSG", {chan_name, line}), callback);
565   return true;
566 }
567 
send_private_message(const std::string & username,const std::string & body,const std::string & type)568 void IrcClient::send_private_message(const std::string& username, const std::string& body, const std::string& type)
569 {
570   std::string::size_type pos = 0;
571   while (pos < body.size())
572     {
573       this->send_message(IrcMessage(std::string(type), {username, body.substr(pos, 400)}));
574       pos += 400;
575     }
576   // We always try to insert and we don't care if the username was already
577   // in the set.
578   this->nicks_to_treat_as_private.insert(username);
579 }
580 
send_part_command(const std::string & chan_name,const std::string & status_message)581 void IrcClient::send_part_command(const std::string& chan_name, const std::string& status_message)
582 {
583   this->send_message(IrcMessage("PART", {chan_name, status_message}));
584 }
585 
send_mode_command(const std::string & chan_name,const std::vector<std::string> & arguments)586 void IrcClient::send_mode_command(const std::string& chan_name, const std::vector<std::string>& arguments)
587 {
588   std::vector<std::string> args(arguments);
589   args.insert(args.begin(), chan_name);
590   IrcMessage m("MODE", std::move(args));
591   this->send_message(std::move(m));
592 }
593 
send_pong_command(const IrcMessage & message)594 void IrcClient::send_pong_command(const IrcMessage& message)
595 {
596   const std::string id = message.arguments[0];
597   this->send_message(IrcMessage("PONG", {id}));
598 }
599 
on_pong(const IrcMessage &)600 void IrcClient::on_pong(const IrcMessage&)
601 {
602 }
603 
send_ping_command()604 void IrcClient::send_ping_command()
605 {
606   this->send_message(IrcMessage("PING", {"biboumi"}));
607 }
608 
forward_server_message(const IrcMessage & message)609 void IrcClient::forward_server_message(const IrcMessage& message)
610 {
611   const std::string from = message.prefix;
612   std::string body;
613   for (auto it = std::next(message.arguments.begin()); it != message.arguments.end(); ++it)
614     body += *it + ' ';
615 
616   this->bridge.send_xmpp_message(this->hostname, from, body);
617 }
618 
on_notice(const IrcMessage & message)619 void IrcClient::on_notice(const IrcMessage& message)
620 {
621   std::string from = message.prefix;
622   std::string to = message.arguments[0];
623   const std::string body = message.arguments[1];
624 
625   // Handle notices starting with [#channame] as if they were sent to that channel
626   if (body.size() > 3 && body[0] == '[')
627     {
628       const auto chan_prefix = body[1];
629       auto end = body.find(']');
630       if (this->chantypes.find(chan_prefix) != this->chantypes.end() && end != std::string::npos)
631         to = body.substr(1, end - 1);
632     }
633 
634   if (!body.empty() && body[0] == '\01' && body[body.size() - 1] == '\01')
635     // Do not forward the notice to the user if it's a CTCP command
636     return ;
637 
638   if (!to.empty() && this->chantypes.find(to[0]) == this->chantypes.end())
639     {
640       // The notice is for us precisely.
641 
642       // Find out if we already sent a private message to this user. If yes
643       // we treat that message as a private message coming from
644       // it. Otherwise we treat it as a notice coming from the server.
645       IrcUser user(from);
646       std::string nick = utils::tolower(user.nick);
647       if (this->nicks_to_treat_as_private.find(nick) !=
648           this->nicks_to_treat_as_private.end())
649         { // We previously sent a message to that nick)
650           this->bridge.send_message({nick, this->hostname, Iid::Type::User}, nick, body,
651                                      false);
652         }
653       else
654         this->bridge.send_xmpp_message(this->hostname, from, body);
655     }
656   else
657     {
658       // The notice was directed at a channel we are in. Modify the message
659       // to indicate that it is a notice, and make it a MUC message coming
660       // from the MUC JID
661       IrcMessage modified_message(std::move(from), "PRIVMSG", {to, "\u000303[notice]\u0003 " + body});
662       this->on_channel_message(modified_message);
663     }
664 }
665 
on_isupport_message(const IrcMessage & message)666 void IrcClient::on_isupport_message(const IrcMessage& message)
667 {
668   const size_t len = message.arguments.size();
669   for (size_t i = 1; i < len; ++i)
670   {
671     const std::string token = message.arguments[i];
672     if (token.substr(0, 10) == "CHANMODES=")
673     {
674       this->chanmodes = utils::split(token.substr(11), ',');
675       // make sure we have 4 strings
676       this->chanmodes.resize(4);
677     }
678     else if (token.substr(0, 7) == "PREFIX=")
679       {
680         size_t i = 8;           // jump PREFIX=(
681         size_t j = 9;
682         // Find the ) char
683         while (j < token.size() && token[j] != ')')
684           j++;
685         j++;
686         while (j < token.size() && token[i] != ')')
687           {
688             this->sorted_user_modes.push_back(token[i]);
689             this->prefix_to_mode[token[j++]] = token[i++];
690           }
691       }
692     else if (token.substr(0, 10) == "CHANTYPES=")
693       {
694         // Remove the default types, they apply only if no other value is
695         // specified.
696         this->chantypes.clear();
697         size_t i = 10;
698         while (i < token.size())
699           this->chantypes.insert(token[i++]);
700       }
701   }
702 }
703 
on_server_myinfo(const IrcMessage &)704 void IrcClient::on_server_myinfo(const IrcMessage&)
705 {
706 }
707 
send_gateway_message(const std::string & message,const std::string & from)708 void IrcClient::send_gateway_message(const std::string& message, const std::string& from)
709 {
710   this->bridge.send_xmpp_message(this->hostname, from, message);
711 }
712 
set_and_forward_user_list(const IrcMessage & message)713 void IrcClient::set_and_forward_user_list(const IrcMessage& message)
714 {
715   const std::string chan_name = utils::tolower(message.arguments[2]);
716   IrcChannel* channel = this->get_channel(chan_name);
717   if (channel->joined)
718     {
719       this->forward_server_message(message);
720       return;
721     }
722   std::vector<std::string> nicks = utils::split(message.arguments[3], ' ');
723   for (const std::string& nick: nicks)
724     {
725       // Just create this dummy user to parse and get its modes
726       IrcUser tmp_user{nick, this->prefix_to_mode};
727       // Does this concern ourself
728       if (channel->get_self() && channel->find_user(tmp_user.nick) == channel->get_self())
729         {
730           // We now know our own modes, that’s all.
731           channel->get_self()->modes = tmp_user.modes;
732         }
733       else
734         { // Otherwise this is a new user
735           const IrcUser *user = channel->add_user(nick, this->prefix_to_mode);
736           this->bridge.send_user_join(this->hostname, chan_name, user, user->get_most_significant_mode(this->sorted_user_modes), false);
737         }
738     }
739 }
740 
on_channel_join(const IrcMessage & message)741 void IrcClient::on_channel_join(const IrcMessage& message)
742 {
743   const std::string chan_name = utils::tolower(message.arguments[0]);
744   IrcChannel* channel;
745   channel = this->get_channel(chan_name);
746   const std::string nick = message.prefix;
747   IrcUser* user = channel->add_user(nick, this->prefix_to_mode);
748   if (channel->joined == false)
749     channel->set_self(user);
750   else
751     this->bridge.send_user_join(this->hostname, chan_name, user, user->get_most_significant_mode(this->sorted_user_modes), false);
752 }
753 
on_channel_message(const IrcMessage & message)754 void IrcClient::on_channel_message(const IrcMessage& message)
755 {
756   const IrcUser user(message.prefix);
757   const std::string nick = user.nick;
758   Iid iid;
759   iid.set_local(message.arguments[0]);
760   iid.set_server(this->hostname);
761   const std::string body = message.arguments[1];
762   bool muc = true;
763   if (!this->get_channel(iid.get_local())->joined)
764     {
765       iid.type = Iid::Type::User;
766       iid.set_local(nick);
767       muc = false;
768     }
769   else
770     iid.type = Iid::Type::Channel;
771   if (!body.empty() && body[0] == '\01')
772     {
773       if (body.substr(1, 6) == "ACTION")
774         this->bridge.send_message(iid, nick,
775                   "/me" + body.substr(7, body.size() - 8), muc);
776       else if (body.substr(1, 8) == "VERSION\01")
777         this->bridge.send_iq_version_request(nick, this->hostname);
778       else if (body.substr(1, 5) == "PING ")
779         this->bridge.send_xmpp_ping_request(utils::tolower(nick), this->hostname,
780                                              body.substr(6, body.size() - 7));
781     }
782   else
783     this->bridge.send_message(iid, nick, body, muc);
784 }
785 
on_rpl_liststart(const IrcMessage &)786 void IrcClient::on_rpl_liststart(const IrcMessage&)
787 {
788 }
789 
on_rpl_list(const IrcMessage &)790 void IrcClient::on_rpl_list(const IrcMessage&)
791 {
792 }
793 
on_rpl_listend(const IrcMessage &)794 void IrcClient::on_rpl_listend(const IrcMessage&)
795 {
796 }
797 
empty_motd(const IrcMessage &)798 void IrcClient::empty_motd(const IrcMessage&)
799 {
800   this->motd.erase();
801 }
802 
on_invited(const IrcMessage & message)803 void IrcClient::on_invited(const IrcMessage& message)
804 {
805   const std::string& chan_name = message.arguments[2];
806   const std::string& invited_nick = message.arguments[1];
807 
808   this->bridge.send_xmpp_message(this->hostname, "", invited_nick + " has been invited to " + chan_name);
809 }
810 
on_empty_topic(const IrcMessage & message)811 void IrcClient::on_empty_topic(const IrcMessage& message)
812 {
813   const std::string chan_name = utils::tolower(message.arguments[1]);
814   log_debug("empty topic for ", chan_name);
815   IrcChannel* channel = this->get_channel(chan_name);
816   if (channel)
817     channel->topic.clear();
818 }
819 
on_motd_line(const IrcMessage & message)820 void IrcClient::on_motd_line(const IrcMessage& message)
821 {
822   const std::string body = message.arguments[1];
823   // We could send the MOTD without a line break between each IRC-message,
824   // but sometimes it contains some ASCII art, we use line breaks to keep
825   // them intact.
826   this->motd += body+"\n";
827 }
828 
send_motd(const IrcMessage &)829 void IrcClient::send_motd(const IrcMessage&)
830 {
831   this->bridge.send_xmpp_message(this->hostname, "", this->motd);
832 }
833 
on_topic_received(const IrcMessage & message)834 void IrcClient::on_topic_received(const IrcMessage& message)
835 {
836   const std::string chan_name = utils::tolower(message.arguments[message.arguments.size() - 2]);
837   IrcUser author(message.prefix);
838   IrcChannel* channel = this->get_channel(chan_name);
839   channel->topic = message.arguments[message.arguments.size() - 1];
840   channel->topic_author = author.nick;
841   if (channel->joined)
842     this->bridge.send_topic(this->hostname, chan_name, channel->topic, channel->topic_author);
843 }
844 
on_topic_who_time_received(const IrcMessage & message)845 void IrcClient::on_topic_who_time_received(const IrcMessage& message)
846 {
847   IrcUser author(message.arguments[2]);
848   const std::string chan_name = utils::tolower(message.arguments[1]);
849   IrcChannel* channel = this->get_channel(chan_name);
850   channel->topic_author = author.nick;
851 }
852 
on_channel_completely_joined(const IrcMessage & message)853 void IrcClient::on_channel_completely_joined(const IrcMessage& message)
854 {
855   const std::string chan_name = utils::tolower(message.arguments[1]);
856   IrcChannel* channel = this->get_channel(chan_name);
857   if (chan_name == "*" || channel->joined)
858     {
859       this->forward_server_message(message);
860       return;
861     }
862   if (!channel->get_self())
863     {
864       log_error("End of NAMES list but we never received our own nick.");
865       return;
866     }
867   channel->joined = true;
868   this->bridge.send_user_join(this->hostname, chan_name, channel->get_self(),
869                               channel->get_self()->get_most_significant_mode(this->sorted_user_modes), true);
870   this->bridge.send_room_history(this->hostname, chan_name, this->history_limit);
871   this->bridge.send_topic(this->hostname, chan_name, channel->topic, channel->topic_author);
872 }
873 
on_banlist(const IrcMessage & message)874 void IrcClient::on_banlist(const IrcMessage& message)
875 {
876   const std::string chan_name = utils::tolower(message.arguments[1]);
877   IrcChannel* channel = this->get_channel(chan_name);
878   if (channel->joined)
879     {
880       Iid iid;
881       iid.set_local(chan_name);
882       iid.set_server(this->hostname);
883       iid.type = Iid::Type::Channel;
884       std::string body{message.arguments[2] + " banned"};
885       if (message.arguments.size() >= 4)
886         {
887           IrcUser by(message.arguments[3], this->prefix_to_mode);
888           body += " by " + by.nick;
889         }
890       if (message.arguments.size() >= 5)
891         body += " on " + message.arguments[4];
892 
893       this->bridge.send_message(iid, "", body, true);
894     }
895 }
896 
on_banlist_end(const IrcMessage & message)897 void IrcClient::on_banlist_end(const IrcMessage& message)
898 {
899   const std::string chan_name = utils::tolower(message.arguments[1]);
900   IrcChannel* channel = this->get_channel(chan_name);
901   if (channel->joined)
902     {
903       Iid iid;
904       iid.set_local(chan_name);
905       iid.set_server(this->hostname);
906       iid.type = Iid::Type::Channel;
907       this->bridge.send_message(iid, "", message.arguments[2], true);
908     }
909 }
910 
on_own_host_received(const IrcMessage & message)911 void IrcClient::on_own_host_received(const IrcMessage& message)
912 {
913   this->own_host = message.arguments[1];
914   const std::string from = message.prefix;
915   if (message.arguments.size() >= 3)
916     this->bridge.send_xmpp_message(this->hostname, from,
917                                    this->own_host + " " + message.arguments[2]);
918   else
919     this->bridge.send_xmpp_message(this->hostname, from, this->own_host +
920                                                          " is now your displayed host");
921 }
922 
on_erroneous_nickname(const IrcMessage & message)923 void IrcClient::on_erroneous_nickname(const IrcMessage& message)
924 {
925   const std::string error_msg = message.arguments.size() >= 3 ?
926     message.arguments[2]: "Erroneous nickname";
927   this->send_gateway_message(error_msg + ": " + message.arguments[1], message.prefix);
928 }
929 
on_nickname_conflict(const IrcMessage & message)930 void IrcClient::on_nickname_conflict(const IrcMessage& message)
931 {
932   const std::string nickname = message.arguments[1];
933   this->on_generic_error(message);
934   for (const auto& pair: this->channels)
935   {
936     Iid iid;
937     iid.set_local(pair.first);
938     iid.set_server(this->hostname);
939     iid.type = Iid::Type::Channel;
940     this->bridge.send_nickname_conflict_error(iid, nickname);
941   }
942 }
943 
on_nickname_change_too_fast(const IrcMessage & message)944 void IrcClient::on_nickname_change_too_fast(const IrcMessage& message)
945 {
946   const std::string nickname = message.arguments[1];
947   std::string txt;
948   if (message.arguments.size() >= 3)
949     txt = message.arguments[2];
950   this->on_generic_error(message);
951   for (const auto& pair: this->channels)
952   {
953     Iid iid;
954     iid.set_local(pair.first);
955     iid.set_server(this->hostname);
956     iid.type = Iid::Type::Channel;
957     this->bridge.send_presence_error(iid, nickname,
958                                       "cancel", "not-acceptable",
959                                       "", txt);
960   }
961 }
on_generic_error(const IrcMessage & message)962 void IrcClient::on_generic_error(const IrcMessage& message)
963 {
964   const std::string error_msg = message.arguments.size() >= 3 ?
965     message.arguments[2]: "Unspecified error";
966   this->send_gateway_message(message.arguments[1] + ": " + error_msg, message.prefix);
967 }
968 
on_useronchannel(const IrcMessage & message)969 void IrcClient::on_useronchannel(const IrcMessage& message)
970 {
971   this->send_gateway_message(message.arguments[1] + " " + message.arguments[3] + " "
972                              + message.arguments[2]);
973 }
974 
on_welcome_message(const IrcMessage & message)975 void IrcClient::on_welcome_message(const IrcMessage& message)
976 {
977   this->current_nick = message.arguments[0];
978   this->welcomed = true;
979 #ifdef USE_DATABASE
980   auto options = Database::get_irc_server_options(this->bridge.get_bare_jid(),
981                                                   this->get_hostname());
982   const auto commands = Database::get_after_connection_commands(options);
983   for (const auto& command: commands)
984     this->send_raw(command.col<Database::AfterConnectionCommand>());
985 #endif
986   // Install a repeated events to regularly send a PING
987   TimedEventsManager::instance().add_event(TimedEvent(240s, std::bind(&IrcClient::send_ping_command, this),
988                                                       "PING" + this->hostname + this->bridge.get_jid()));
989   std::string channels{};
990   std::string channels_with_key{};
991   std::string keys{};
992 
993   for (const auto& tuple: this->channels_to_join)
994     {
995       const auto& chan = std::get<0>(tuple);
996       const auto& key = std::get<1>(tuple);
997       if (chan.empty())
998         continue;
999       if (!key.empty())
1000         {
1001           if (keys.size() + channels_with_key.size() >= 300)
1002             { // Arbitrary size, to make sure we never send more than 512
1003               this->send_join_command(channels_with_key, keys);
1004               channels_with_key.clear();
1005               keys.clear();
1006             }
1007           if (!keys.empty())
1008             keys += ",";
1009           keys += key;
1010           if (!channels_with_key.empty())
1011             channels_with_key += ",";
1012           channels_with_key += chan;
1013         }
1014       else
1015         {
1016           if (channels.size() >= 300)
1017             { // Arbitrary size, to make sure we never send more than 512
1018               this->send_join_command(channels, {});
1019               channels.clear();
1020             }
1021           if (!channels.empty())
1022             channels += ",";
1023           channels += chan;
1024         }
1025     }
1026   if (!channels.empty())
1027     this->send_join_command(channels, {});
1028   if (!channels_with_key.empty())
1029     this->send_join_command(channels_with_key, keys);
1030   this->channels_to_join.clear();
1031 }
1032 
on_part(const IrcMessage & message)1033 void IrcClient::on_part(const IrcMessage& message)
1034 {
1035   const std::string chan_name = message.arguments[0];
1036   IrcChannel* channel = this->get_channel(chan_name);
1037   if (!channel->joined)
1038     return ;
1039   std::string txt;
1040   if (message.arguments.size() >= 2)
1041     txt = message.arguments[1];
1042   const IrcUser* user = channel->find_user(message.prefix);
1043   if (user)
1044     {
1045       std::string nick = user->nick;
1046       bool self = channel->get_self() && channel->get_self()->nick == nick;
1047       auto user_ptr = channel->remove_user(user);
1048       if (self)
1049       {
1050         this->channels.erase(utils::tolower(chan_name));
1051         // channel pointer is now invalid
1052         channel = nullptr;
1053       }
1054       Iid iid;
1055       iid.set_local(chan_name);
1056       iid.set_server(this->hostname);
1057       iid.type = Iid::Type::Channel;
1058       this->bridge.send_muc_leave(iid, *user_ptr, txt, self, true, {}, this);
1059     }
1060 }
1061 
on_error(const IrcMessage & message)1062 void IrcClient::on_error(const IrcMessage& message)
1063 {
1064   const std::string leave_message = message.arguments[0];
1065   // The user is out of all the channels
1066   for (const auto& pair: this->channels)
1067   {
1068     Iid iid;
1069     iid.set_local(pair.first);
1070     iid.set_server(this->hostname);
1071     iid.type = Iid::Type::Channel;
1072     IrcChannel* channel = pair.second.get();
1073     if (!channel->joined)
1074       continue;
1075     this->bridge.send_muc_leave(iid, *channel->get_self(), leave_message, true, false, {}, this);
1076   }
1077   this->channels.clear();
1078   this->send_gateway_message("ERROR: " + leave_message);
1079 }
1080 
on_quit(const IrcMessage & message)1081 void IrcClient::on_quit(const IrcMessage& message)
1082 {
1083   std::string txt;
1084   if (message.arguments.size() >= 1)
1085     txt = message.arguments[0];
1086   for (const auto& pair: this->channels)
1087     {
1088       const std::string& chan_name = pair.first;
1089       IrcChannel* channel = pair.second.get();
1090       const IrcUser* user = channel->find_user(message.prefix);
1091       if (!user)
1092         continue;
1093       bool self = false;
1094       if (user == channel->get_self())
1095         self = true;
1096       Iid iid;
1097       iid.set_local(chan_name);
1098       iid.set_server(this->hostname);
1099       iid.type = Iid::Type::Channel;
1100       this->bridge.send_muc_leave(iid, *user, txt, self, false, {}, this);
1101       channel->remove_user(user);
1102     }
1103 }
1104 
on_nick(const IrcMessage & message)1105 void IrcClient::on_nick(const IrcMessage& message)
1106 {
1107   const std::string new_nick = IrcUser(message.arguments[0]).nick;
1108   const std::string current_nick = IrcUser(message.prefix).nick;
1109   const auto change_nick_func = [this, &new_nick, &current_nick](const std::string& chan_name, const IrcChannel* channel)
1110   {
1111     IrcUser* user;
1112     if (channel->get_self() && channel->get_self()->nick == current_nick)
1113       user = channel->get_self();
1114     else
1115       user = channel->find_user(current_nick);
1116     if (user)
1117       {
1118         std::string old_nick = user->nick;
1119         Iid iid(chan_name, this->hostname, Iid::Type::Channel);
1120         const bool self = channel->get_self()->nick == old_nick;
1121         const char user_mode = user->get_most_significant_mode(this->sorted_user_modes);
1122         this->bridge.send_nick_change(std::move(iid), old_nick, new_nick, user_mode, self);
1123         user->nick = new_nick;
1124         if (self)
1125           {
1126             channel->get_self()->nick = new_nick;
1127             this->current_nick = new_nick;
1128           }
1129       }
1130   };
1131 
1132   for (const auto& pair: this->channels)
1133     {
1134       change_nick_func(pair.first, pair.second.get());
1135     }
1136 }
1137 
on_kick(const IrcMessage & message)1138 void IrcClient::on_kick(const IrcMessage& message)
1139 {
1140   const std::string chan_name = utils::tolower(message.arguments[0]);
1141   const std::string target_nick = message.arguments[1];
1142   const std::string reason = message.arguments[2];
1143   IrcChannel* channel = this->get_channel(chan_name);
1144   if (!channel->joined)
1145     return ;
1146   const IrcUser* target = channel->find_user(target_nick);
1147   if (!target)
1148     {
1149       log_warning("Received a KICK command from a nick absent from the channel.");
1150       return;
1151     }
1152   const bool self = channel->get_self() == target;
1153   if (self)
1154     channel->joined = false;
1155   IrcUser author(message.prefix);
1156   Iid iid;
1157   iid.set_local(chan_name);
1158   iid.set_server(this->hostname);
1159   iid.type = Iid::Type::Channel;
1160   this->bridge.kick_muc_user(std::move(iid), target_nick, reason, author.nick, self);
1161   channel->remove_user(target);
1162 }
1163 
on_invite(const IrcMessage & message)1164 void IrcClient::on_invite(const IrcMessage& message)
1165 {
1166   IrcUser author(message.prefix);
1167   Iid iid;
1168   iid.set_local(message.arguments[1]);
1169   iid.set_server(this->hostname);
1170   iid.type = Iid::Type::Channel;
1171 
1172   this->bridge.send_xmpp_invitation(iid, author.nick);
1173 }
1174 
on_mode(const IrcMessage & message)1175 void IrcClient::on_mode(const IrcMessage& message)
1176 {
1177   const std::string target = message.arguments[0];
1178   if (this->chantypes.find(target[0]) != this->chantypes.end())
1179     this->on_channel_mode(message);
1180   else
1181     this->on_user_mode(message);
1182 }
1183 
on_channel_bad_key(const IrcMessage & message)1184 void IrcClient::on_channel_bad_key(const IrcMessage& message)
1185 {
1186   this->on_generic_error(message);
1187   const std::string& nickname = message.arguments[0];
1188   const std::string& channel = message.arguments[1];
1189   std::string text;
1190   if (message.arguments.size() > 2)
1191     text = message.arguments[2];
1192 
1193   this->bridge.send_presence_error({channel, this->hostname, Iid::Type::Channel}, nickname, "auth", "not-authorized", "", text);
1194 }
1195 
on_channel_mode(const IrcMessage & message)1196 void IrcClient::on_channel_mode(const IrcMessage& message)
1197 {
1198   Iid iid;
1199   iid.set_local(message.arguments[0]);
1200   iid.set_server(this->hostname);
1201   iid.type = Iid::Type::Channel;
1202   IrcUser user(message.prefix);
1203   std::string mode_arguments;
1204   for (size_t i = 1; i < message.arguments.size(); ++i)
1205     {
1206       if (!message.arguments[i].empty())
1207         {
1208           if (i != 1)
1209             mode_arguments += " ";
1210           mode_arguments += message.arguments[i];
1211         }
1212     }
1213   this->bridge.send_message(iid, "", "Mode " + iid.get_local() +
1214                                       " [" + mode_arguments + "] by " + user.nick,
1215                              true, this->is_channel_joined(iid.get_local()));
1216   const IrcChannel* channel = this->get_channel(iid.get_local());
1217   if (!channel)
1218     return;
1219 
1220   // parse the received modes, we need to handle things like "+m-oo coucou toutou"
1221   const std::string modes = message.arguments[1];
1222   // a list of modified IrcUsers. When we applied all modes, we check the
1223   // modes that now applies to each of them, and send a notification for
1224   // each one. This is to disallow sending two notifications or more when a
1225   // single MODE command changes two or more modes on the same participant
1226   std::set<const IrcUser*> modified_users;
1227   // If it is true, the modes are added, if it’s false they are
1228   // removed. When we encounter the '+' char, the value is changed to true,
1229   // and with '-' it is changed to false.
1230   bool add = true;
1231   bool use_arg;
1232   size_t arg_pos = 2;
1233   for (const char c: modes)
1234     {
1235       if (c == '+')
1236         add = true;
1237       else if (c == '-')
1238         add = false;
1239       else
1240         { // lookup the mode symbol in the 4 chanmodes lists, depending on
1241           // the list where it is found, it takes an argument or not
1242           size_t type;
1243           for (type = 0; type < 4; ++type)
1244             if (this->chanmodes[type].find(c) != std::string::npos)
1245               break;
1246           if (type == 4)        // if mode was not found
1247             {
1248               // That mode can also be of type B if it is present in the
1249               // prefix_to_mode map
1250               for (const auto& pair: this->prefix_to_mode)
1251                 if (pair.second == c)
1252                   {
1253                     type = 1;
1254                     break;
1255                   }
1256             }
1257           // modes of type A, B or C (but only with add == true)
1258           if (type == 0 || type == 1 ||
1259               (type == 2 && add == true))
1260             use_arg = true;
1261           else // modes of type C (but only with add == false), D, or unknown
1262             use_arg = false;
1263           if (use_arg == true && message.arguments.size() > arg_pos)
1264             {
1265               const std::string target = message.arguments[arg_pos++];
1266               IrcUser* user = channel->find_user(target);
1267               if (!user)
1268                 {
1269                   log_warning("Trying to set mode for non-existing user '", target
1270                              , "' in channel", iid.get_local());
1271                   return;
1272                 }
1273               if (add)
1274                 user->add_mode(c);
1275               else
1276                 user->remove_mode(c);
1277               modified_users.insert(user);
1278             }
1279         }
1280     }
1281   for (const IrcUser* u: modified_users)
1282     {
1283       char most_significant_mode = u->get_most_significant_mode(this->sorted_user_modes);
1284       this->bridge.send_affiliation_role_change(iid, u->nick, most_significant_mode);
1285     }
1286 }
1287 
set_throttle_limit(long int limit)1288 void IrcClient::set_throttle_limit(long int limit)
1289 {
1290   this->tokens_bucket.set_limit(limit);
1291 }
1292 
on_user_mode(const IrcMessage & message)1293 void IrcClient::on_user_mode(const IrcMessage& message)
1294 {
1295   this->bridge.send_xmpp_message(this->hostname, "",
1296                                   "User mode for " + message.arguments[0] +
1297                                   " is [" + message.arguments[1] + "]");
1298 }
1299 
on_unknown_message(const IrcMessage & message)1300 void IrcClient::on_unknown_message(const IrcMessage& message)
1301 {
1302   if (message.arguments.size() < 2)
1303     return ;
1304   std::string from = message.prefix;
1305   std::stringstream ss;
1306   for (auto it = std::next(message.arguments.begin()); it != message.arguments.end(); ++it)
1307     {
1308       ss << *it;
1309       if (it + 1 != message.arguments.end())
1310         ss << " ";
1311     }
1312   this->bridge.send_xmpp_message(this->hostname, from, ss.str());
1313 }
1314 
number_of_joined_channels() const1315 size_t IrcClient::number_of_joined_channels() const
1316 {
1317   return this->channels.size();
1318 }
1319 
1320 #ifdef BOTAN_FOUND
abort_on_invalid_cert() const1321 bool IrcClient::abort_on_invalid_cert() const
1322 {
1323 #ifdef USE_DATABASE
1324   auto options = Database::get_irc_server_options(this->bridge.get_bare_jid(), this->hostname);
1325   return options.col<Database::VerifyCert>();
1326 #endif
1327   return true;
1328 }
1329 #endif
1330 
get_throttle_limit() const1331 long int IrcClient::get_throttle_limit() const
1332 {
1333 #ifdef USE_DATABASE
1334   return Database::get_irc_server_options(this->bridge.get_bare_jid(), this->hostname).col<Database::ThrottleLimit>();
1335 #else
1336   return 10;
1337 #endif
1338 }
1339 
on_cap(const IrcMessage & message)1340 void IrcClient::on_cap(const IrcMessage &message)
1341 {
1342   const auto& sub_command = message.arguments[1];
1343   const auto& caps = utils::split(message.arguments[2], ' ', false);
1344   for (const auto& cap: caps)
1345     {
1346       auto it = this->capabilities.find(cap);
1347       if (it == this->capabilities.end())
1348         {
1349           log_warning("Received a CAP message for something we didn’t ask, or that we already handled: [", cap, "]");
1350           return;
1351         }
1352       Capability& capability = it->second;
1353       if (sub_command == "ACK")
1354         capability.on_ack();
1355       else if (sub_command == "NACK")
1356         capability.on_nack();
1357       this->capabilities.erase(it);
1358     }
1359   if (this->capabilities.empty())
1360     this->cap_end();
1361 }
1362 
1363 #ifdef WITH_SASL
on_authenticate(const IrcMessage &)1364 void IrcClient::on_authenticate(const IrcMessage &)
1365 {
1366   if (this->sasl_state == SaslState::unneeded)
1367     {
1368       log_warning("Received an AUTHENTICATE command but we don’t intend to authenticate…");
1369       return;
1370     }
1371 
1372   auto options = Database::get_irc_server_options(this->bridge.get_bare_jid(),
1373                                                   this->get_hostname());
1374   const auto auth_string = '\0' + options.col<Database::Nick>() + '\0' + options.col<Database::SaslPassword>();
1375   const auto base64_auth_string = base64::encode(auth_string);
1376   this->send_message({"AUTHENTICATE", {base64_auth_string}});
1377 }
1378 
on_sasl_success(const IrcMessage &)1379 void IrcClient::on_sasl_success(const IrcMessage &)
1380 {
1381   this->sasl_state = SaslState::success;
1382   this->cap_end();
1383 }
1384 
on_sasl_failure(const IrcMessage & message)1385 void IrcClient::on_sasl_failure(const IrcMessage& message)
1386 {
1387   this->sasl_state = SaslState::failure;
1388   const auto reason = message.arguments[1];
1389   // Send an error message for all room that the user wanted to join
1390   for (const auto& tuple: this->channels_to_join)
1391   {
1392     Iid iid(std::get<0>(tuple) + "%" + this->hostname, this->chantypes);
1393     this->bridge.send_presence_error(iid, this->current_nick,
1394                                      "cancel", "item-not-found",
1395                                      "", reason);
1396   }
1397   this->channels_to_join.clear();
1398   this->send_quit_command(reason);
1399 }
1400 
on_sasl_login(const IrcMessage & message)1401 void IrcClient::on_sasl_login(const IrcMessage &message)
1402 {
1403   const auto& login = message.arguments[2];
1404   std::string text = "Your are now logged in as " + login;
1405   if (message.arguments.size() > 3)
1406     text = message.arguments[3];
1407   this->bridge.send_xmpp_message(this->hostname, message.prefix, text);
1408 }
1409 #endif
1410 
cap_end()1411 void IrcClient::cap_end()
1412 {
1413 #ifdef WITH_SASL
1414   // If we are currently authenticating through sasl, finish that before sending CAP END
1415   if (this->sasl_state == SaslState::needed)
1416     return;
1417 #endif
1418   this->send_message({"CAP", {"END"}});
1419   this->bridge.on_irc_client_connected(this->get_hostname());
1420 }
1421