~singpolyma/biboumi

5475d16b574e1daf9c1e5f94b5b821bfb1b9c8f8 — Florent Le Coz 9 years ago 71fec77
Return a stanza error whenever the IRCClient for a given server does not exist

Instead of ignoring the stanza, we send back an error of type
remote-server-not-found each time it's possible.

Also avoid having to do if (!irc) return; everytime.

fix #3045
3 files changed, 66 insertions(+), 41 deletions(-)

M src/bridge/bridge.cpp
M src/bridge/bridge.hpp
M src/xmpp/biboumi_component.cpp
M src/bridge/bridge.cpp => src/bridge/bridge.cpp +32 -39
@@ 106,6 106,18 @@ IrcClient* Bridge::get_irc_client(const std::string& hostname)
    }
  catch (const std::out_of_range& exception)
    {
      throw IRCNotConnected(hostname);
    }
}

IrcClient* Bridge::find_irc_client(const std::string& hostname)
{
  try
    {
      return this->irc_clients.at(hostname).get();
    }
  catch (const std::out_of_range& exception)
    {
      return nullptr;
    }
}


@@ 145,17 157,17 @@ bool Bridge::join_irc_channel(const Iid& iid, const std::string& username, const

void Bridge::send_channel_message(const Iid& iid, const std::string& body)
{
  if (iid.get_local().empty() || iid.get_server().empty())
  if (iid.get_server().empty())
    {
      log_warning("Cannot send message to channel: [" << iid.get_local() << "] on server [" << iid.get_server() << "]");
      this->xmpp->send_stanza_error("message", this->user_jid, std::to_string(iid), "",
                                    "cancel", "remote-server-not-found",
                                    std::to_string(iid) + " is not a valid channel name. "
                                    "A correct room jid is of the form: #<chan>%<server>",
                                    false);
      return;
    }
  IrcClient* irc = this->get_irc_client(iid.get_server());
  if (!irc)
    {
      log_warning("Cannot send message: no client exist for server " << iid.get_server());
      return;
    }

  // Because an IRC message cannot contain \n, we need to convert each line
  // of text into a separate IRC message. For conveniance, we also cut the
  // message into submessages on the XMPP side, this way the user of the


@@ 190,8 202,6 @@ void Bridge::forward_affiliation_role_change(const Iid& iid, const std::string& 
                                             const std::string& role)
{
  IrcClient* irc = this->get_irc_client(iid.get_server());
  if (!irc)
    return;
  IrcChannel* chan = irc->get_channel(iid.get_local());
  if (!chan || !chan->joined)
    return;


@@ 254,13 264,15 @@ void Bridge::forward_affiliation_role_change(const Iid& iid, const std::string& 
void Bridge::send_private_message(const Iid& iid, const std::string& body, const std::string& type)
{
  if (iid.get_local().empty() || iid.get_server().empty())
    return ;
  IrcClient* irc = this->get_irc_client(iid.get_server());
  if (!irc)
    {
      log_warning("Cannot send message: no client exist for server " << iid.get_server());
      this->xmpp->send_stanza_error("message", this->user_jid, std::to_string(iid), "",
                                    "cancel", "remote-server-not-found",
                                    std::to_string(iid) + " is not a valid channel name. "
                                    "A correct room jid is of the form: #<chan>%<server>",
                                    false);
      return;
    }
  IrcClient* irc = this->get_irc_client(iid.get_server());
  std::vector<std::string> lines = utils::split(body, '\n', true);
  if (lines.empty())
    return ;


@@ 276,26 288,19 @@ void Bridge::send_private_message(const Iid& iid, const std::string& body, const
void Bridge::send_raw_message(const std::string& hostname, const std::string& body)
{
  IrcClient* irc = this->get_irc_client(hostname);
  if (!irc)
    {
      log_warning("Cannot send message: no client exist for server " << hostname);
      return ;
    }
  irc->send_raw(body);
}

void Bridge::leave_irc_channel(Iid&& iid, std::string&& status_message)
{
  IrcClient* irc = this->get_irc_client(iid.get_server());
  if (irc)
    irc->send_part_command(iid.get_local(), status_message);
  irc->send_part_command(iid.get_local(), status_message);
}

void Bridge::send_irc_nick_change(const Iid& iid, const std::string& new_nick)
{
  IrcClient* irc = this->get_irc_client(iid.get_server());
  if (irc)
    irc->send_nick_command(new_nick);
  irc->send_nick_command(new_nick);
}

void Bridge::send_irc_channel_list_request(const Iid& iid, const std::string& iq_id,


@@ 303,10 308,8 @@ void Bridge::send_irc_channel_list_request(const Iid& iid, const std::string& iq
{
  IrcClient* irc = this->get_irc_client(iid.get_server());

  if (!irc)
    return;

  irc->send_list_command();

  irc_responder_callback_t cb = [this, iid, iq_id, to_jid](const std::string& irc_hostname,
                                                           const IrcMessage& message) -> bool
    {


@@ 340,9 343,6 @@ void Bridge::send_irc_kick(const Iid& iid, const std::string& target, const std:
{
  IrcClient* irc = this->get_irc_client(iid.get_server());

  if (!irc)
    return;

  irc->send_kick_command(iid.get_local(), target, reason);
  irc_responder_callback_t cb = [this, target, iq_id, to_jid, iid](const std::string& irc_hostname,
                                                                   const IrcMessage& message) -> bool


@@ 387,8 387,7 @@ void Bridge::send_irc_kick(const Iid& iid, const std::string& target, const std:
void Bridge::set_channel_topic(const Iid& iid, const std::string& subject)
{
  IrcClient* irc = this->get_irc_client(iid.get_server());
  if (irc)
    irc->send_topic_command(iid.get_local(), subject);
  irc->send_topic_command(iid.get_local(), subject);
}

void Bridge::send_xmpp_version_to_irc(const Iid& iid, const std::string& name, const std::string& version, const std::string& os)


@@ 447,12 446,6 @@ void Bridge::send_irc_participant_ping_request(const Iid& iid, const std::string
                                               const std::string& from_jid)
{
  IrcClient* irc = this->get_irc_client(iid.get_server());
  if (!irc)
    {
      this->xmpp->send_stanza_error("iq", to_jid, from_jid, iq_id, "cancel", "item-not-found",
                                    "Not connected to IRC server"s + iid.get_server(), true);
      return;
    }
  IrcChannel* chan = irc->get_channel(iid.get_local());
  if (!chan->joined)
    {


@@ 475,7 468,7 @@ void Bridge::on_gateway_ping(const std::string& irc_hostname, const std::string&
                             const std::string& from_jid)
{
  Jid jid(from_jid);
  if (irc_hostname.empty() || this->get_irc_client(irc_hostname))
  if (irc_hostname.empty() || this->find_irc_client(irc_hostname))
    this->xmpp->send_iq_result(iq_id, to_jid, jid.local);
  else
    this->xmpp->send_stanza_error("iq", to_jid, from_jid, iq_id, "cancel", "service-unavailable",


@@ 548,7 541,7 @@ void Bridge::send_presence_error(const Iid& iid, const std::string& nick,
void Bridge::send_muc_leave(Iid&& iid, std::string&& nick, const std::string& message, const bool self)
{
  this->xmpp->send_muc_leave(std::to_string(iid), std::move(nick), this->make_xmpp_body(message), this->user_jid, self);
  IrcClient* irc = this->get_irc_client(iid.get_server());
  IrcClient* irc = this->find_irc_client(iid.get_server());
  if (irc && irc->number_of_joined_channels() == 0)
    irc->send_quit_command("");
}


@@ 603,7 596,7 @@ void Bridge::send_topic(const std::string& hostname, const std::string& chan_nam

std::string Bridge::get_own_nick(const Iid& iid)
{
  IrcClient* irc = this->get_irc_client(iid.get_server());
  IrcClient* irc = this->find_irc_client(iid.get_server());
  if (irc)
    return irc->get_own_nick();
  return "";

M src/bridge/bridge.hpp => src/bridge/bridge.hpp +14 -2
@@ 9,6 9,7 @@

#include <unordered_map>
#include <functional>
#include <exception>
#include <string>
#include <memory>



@@ 193,11 194,15 @@ private:
   */
  IrcClient* get_irc_client(const std::string& hostname, const std::string& username);
  /**
   * This version does not create the IrcClient if it does not exist, and
   * returns nullptr in that case
   * This version does not create the IrcClient if it does not exist, throws
   * a IRCServerNotConnected error in that case.
   */
  IrcClient* get_irc_client(const std::string& hostname);
  /**
   * Idem, but returns nullptr if the server does not exist.
   */
  IrcClient* find_irc_client(const std::string& hostname);
  /**
   * The JID of the user associated with this bridge. Messages from/to this
   * JID are only managed by this bridge.
   */


@@ 240,4 245,11 @@ private:
  Bridge& operator=(Bridge&&) = delete;
};

struct IRCNotConnected: public std::exception
{
  IRCNotConnected(const std::string& hostname):
    hostname(hostname) {}
  const std::string hostname;
};

#endif // BRIDGE_INCLUDED

M src/xmpp/biboumi_component.cpp => src/xmpp/biboumi_component.cpp +20 -0
@@ 166,6 166,8 @@ void BiboumiComponent::handle_message(const Stanza& stanza)
                              error_type, error_name, "");
    });
  XmlNode* body = stanza.get_child("body", COMPONENT_NS);

  try {                         // catch IRCNotConnected exceptions
  if (type == "groupchat" && iid.is_channel)
    {
      if (body && !body->get_inner().empty())


@@ 220,6 222,13 @@ void BiboumiComponent::handle_message(const Stanza& stanza)
    }
  else if (iid.is_user)
    this->send_invalid_user_error(to.local, from);
  } catch (const IRCNotConnected& ex)
    {
      this->send_stanza_error("message", from, to_str, id,
                              "cancel", "remote-server-not-found",
                              "Not connected to IRC server "s + ex.hostname,
                              true);
    }
  stanza_error.disable();
}



@@ 262,6 271,7 @@ void BiboumiComponent::handle_iq(const Stanza& stanza)
      this->send_stanza_error("iq", from, to_str, id,
                              error_type, error_name, "");
    });
  try {
  if (type == "set")
    {
      XmlNode* query;


@@ 412,6 422,16 @@ void BiboumiComponent::handle_iq(const Stanza& stanza)
            }
        }
    }
  }
  catch (const IRCNotConnected& ex)
    {
      this->send_stanza_error("iq", from, to_str, id,
                              "cancel", "remote-server-not-found",
                              "Not connected to IRC server "s + ex.hostname,
                              true);
      stanza_error.disable();
      return;
    }
  error_type = "cancel";
  error_name = "feature-not-implemented";
}