irc_client.cpp 6.17 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 79
      else if (message.command == "NOTICE" ||
               message.command == "375" ||
               message.command == "372")
        this->forward_server_message(message);
      else if (message.command == "JOIN")
        this->on_self_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 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
    }
}

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

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

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
126 127 128 129 130
  if (this->welcomed == false)
    {
      this->channels_to_join.push_back(chan_name);
      return ;
    }
131 132 133 134 135
  IrcChannel* channel = this->get_channel(chan_name);
  if (channel->joined == false)
    this->send_message(IrcMessage("JOIN", {chan_name}));
}

louiz’'s avatar
louiz’ committed
136
bool IrcClient::send_channel_message(const std::string& chan_name, const std::string& body)
137
{
louiz’'s avatar
louiz’ committed
138 139 140 141 142 143 144 145 146 147 148 149 150 151
  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;
}

void IrcClient::send_pong_command(const IrcMessage& message)
{
  const std::string id = message.arguments[0];
  this->send_message(IrcMessage("PONG", {id}));
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
}

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

void IrcClient::on_self_channel_join(const IrcMessage& message)
{
  const std::string chan_name = message.arguments[0];
  IrcChannel* channel = this->get_channel(chan_name);
  channel->joined = true;
  channel->set_self(message.prefix);
}

louiz’'s avatar
louiz’ committed
186 187 188 189 190 191 192 193 194 195 196
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];
  this->bridge->send_muc_message(iid, nick, body);
}

197 198 199 200 201 202 203 204 205 206 207 208 209
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);
210
}
louiz’'s avatar
louiz’ committed
211 212 213 214 215 216 217 218 219

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