irc_client.cpp 8.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
std::string IrcClient::get_own_nick() const
{
  return this->current_nick;
}

61 62
void IrcClient::parse_in_buffer()
{
louiz’'s avatar
louiz’ committed
63 64 65 66 67 68 69 70
  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;
71 72
      // TODO map function and command name properly
      if (message.command == "PING")
louiz’'s avatar
louiz’ committed
73
        this->send_pong_command(message);
74 75 76 77 78
      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
79
        this->on_channel_join(message);
louiz’'s avatar
louiz’ committed
80 81
      else if (message.command == "PRIVMSG")
        this->on_channel_message(message);
82 83 84 85 86 87
      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
88 89
      else if (message.command == "001")
        this->on_welcome_message(message);
louiz’'s avatar
louiz’ committed
90 91
      else if (message.command == "PART")
        this->on_part(message);
louiz’'s avatar
louiz’ committed
92 93
      else if (message.command == "QUIT")
        this->on_quit(message);
louiz’'s avatar
louiz’ committed
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
    }
}

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";
113 114
  std::cout << "=== IRC SENDING ===" << std::endl;
  std::cout << res << std::endl;
115
  this->send_data(std::move(res));
louiz’'s avatar
louiz’ committed
116 117 118 119
}

void IrcClient::send_user_command(const std::string& username, const std::string& realname)
{
louiz’'s avatar
louiz’ committed
120
  this->send_message(IrcMessage("USER", {username, "ignored", "ignored", realname}));
louiz’'s avatar
louiz’ committed
121 122 123 124 125 126 127 128 129
}

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
130 131 132 133 134
  if (this->welcomed == false)
    {
      this->channels_to_join.push_back(chan_name);
      return ;
    }
louiz’'s avatar
louiz’ committed
135
  this->send_message(IrcMessage("JOIN", {chan_name}));
136 137
}

louiz’'s avatar
louiz’ committed
138
bool IrcClient::send_channel_message(const std::string& chan_name, const std::string& body)
139
{
louiz’'s avatar
louiz’ committed
140 141 142 143 144 145 146 147 148 149
  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
150 151 152 153 154
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
155 156 157 158 159 160 161 162 163
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
164 165 166 167
void IrcClient::send_pong_command(const IrcMessage& message)
{
  const std::string id = message.arguments[0];
  this->send_message(IrcMessage("PONG", {id}));
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
}

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
194
void IrcClient::on_channel_join(const IrcMessage& message)
195 196 197
{
  const std::string chan_name = message.arguments[0];
  IrcChannel* channel = this->get_channel(chan_name);
louiz’'s avatar
louiz’ committed
198 199 200 201 202 203 204 205 206 207 208
  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);
    }
209 210
}

louiz’'s avatar
louiz’ committed
211 212 213 214 215 216 217 218
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
219 220 221 222 223 224 225
  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
226 227
}

228 229 230 231 232 233 234 235 236 237 238 239 240
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);
241
}
louiz’'s avatar
louiz’ committed
242 243 244 245 246 247 248 249 250

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
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272

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
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294

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;
          this->bridge->send_muc_leave(std::move(iid), std::move(nick), std::move(txt), false);
        }
    }
}