irc_client.cpp 3.97 KB
Newer Older
1
#include <libirc/irc_client.hpp>
louiz’'s avatar
louiz’ committed
2
#include <libirc/irc_message.hpp>
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
#include <network/poller.hpp>
#include <utils/scopeguard.hpp>

#include <sys/types.h>
#include <sys/socket.h>
#include <cstring>
#include <netdb.h>
#include <unistd.h>

#include <iostream>
#include <stdexcept>

IrcClient::IrcClient()
{
  std::cout << "IrcClient()" << std::endl;
  if ((this->socket = ::socket(AF_INET, SOCK_STREAM, 0)) == -1)
    throw std::runtime_error("Could not create socket");
}

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

void IrcClient::on_recv()
{
  char buf[4096];

  ssize_t size = ::recv(this->socket, buf, 4096, 0);
  if (0 == size)
    this->on_connection_close();
  else if (-1 == static_cast<ssize_t>(size))
    throw std::runtime_error("Error reading from socket");
  else
    {
      this->in_buf += std::string(buf, size);
      this->parse_in_buffer();
    }
}

void IrcClient::on_send()
{
louiz’'s avatar
louiz’ committed
45 46 47 48 49 50 51 52 53 54 55 56
  const ssize_t res = ::send(this->socket, this->out_buf.data(), this->out_buf.size(), 0);
  if (res == -1)
    {
      perror("send");
      this->close();
    }
  else
    {
      this->out_buf = this->out_buf.substr(res, std::string::npos);
      if (this->out_buf.empty())
        this->poller->stop_watching_send_events(this);
    }
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
}

socket_t IrcClient::get_socket() const
{
  return this->socket;
}

void IrcClient::connect(const std::string& address, const std::string& port)
{
  std::cout << "Trying to connect to " << address << ":" << port << std::endl;
  struct addrinfo hints;
  memset(&hints, 0, sizeof(struct addrinfo));
  hints.ai_flags = 0;
  hints.ai_family = AF_INET;
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_protocol = 0;

  struct addrinfo* addr_res;
  const int res = ::getaddrinfo(address.c_str(), port.c_str(), &hints, &addr_res);
  // Make sure the alloced structure is always freed at the end of the
  // function
  utils::ScopeGuard sg([&addr_res](){ freeaddrinfo(addr_res); });

  if (res != 0)
    {
      perror("getaddrinfo");
      throw std::runtime_error("getaddrinfo failed");
    }
  for (struct addrinfo* rp = addr_res; rp; rp = rp->ai_next)
    {
      std::cout << "One result" << std::endl;
      if (::connect(this->socket, rp->ai_addr, rp->ai_addrlen) == 0)
        {
          std::cout << "Connection success." << std::endl;
louiz’'s avatar
louiz’ committed
91
          this->on_connected();
92 93 94 95 96 97 98 99 100
          return ;
        }
      std::cout << "Connection failed:" << std::endl;
      perror("connect");
    }
  std::cout << "All connection attempts failed." << std::endl;
  this->close();
}

louiz’'s avatar
louiz’ committed
101 102 103 104
void IrcClient::on_connected()
{
}

105 106 107 108 109 110 111 112 113 114 115 116 117 118
void IrcClient::on_connection_close()
{
  std::cout << "Connection closed by remote server." << std::endl;
  this->close();
}

void IrcClient::close()
{
  this->poller->remove_socket_handler(this->get_socket());
  ::close(this->socket);
}

void IrcClient::parse_in_buffer()
{
louiz’'s avatar
louiz’ committed
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
  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;
    }
}

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";
  this->out_buf += res;
  if (!this->out_buf.empty())
    {
      this->poller->watch_send_events(this);
    }
}

void IrcClient::send_user_command(const std::string& username, const std::string& realname)
{
  this->send_message(IrcMessage("USER", {username, "NONE", "NONE", realname}));
}

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)
{
  this->send_message(IrcMessage("JOIN", {chan_name}));
166
}