~singpolyma/biboumi

2c5d3d89b0eb1e6b8d888b4d37ceca6f23c2e314 — Florent Le Coz 9 years ago a447214
Change IRC modes when receiving an affiliation/role change request

fix #2946
3 files changed, 81 insertions(+), 7 deletions(-)

M src/bridge/bridge.cpp
M src/bridge/bridge.hpp
M src/xmpp/xmpp_component.cpp
M src/bridge/bridge.cpp => src/bridge/bridge.cpp +66 -0
@@ 183,6 183,72 @@ void Bridge::send_channel_message(const Iid& iid, const std::string& body)
    }
}

void Bridge::forward_affiliation_role_change(const Iid& iid, const std::string& nick,
                                             const std::string& affiliation,
                                             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;
  IrcUser* user = chan->find_user(nick);
  if (!user)
    return;
  // For each affiliation or role, we have a “maximal” mode that we want to
  // set. We must remove any superior mode at the same time. For example if
  // the user already has +o mode, and we set its affiliation to member, we
  // remove the +o mode, and add +v.  For each “superior” mode (for example,
  // for +v, the superior modes are 'h', 'a', 'o' and 'q') we check if that
  // user has it, and if yes we remove that mode

  std::size_t nb = 1;               // the number of times the nick must be
                                    // repeated in the argument list
  std::string modes;                // The string of modes to
                                    // add/remove. For example "+v-aoh"
  std::vector<char> modes_to_remove; // List of modes to check for removal
  if (affiliation == "none")
    {
      modes = "";
      nb = 0;
      modes_to_remove = {'v', 'h', 'o', 'a', 'q'};
    }
  else if (affiliation == "member")
    {
      modes = "+v";
      modes_to_remove = {'h', 'o', 'a', 'q'};
    }
  else if (role == "moderator")
    {
      modes = "+h";
      modes_to_remove = {'o', 'a', 'q'};
    }
  else if (affiliation == "admin")
    {
      modes = "+o";
      modes_to_remove = {'a', 'q'};
    }
  else if (affiliation == "owner")
    {
      modes = "+a";
      modes_to_remove = {'q'};
    }
  else
    return;
  for (const char mode: modes_to_remove)
    if (user->modes.find(mode) != user->modes.end())
      {
        modes += "-"s + mode;
        nb++;
      }
  if (modes.empty())
    return;
  std::vector<std::string> args(nb, nick);
  args.insert(args.begin(), modes);
  irc->send_mode_command(iid.get_local(), args);
}

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

M src/bridge/bridge.hpp => src/bridge/bridge.hpp +2 -0
@@ 72,6 72,8 @@ public:
  void send_irc_version_request(const std::string& irc_hostname, const std::string& target,
                                const std::string& iq_id, const std::string& to_jid,
                                const std::string& from_jid);
  void forward_affiliation_role_change(const Iid& iid, const std::string& nick,
                                       const std::string& affiliation, const std::string& role);
  /**
   * Directly send a CTCP PING request to the IRC user
   */

M src/xmpp/xmpp_component.cpp => src/xmpp/xmpp_component.cpp +13 -7
@@ 481,14 481,20 @@ void XmppComponent::handle_iq(const Stanza& stanza)
            {
              std::string nick = child->get_tag("nick");
              std::string role = child->get_tag("role");
              if (!nick.empty() && role == "none")
                {               // This is a kick
                  std::string reason;
                  XmlNode* reason_el = child->get_child("reason", MUC_ADMIN_NS);
                  if (reason_el)
                    reason = reason_el->get_inner();
              std::string affiliation = child->get_tag("affiliation");
              if (!nick.empty())
                {
                  Iid iid(to.local);
                  bridge->send_irc_kick(iid, nick, reason, id, from);
                  if (role == "none")
                    {               // This is a kick
                      std::string reason;
                      XmlNode* reason_el = child->get_child("reason", MUC_ADMIN_NS);
                      if (reason_el)
                        reason = reason_el->get_inner();
                      bridge->send_irc_kick(iid, nick, reason, id, from);
                    }
                  else
                    bridge->forward_affiliation_role_change(iid, nick, affiliation, role);
                  stanza_error.disable();
                }
            }