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;
            }
        }
    }
}