~singpolyma/biboumi

34fc1d4010d23be947c00fc956f2bdded2374cee — Florent Le Coz 8 years ago 2c932cf
Implement a basic webirc support

See https://kiwiirc.com/docs/webirc

fix #3135
4 files changed, 70 insertions(+), 6 deletions(-)

M doc/biboumi.1.md
M src/bridge/bridge.cpp
M src/irc/irc_client.cpp
M src/irc/irc_client.hpp
M doc/biboumi.1.md => doc/biboumi.1.md +7 -0
@@ 96,6 96,13 @@ The configuration file uses a simple format of the form
 username of each user will be set to the nick they used to connect to the
 IRC server.

`webirc_password`

 Configure a password to be communicated to the IRC server, as part of the
 WEBIRC message (see https://kiwiirc.com/docs/webirc).  If this option is
 set, an additional DNS resolution of the hostname of each XMPP server will
 be made when connecting to an IRC server.

`log_file`

  A filename into which logs are written.  If none is provided, the logs are

M src/bridge/bridge.cpp => src/bridge/bridge.cpp +5 -3
@@ 56,7 56,8 @@ void Bridge::clean()
  while (it != this->irc_clients.end())
  {
    IrcClient* client = it->second.get();
    if (!client->is_connected() && !client->is_connecting())
    if (!client->is_connected() && !client->is_connecting() &&
        !client->get_resolver().is_resolving())
      it = this->irc_clients.erase(it);
    else
      ++it;


@@ 94,16 95,17 @@ IrcClient* Bridge::make_irc_client(const std::string& hostname, const std::strin
    {
      auto username = nickname;
      auto realname = nickname;
      Jid jid(this->user_jid);
      if (Config::get("realname_from_jid", "false") == "true")
        {
          Jid jid(this->user_jid);
          username = jid.local;
          realname = this->get_bare_jid();
        }
      this->irc_clients.emplace(hostname,
                                std::make_shared<IrcClient>(this->poller, hostname,
                                                            nickname, username,
                                                            realname, this));
                                                            realname, jid.domain,
                                                            this));
      std::shared_ptr<IrcClient> irc = this->irc_clients.at(hostname);
      return irc.get();
    }

M src/irc/irc_client.cpp => src/irc/irc_client.cpp +42 -2
@@ 26,9 26,11 @@ using namespace std::chrono_literals;

IrcClient::IrcClient(std::shared_ptr<Poller> poller, const std::string& hostname,
                     const std::string& nickname, const std::string& username,
                     const std::string& realname, Bridge* bridge):
                     const std::string& realname, const std::string& user_hostname,
                     Bridge* bridge):
  TCPSocketHandler(poller),
  hostname(hostname),
  user_hostname(user_hostname),
  username(username),
  realname(realname),
  current_nick(nickname),


@@ 113,6 115,39 @@ void IrcClient::on_connection_failed(const std::string& reason)

void IrcClient::on_connected()
{
  const auto webirc_password = Config::get("webirc_password", "");
  static std::string resolved_ip;

  if (!webirc_password.empty())
    {
      if (!resolved_ip.empty())
        this->send_webirc_command(webirc_password, resolved_ip);
      else
        {  // Start resolving the hostname of the user, and call
           // on_connected again when it’s done
          this->dns_resolver.resolve(this->user_hostname, "5222",
                                     [this](const struct addrinfo* addr)
                                     {
                                       resolved_ip = addr_to_string(addr);
                                       // Only continue the process if we
                                       // didn’t get connected while we were
                                       // resolving
                                       if (this->is_connected())
                                         this->on_connected();
                                     },
                                     [this](const char* error_msg)
                                     {
                                       if (this->is_connected())
                                         {
                                           this->on_connection_close("Could not resolve hostname "s + this->user_hostname +
                                                                     ": " + error_msg);
                                           this->send_quit_command("");
                                         }
                                     });
          return;
        }
    }

#ifdef USE_DATABASE
  auto options = Database::get_irc_server_options(this->bridge->get_bare_jid(),
                                                  this->get_hostname());


@@ 253,7 288,7 @@ void IrcClient::send_raw(const std::string& txt)

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

void IrcClient::send_nick_command(const std::string& nick)


@@ 266,6 301,11 @@ void IrcClient::send_pass_command(const std::string& password)
  this->send_message(IrcMessage("PASS", {password}));
}

void IrcClient::send_webirc_command(const std::string& password, const std::string& user_ip)
{
  this->send_message(IrcMessage("WEBIRC", {password, "biboumi", this->user_hostname, user_ip}));
}

void IrcClient::send_kick_command(const std::string& chan_name, const std::string& target, const std::string& reason)
{
  this->send_message(IrcMessage("KICK", {chan_name, target, reason}));

M src/irc/irc_client.hpp => src/irc/irc_client.hpp +16 -1
@@ 6,6 6,7 @@
#include <irc/iid.hpp>

#include <network/tcp_socket_handler.hpp>
#include <network/resolver.hpp>

#include <unordered_map>
#include <utility>


@@ 27,7 28,8 @@ class IrcClient: public TCPSocketHandler
public:
  explicit IrcClient(std::shared_ptr<Poller> poller, const std::string& hostname,
                     const std::string& nickname, const std::string& username,
                     const std::string& realname, Bridge* bridge);
                     const std::string& realname, const std::string& user_hostname,
                     Bridge* bridge);
  ~IrcClient();
  /**
   * Connect to the IRC server


@@ 88,6 90,7 @@ public:
   */
  void send_nick_command(const std::string& username);
  void send_pass_command(const std::string& password);
  void send_webirc_command(const std::string& password, const std::string& user_ip);
  /**
   * Send the JOIN irc command.
   */


@@ 250,12 253,19 @@ public:
  std::string get_nick() const { return this->current_nick; }
  bool is_welcomed() const { return this->welcomed; }

  const Resolver& get_resolver() const;

private:
  /**
   * The hostname of the server we are connected to.
   */
  const std::string hostname;
  /**
   * The hostname of the user.  This is used in the USER and the WEBIRC
   * commands, but only the one in WEBIRC will be used by the IRC server.
   */
  const std::string user_hostname;
  /**
   * The username used in the USER irc command
   */
  std::string username;


@@ 330,6 340,11 @@ private:
   * A set of (lowercase) nicknames to which we sent a private message.
   */
  std::set<std::string> nicks_to_treat_as_private;
  /**
   * DNS resolver, used to resolve the hostname of the user if we are using
   * the WebIRC protocole.
   */
  Resolver dns_resolver;

  IrcClient(const IrcClient&) = delete;
  IrcClient(IrcClient&&) = delete;