irc_client.cpp 9.31 KB
Newer Older
1
#include <irc/irc_message.hpp>
2
3
4
5
6
7
#include <irc/irc_client.hpp>
#include <bridge/bridge.hpp>
#include <irc/irc_user.hpp>

#include <utils/make_unique.hpp>
#include <utils/split.hpp>
8
9
10
11

#include <iostream>
#include <stdexcept>

12
13
14
IrcClient::IrcClient(const std::string& hostname, const std::string& username, Bridge* bridge):
  hostname(hostname),
  username(username),
louiz’'s avatar
louiz’ committed
15
16
17
  current_nick(username),
  bridge(bridge),
  welcomed(false)
18
19
20
21
22
23
24
25
26
{
  std::cout << "IrcClient()" << std::endl;
}

IrcClient::~IrcClient()
{
  std::cout << "~IrcClient()" << std::endl;
}

27
28
29
30
31
void IrcClient::start()
{
  this->connect(this->hostname, "6667");
}

louiz’'s avatar
louiz’ committed
32
33
void IrcClient::on_connected()
{
34
35
  this->send_nick_command(this->username);
  this->send_user_command(this->username, this->username);
louiz’'s avatar
louiz’ committed
36
37
}

38
39
40
41
42
void IrcClient::on_connection_close()
{
  std::cout << "Connection closed by remote server." << std::endl;
}

43
44
45
46
47
48
49
50
51
52
53
54
55
IrcChannel* IrcClient::get_channel(const std::string& name)
{
  try
    {
      return this->channels.at(name).get();
    }
  catch (const std::out_of_range& exception)
    {
      this->channels.emplace(name, std::make_unique<IrcChannel>());
      return this->channels.at(name).get();
    }
}

louiz’'s avatar
louiz’ committed
56
57
58
59
60
61
bool IrcClient::is_channel_joined(const std::string& name)
{
  IrcChannel* client = this->get_channel(name);
  return client->joined;
}

louiz’'s avatar
louiz’ committed
62
63
64
65
66
std::string IrcClient::get_own_nick() const
{
  return this->current_nick;
}

67
68
void IrcClient::parse_in_buffer()
{
louiz’'s avatar
louiz’ committed
69
70
71
72
73
74
75
76
  while (true)
    {
      auto pos = this->in_buf.find("\r\n");
      if (pos == std::string::npos)
        break ;
      IrcMessage message(this->in_buf.substr(0, pos));
      this->in_buf = this->in_buf.substr(pos + 2, std::string::npos);
      std::cout << message << std::endl;
77
78
      // TODO map function and command name properly
      if (message.command == "PING")
louiz’'s avatar
louiz’ committed
79
        this->send_pong_command(message);
80
81
82
83
84
      else if (message.command == "NOTICE" ||
               message.command == "375" ||
               message.command == "372")
        this->forward_server_message(message);
      else if (message.command == "JOIN")
louiz’'s avatar
louiz’ committed
85
        this->on_channel_join(message);
louiz’'s avatar
louiz’ committed
86
87
      else if (message.command == "PRIVMSG")
        this->on_channel_message(message);
88
89
90
91
92
93
      else if (message.command == "353")
        this->set_and_forward_user_list(message);
      else if (message.command == "332")
        this->on_topic_received(message);
      else if (message.command == "366")
        this->on_channel_completely_joined(message);
louiz’'s avatar
louiz’ committed
94
95
      else if (message.command == "001")
        this->on_welcome_message(message);
louiz’'s avatar
louiz’ committed
96
97
      else if (message.command == "PART")
        this->on_part(message);
louiz’'s avatar
louiz’ committed
98
99
      else if (message.command == "QUIT")
        this->on_quit(message);
louiz’'s avatar
louiz’ committed
100
101
      else if (message.command == "NICK")
        this->on_nick(message);
louiz’'s avatar
louiz’ committed
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
    }
}

