~singpolyma/biboumi

82256d40a32e1401c7884b1c9ff26ea77d775d3d — Florent Le Coz 8 years ago c01befb + c307df8
Merge branch 'fixed_server'
M doc/biboumi.1.md => doc/biboumi.1.md +18 -0
@@ 59,6 59,24 @@ The configuration file uses a simple format of the form
  privileges), for example some administration ad-hoc commands will only be
  available to that JID.

`fixed_irc_server`

  If this option contains the hostname of an IRC server (for example
  irc.example.org), then biboumi will enforce the connexion to that IRC
  server only.  This means that a JID like "#chan@biboumi.example.com" must
  be used instead of "#chan%irc.example.org@biboumi.example.com".  In that
  mode, the virtual channel (see *Connect to an IRC server*) is not
  available and you still need to use the ! separator to send message to an
  IRC user (for example "foo!@biboumi.example.com" to send a message to
  foo), although the in-room JID still work as expected
  ("#channel@biboumi.example.com/Nick").  On the other hand, the '%' lose
  any meaning.  It can appear in the JID but will not be interpreted as a
  separator (thus the JID "#channel%hello@biboumi.example.com" points to the
  channel named "#channel%hello" on the configured IRC server) This option
  can for example be used by an administrator that just wants to let their
  users join their own IRC server using an XMPP client, while forbidding
  access to any other 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 -4
@@ 5,6 5,7 @@
#include <xmpp/xmpp_stanza.hpp>
#include <irc/irc_message.hpp>
#include <network/poller.hpp>
#include <utils/empty_if_fixed_server.hpp>
#include <utils/encoding.hpp>
#include <utils/tolower.hpp>
#include <logger/logger.hpp>