void IrcClient::send_message(IrcMessage&& message)
{
  std::string res;
  if (!message.prefix.empty())
    res += ":" + std::move(message.prefix) + " ";
  res += std::move(message.command);
  for (const std::string& arg: message.arguments)
    {
      if (arg.find(" ") != std::string::npos)
        {
          res += " :" + arg;
          break;
        }
      res += " " + arg;
    }
  res += "\r\n";
121
122
  std::cout << "=== IRC SENDING ===" << std::endl;
  std::cout << res << std::endl;
123
  this->send_data(std::move(res));
louiz’'s avatar
louiz’ committed
124
125
126
127
}

void IrcClient::send_user_command(const std::string& username, const std::string& realname)
{
louiz’'s avatar
louiz’ committed
128
  this->send_message(IrcMessage("USER", {username, "ignored", "ignored", realname}));
louiz’'s avatar
louiz’ committed
129
130
131
132
133
134
135
136
137
}

void IrcClient::send_nick_command(const std::string& nick)
{
  this->send_message(IrcMessage("NICK", {nick}));
}

void IrcClient::send_join_command(const std::string& chan_name)
{
louiz’'s avatar
louiz’ committed
138
  if (this->welcomed == false)
louiz’'s avatar
louiz’ committed
139
140
141
    this->channels_to_join.push_back(chan_name);
  else
    this->send_message(IrcMessage("JOIN", {chan_name}));
142
143
}

louiz’'s avatar
louiz’ committed
144
bool IrcClient::send_channel_message(const std::string& chan_name, const std::string& body)
145
{
louiz’'s avatar
louiz’ committed
146
147
148
149
150
151
152
153
154
155
  IrcChannel* channel = this->get_channel(chan_name);
  if (channel->joined == false)
    {
      std::cout << "Cannot send message to channel " << chan_name << ", it is not joined" << std::endl;
      return false;
    }
  this->send_message(IrcMessage("PRIVMSG", {chan_name, body}));
  return true;
}

louiz’'s avatar
louiz’ committed
156
157
158
159
160
void IrcClient::send_private_message(const std::string& username, const std::string& body)
{
  this->send_message(IrcMessage("PRIVMSG", {username, body}));
}

louiz’'s avatar
louiz’ committed
161
162
163
164
165
166
167
168
169
void IrcClient::send_part_command(const std::string& chan_name, const std::string& status_message)
{
  IrcChannel* channel = this->get_channel(chan_name);
  if (channel->joined == true)
    {
      this->send_message(IrcMessage("PART", {chan_name, status_message}));
    }
}

louiz’'s avatar
louiz’ committed
170
171
172
173
void IrcClient::send_pong_command(const IrcMessage& message)
{
  const std::string id = message.arguments[0];
  this->send_message(IrcMessage("PONG", {id}));
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
}

void IrcClient::forward_server_message(const IrcMessage& message)
{
  const std::string from = message.prefix;
  const std::string body = message.arguments[1];

  this->bridge->send_xmpp_message(this->hostname, from, body);
}

void IrcClient::set_and_forward_user_list(const IrcMessage& message)
{
  const std::string chan_name = message.arguments[2];
  IrcChannel* channel = this->get_channel(chan_name);
  std::vector<std::string> nicks = utils::split(message.arguments[3], ' ');
  for (const std::string& nick: nicks)
    {
      IrcUser* user = channel->add_user(nick);
      if (user->nick != channel->get_self()->nick)
        {
          std::cout << "Adding user [" << nick << "] to chan " << chan_name << std::endl;
          this->bridge->send_user_join(this->hostname, chan_name, user->nick);
        }
    }
}

louiz’'s avatar
louiz’ committed
200
void IrcClient::on_channel_join(const IrcMessage& message)
201
202
203
{
  const std::string chan_name = message.arguments[0];
  IrcChannel* channel = this->get_channel(chan_name);
louiz’'s avatar
louiz’ committed
204
205
206
207
208
209
210
211
212
213
214
  const std::string nick = message.prefix;
  if (channel->joined == false)
    {
      channel->joined = true;
      channel->set_self(nick);
    }
  else
    {
      IrcUser* user = channel->add_user(nick);
      this->bridge->send_user_join(this->hostname, chan_name, user->nick);
    }
215
216
}