@@ 580,13 581,13 @@ void Bridge::send_user_join(const std::string& hostname,
  std::string role;
  std::tie(role, affiliation) = get_role_affiliation_from_irc_mode(user_mode);

  this->xmpp->send_user_join(chan_name + "%" + hostname, user->nick, user->host,
  this->xmpp->send_user_join(chan_name + utils::empty_if_fixed_server("%" + hostname), user->nick, user->host,
                             affiliation, role, this->user_jid, self);
}

void Bridge::send_topic(const std::string& hostname, const std::string& chan_name, const std::string& topic)
{
  this->xmpp->send_topic(chan_name + "%" + hostname, this->make_xmpp_body(topic), this->user_jid);
  this->xmpp->send_topic(chan_name + utils::empty_if_fixed_server("%" + hostname), this->make_xmpp_body(topic), this->user_jid);
}

std::string Bridge::get_own_nick(const Iid& iid)


@@ 623,7 624,7 @@ void Bridge::send_affiliation_role_change(const Iid& iid, const std::string& tar

void Bridge::send_iq_version_request(const std::string& nick, const std::string& hostname)
{
  this->xmpp->send_iq_version_request(nick + "!" + hostname, this->user_jid);
  this->xmpp->send_iq_version_request(nick + "!" + utils::empty_if_fixed_server(hostname), this->user_jid);
}

void Bridge::send_xmpp_ping_request(const std::string& nick, const std::string& hostname,


@@ 632,7 633,7 @@ void Bridge::send_xmpp_ping_request(const std::string& nick, const std::string& 
  // Use revstr because the forwarded ping to target XMPP user must not be
  // the same that the request iq, but we also need to get it back easily
  // (revstr again)
  this->xmpp->send_ping_request(nick + "!" + hostname, this->user_jid, utils::revstr(id));
  this->xmpp->send_ping_request(nick + "!" + utils::empty_if_fixed_server(hostname), this->user_jid, utils::revstr(id));
}

void Bridge::set_preferred_from_jid(const std::string& nick, const std::string& full_jid)

M src/irc/iid.cpp => src/irc/iid.cpp +40 -1
@@ 1,4 1,5 @@
#include <utils/tolower.hpp>
#include <config/config.hpp>

#include <irc/iid.hpp>



@@ 6,6 7,16 @@ Iid::Iid(const std::string& iid):
  is_channel(false),
  is_user(false)
{
  const std::string fixed_irc_server = Config::get("fixed_irc_server", "");
  if (fixed_irc_server.empty())
    this->init(iid);
  else
    this->init_with_fixed_server(iid, fixed_irc_server);
}


void Iid::init(const std::string& iid)
{
  const std::string::size_type sep = iid.find_first_of("%!");
  if (sep != std::string::npos)
    {


@@ 20,6 31,26 @@ Iid::Iid(const std::string& iid):
    this->set_server(iid);
}

void Iid::init_with_fixed_server(const std::string& iid, const std::string& hostname)
{
  this->set_server(hostname);

  const std::string::size_type sep = iid.find("!");

  // Without any separator, we consider that it's a channel
  if (sep == std::string::npos)
    {
      this->is_channel = true;
      this->set_local(iid);
    }
  else // A separator can be present to differenciate a channel from a user,
       // but the part behind it (the hostname) is ignored
    {
      this->set_local(iid.substr(0, sep));
        this->is_user = true;
    }
}

Iid::Iid(const Iid& other):
  is_channel(other.is_channel),
  is_user(other.is_user),


@@ 66,6 97,14 @@ std::string Iid::get_sep() const
namespace std {
  const std::string to_string(const Iid& iid)
  {
    return iid.get_local() + iid.get_sep() + iid.get_server();
    if (Config::get("fixed_irc_server", "").empty())
      return iid.get_local() + iid.get_sep() + iid.get_server();
    else
      {
        if (iid.get_sep() == "!")
          return iid.get_local() + iid.get_sep();
        else
          return iid.get_local();
      }
  }
}

M src/irc/iid.hpp => src/irc/iid.hpp +4 -0
@@ 57,6 57,10 @@ public:
  std::string get_sep() const;

private:

  void init(const std::string& iid);
  void init_with_fixed_server(const std::string& iid, const std::string& hostname);

  std::string local;
  std::string server;


M src/test.cpp => src/test.cpp +94 -32
@@ 303,38 303,100 @@ int main()
  /**
   * IID parsing
   */
  std::cout << color << "Testing IID parsing…" << reset << std::endl;
  Iid iid1("foo!irc.example.org");
  std::cout << std::to_string(iid1) << std::endl;
  assert(std::to_string(iid1) == "foo!irc.example.org");
  assert(iid1.get_local() == "foo");
  assert(iid1.get_server() == "irc.example.org");
  assert(!iid1.is_channel);
  assert(iid1.is_user);

  Iid iid2("#test%irc.example.org");
  std::cout << std::to_string(iid2) << std::endl;
  assert(std::to_string(iid2) == "#test%irc.example.org");
  assert(iid2.get_local() == "#test");
  assert(iid2.get_server() == "irc.example.org");
  assert(iid2.is_channel);
  assert(!iid2.is_user);

  Iid iid3("%irc.example.org");
  std::cout << std::to_string(iid3) << std::endl;
  assert(std::to_string(iid3) == "%irc.example.org");
  assert(iid3.get_local() == "");
  assert(iid3.get_server() == "irc.example.org");
  assert(iid3.is_channel);
  assert(!iid3.is_user);

  Iid iid4("irc.example.org");
  std::cout << std::to_string(iid4) << std::endl;
  assert(std::to_string(iid4) == "irc.example.org");
  assert(iid4.get_local() == "");
  assert(iid4.get_server() == "irc.example.org");
  assert(!iid4.is_channel);
  assert(!iid4.is_user);
  {
    std::cout << color << "Testing IID parsing…" << reset << std::endl;
    Iid iid1("foo!irc.example.org");
    std::cout << std::to_string(iid1) << std::endl;
    assert(std::to_string(iid1) == "foo!irc.example.org");
    assert(iid1.get_local() == "foo");
    assert(iid1.get_server() == "irc.example.org");
    assert(!iid1.is_channel);
    assert(iid1.is_user);

    Iid iid2("#test%irc.example.org");
    std::cout << std::to_string(iid2) << std::endl;
    assert(std::to_string(iid2) == "#test%irc.example.org");
    assert(iid2.get_local() == "#test");
    assert(iid2.get_server() == "irc.example.org");
    assert(iid2.is_channel);
    assert(!iid2.is_user);

    Iid iid3("%irc.example.org");
    std::cout << std::to_string(iid3) << std::endl;
    assert(std::to_string(iid3) == "%irc.example.org");
    assert(iid3.get_local() == "");
    assert(iid3.get_server() == "irc.example.org");
    assert(iid3.is_channel);
    assert(!iid3.is_user);

    Iid iid4("irc.example.org");
    std::cout << std::to_string(iid4) << std::endl;
    assert(std::to_string(iid4) == "irc.example.org");
    assert(iid4.get_local() == "");
    assert(iid4.get_server() == "irc.example.org");
    assert(!iid4.is_channel);
    assert(!iid4.is_user);

    Iid iid5("nick!");
    std::cout << std::to_string(iid5) << std::endl;
    assert(std::to_string(iid5) == "nick!");
    assert(iid5.get_local() == "nick");
    assert(iid5.get_server() == "");
    assert(!iid5.is_channel);
    assert(iid5.is_user);

    Iid iid6("##channel%");
    std::cout << std::to_string(iid6) << std::endl;
    assert(std::to_string(iid6) == "##channel%");
    assert(iid6.get_local() == "##channel");
    assert(iid6.get_server() == "");
    assert(iid6.is_channel);
    assert(!iid6.is_user);
  }

  {
    std::cout << color << "Testing IID parsing with a fixed server configured…" << reset << std::endl;
    // Now do the same tests, but with a configured fixed_irc_server
    Config::set("fixed_irc_server", "fixed.example.com", false);

    Iid iid1("foo!irc.example.org");
    std::cout << std::to_string(iid1) << std::endl;
    assert(std::to_string(iid1) == "foo!");
    assert(iid1.get_local() == "foo");
    assert(iid1.get_server() == "fixed.example.com");
    assert(!iid1.is_channel);
    assert(iid1.is_user);

    Iid iid2("#test%irc.example.org");
    std::cout << std::to_string(iid2) << std::endl;
    assert(std::to_string(iid2) == "#test%irc.example.org");
    assert(iid2.get_local() == "#test%irc.example.org");
    assert(iid2.get_server() == "fixed.example.com");
    assert(iid2.is_channel);
    assert(!iid2.is_user);

    // Note that it is impossible to adress the IRC server directly, or to
    // use the virtual channel, in that mode

    // Iid iid3("%irc.example.org");
    // Iid iid4("irc.example.org");

    Iid iid5("nick!");
    std::cout << std::to_string(iid5) << std::endl;
    assert(std::to_string(iid5) == "nick!");
    assert(iid5.get_local() == "nick");
    assert(iid5.get_server() == "fixed.example.com");
    assert(!iid5.is_channel);
    assert(iid5.is_user);

    Iid iid6("##channel%");
    std::cout << std::to_string(iid6) << std::endl;
    assert(std::to_string(iid6) == "##channel%");
    assert(iid6.get_local() == "##channel%");
    assert(iid6.get_server() == "fixed.example.com");
    assert(iid6.is_channel);
    assert(!iid6.is_user);
  }

  return 0;
}

A src/utils/empty_if_fixed_server.hpp => src/utils/empty_if_fixed_server.hpp +26 -0
@@ 0,0 1,26 @@
#ifndef EMPTY_IF_FIXED_SERVER_HPP_INCLUDED
#define EMPTY_IF_FIXED_SERVER_HPP_INCLUDED

#include <string>

#include <config/config.hpp>

namespace utils
{
  inline std::string empty_if_fixed_server(std::string&& str)
  {
    if (!Config::get("fixed_irc_server", "").empty())
      return {};
    return str;
  }

  inline std::string empty_if_fixed_server(const std::string& str)
  {
    if (!Config::get("fixed_irc_server", "").empty())
      return {};
    return str;
  }

}

#endif /* EMPTY_IF_FIXED_SERVER_HPP_INCLUDED */