louiz’'s avatar
louiz’ committed
217
218
219
220
221
222
223
224
void IrcClient::on_channel_message(const IrcMessage& message)
{
  const IrcUser user(message.prefix);
  const std::string nick = user.nick;
  Iid iid;
  iid.chan = message.arguments[0];
  iid.server = this->hostname;
  const std::string body = message.arguments[1];
louiz’'s avatar
louiz’ committed
225
226
227
228
229
230
231
  bool muc = true;
  if (!this->get_channel(iid.chan)->joined)
    {
      iid.chan = nick;
      muc = false;
    }
  this->bridge->send_message(iid, nick, body, muc);
louiz’'s avatar
louiz’ committed
232
233
}

234
235
236
237
238
239
240
241
242
243
244
245
246
void IrcClient::on_topic_received(const IrcMessage& message)
{
  const std::string chan_name = message.arguments[1];
  IrcChannel* channel = this->get_channel(chan_name);
  channel->topic = message.arguments[2];
}

void IrcClient::on_channel_completely_joined(const IrcMessage& message)
{
  const std::string chan_name = message.arguments[1];
  IrcChannel* channel = this->get_channel(chan_name);
  this->bridge->send_self_join(this->hostname, chan_name, channel->get_self()->nick);
  this->bridge->send_topic(this->hostname, chan_name, channel->topic);
247
}
louiz’'s avatar
louiz’ committed
248
249
250
251
252
253
254
255
256

void IrcClient::on_welcome_message(const IrcMessage& message)
{
  this->current_nick = message.arguments[0];
  this->welcomed = true;
  for (const std::string& chan_name: this->channels_to_join)
    this->send_join_command(chan_name);
  this->channels_to_join.clear();
}
louiz’'s avatar
louiz’ committed
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278

void IrcClient::on_part(const IrcMessage& message)
{
  const std::string chan_name = message.arguments[0];
  IrcChannel* channel = this->get_channel(chan_name);
  std::string txt;
  if (message.arguments.size() >= 2)
    txt = message.arguments[1];
  const IrcUser* user = channel->find_user(message.prefix);
  if (user)
    {
      std::string nick = user->nick;
      channel->remove_user(user);
      Iid iid;
      iid.chan = chan_name;
      iid.server = this->hostname;
      bool self = channel->get_self()->nick == nick;
      this->bridge->send_muc_leave(std::move(iid), std::move(nick), std::move(txt), self);
      if (self)
        channel->joined = false;
    }
}
louiz’'s avatar
louiz’ committed
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296

void IrcClient::on_quit(const IrcMessage& message)
{
  std::string txt;
  if (message.arguments.size() >= 1)
    txt = message.arguments[0];
  for (auto it = this->channels.begin(); it != this->channels.end(); ++it)
    {
      const std::string chan_name = it->first;
      IrcChannel* channel = it->second.get();
      const IrcUser* user = channel->find_user(message.prefix);
      if (user)
        {
          std::string nick = user->nick;
          channel->remove_user(user);
          Iid iid;
          iid.chan = chan_name;
          iid.server = this->hostname;
louiz’'s avatar
louiz’ committed
297
          this->bridge->send_muc_leave(std::move(iid), std::move(nick), txt, false);
louiz’'s avatar
louiz’ committed
298
299
300
        }
    }
}
louiz’'s avatar
louiz’ committed
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327

void IrcClient::on_nick(const IrcMessage& message)
{
  const std::string new_nick = message.arguments[0];
  for (auto it = this->channels.begin(); it != this->channels.end(); ++it)
    {
      const std::string chan_name = it->first;
      IrcChannel* channel = it->second.get();
      IrcUser* user = channel->find_user(message.prefix);
      if (user)
        {
          std::string old_nick = user->nick;
          Iid iid;
          iid.chan = chan_name;
          iid.server = this->hostname;
          bool self = channel->get_self()->nick == old_nick;
          this->bridge->send_nick_change(std::move(iid), old_nick, new_nick, self);
          user->nick = new_nick;
          if (self)
            {
              channel->get_self()->nick = new_nick;
              this->current_nick = new_nick;
            }
        }
    }
}