~singpolyma/biboumi

e397fc837e00cff58081810c8b54cb741299d993 — louiz’ 6 years ago 36a9eb0
Send iq error/result when the user changed a MODE command  with an iq

And add tests for all the mode changes
M src/bridge/bridge.cpp => src/bridge/bridge.cpp +59 -3
@@ 263,9 263,11 @@ 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,
void Bridge::forward_affiliation_role_change(const Iid& iid, const std::string& from,
                                             const std::string& nick,
                                             const std::string& affiliation,
                                             const std::string& role)
                                             const std::string& role,
                                             const std::string& id)
{
  IrcClient* irc = this->get_irc_client(iid.get_server());
  IrcChannel* chan = irc->get_channel(iid.get_local());


@@ 273,7 275,11 @@ void Bridge::forward_affiliation_role_change(const Iid& iid, const std::string& 
    return;
  IrcUser* user = chan->find_user(nick);
  if (!user)
    return;
    {
      this->xmpp.send_stanza_error("iq", from, std::to_string(iid), id, "cancel",
                                   "item-not-found", "no such nick", false);
      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


@@ 325,6 331,56 @@ void Bridge::forward_affiliation_role_change(const Iid& iid, const std::string& 
  std::vector<std::string> args(nb, nick);
  args.insert(args.begin(), modes);
  irc->send_mode_command(iid.get_local(), args);

  irc_responder_callback_t cb = [this, iid, irc, id, from, nick](const std::string& irc_hostname, const IrcMessage& message) -> bool
  {
    if (irc_hostname != iid.get_server())
      return false;

    if (message.command == "MODE" && message.arguments.size() >= 2)
      {
        const std::string& chan_name = message.arguments[0];
        if (chan_name != iid.get_local())
          return false;
        const std::string actor_nick = IrcUser{message.prefix}.nick;
        if (!irc || irc->get_own_nick() != actor_nick)
          return false;

        this->xmpp.send_iq_result(id, from, std::to_string(iid));
      }
    else if (message.command == "401" && message.arguments.size() >= 2)
        {
          const std::string target_later = message.arguments[1];
          if (target_later != nick)
            return false;
          std::string error_message = "No such nick";
          if (message.arguments.size() >= 3)
            error_message = message.arguments[2];
          this->xmpp.send_stanza_error("iq", from, std::to_string(iid), id, "cancel", "item-not-found",
                                        error_message, false);
        }
    else if (message.command == "482" && message.arguments.size() >= 2)
      {
        const std::string chan_name_later = utils::tolower(message.arguments[1]);
        if (chan_name_later != iid.get_local())
          return false;
        std::string error_message = "You're not channel operator";
        if (message.arguments.size() >= 3)
          error_message = message.arguments[2];
        this->xmpp.send_stanza_error("iq", from, std::to_string(iid), id, "cancel", "not-allowed",
                                     error_message, false);
      }
    else if (message.command == "472" && message.arguments.size() >= 2)
      {
          std::string error_message = "Unknown mode: "s + message.arguments[1];
          if (message.arguments.size() >= 3)
            error_message = message.arguments[2];
          this->xmpp.send_stanza_error("iq", from, std::to_string(iid), id, "cancel", "not-allowed",
                                        error_message, false);
      }
    return true;
  };
  this->add_waiting_irc(std::move(cb));
}

void Bridge::send_private_message(const Iid& iid, const std::string& body, const std::string& type)

M src/bridge/bridge.hpp => src/bridge/bridge.hpp +2 -2
@@ 103,8 103,8 @@ public:
  bool send_matching_channel_list(const ChannelList& channel_list,
                                  const ResultSetInfo& rs_info, const std::string& id, const std::string& to_jid,
                                  const std::string& from);
  void forward_affiliation_role_change(const Iid& iid, const std::string& nick,
                                       const std::string& affiliation, const std::string& role);
  void forward_affiliation_role_change(const Iid& iid, const std::string& from, const std::string& nick,
                                       const std::string& affiliation, const std::string& role, const std::string& id);
  /**
   * Directly send a CTCP PING request to the IRC user
   */

M src/xmpp/biboumi_component.cpp => src/xmpp/biboumi_component.cpp +1 -1
@@ 352,7 352,7 @@ void BiboumiComponent::handle_iq(const Stanza& stanza)
                      bridge->send_irc_kick(iid, nick, reason, id, from);
                    }
                  else
                    bridge->forward_affiliation_role_change(iid, nick, affiliation, role);
                    bridge->forward_affiliation_role_change(iid, from, nick, affiliation, role, id);
                  stanza_error.disable();
                }
            }

M tests/end_to_end/__main__.py => tests/end_to_end/__main__.py +76 -0
@@ 1095,6 1095,82 @@ if __name__ == '__main__':
                             ("/iq[@id='kick1'][@type='result']",),
                     ]),
                ]),
        Scenario("mode_change",
                [
                     handshake_sequence(),
                     # First user joins
                     partial(send_stanza,
                     "<presence from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}/{nick_one}' />"),
                     connection_sequence("irc.localhost", '{jid_one}/{resource_one}'),
                     partial(expect_stanza, "/message"),
                     partial(expect_stanza, "/presence/muc_user:x/muc_user:status[@code='110']"),
                     partial(expect_stanza, "/message[@type='groupchat']/subject"),

                     # Second user joins
                     partial(send_stanza,
                     "<presence from='{jid_two}/{resource_one}' to='#foo%{irc_server_one}/{nick_two}' />"),
                     connection_sequence("irc.localhost", '{jid_two}/{resource_one}'),
                     partial(expect_unordered, [
                         ("/presence/muc_user:x/muc_user:item[@affiliation='none'][@role='participant']",),
                         ("/presence/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",),
                         ("/presence/muc_user:x/muc_user:status[@code='110']",),
                         ("/message/subject",),
                         ]),

                     # Change a user mode with a message starting with /mode
                    partial(send_stanza,
                            "<message from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}' type='groupchat'><body>/mode +v {nick_two}</body></message>"),
                    partial(expect_unordered, [
                        ("/message[@to='{jid_one}/{resource_one}']/body[text()='Mode #foo [+v {nick_two}] by {nick_one}']",),
                        ("/message[@to='{jid_two}/{resource_one}']/body[text()='Mode #foo [+v {nick_two}] by {nick_one}']",),
                        ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='member'][@role='participant']",),
                        ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='member'][@role='participant']",)
                        ]),

                    # using an iq
                    partial(send_stanza,
                            "<iq from='{jid_one}/{resource_one}' id='id1' to='#foo%{irc_server_one}' type='set'><query xmlns='http://jabber.org/protocol/muc#admin'><item affiliation='admin' nick='{nick_two}'/></query></iq>"),
                    partial(expect_unordered, [
                        ("/message[@to='{jid_one}/{resource_one}']/body[text()='Mode #foo [+o {nick_two}] by {nick_one}']",),
                        ("/message[@to='{jid_two}/{resource_one}']/body[text()='Mode #foo [+o {nick_two}] by {nick_one}']",),
                        ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",),
                        ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='admin'][@role='moderator']",),
                        ("/iq[@id='id1'][@type='result'][@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}']",),
                        ]),

                    # remove the mode
                    partial(send_stanza,
                            "<iq from='{jid_one}/{resource_one}' id='id1' to='#foo%{irc_server_one}' type='set'><query xmlns='http://jabber.org/protocol/muc#admin'><item affiliation='member' nick='{nick_two}' role='participant'/></query></iq>"),
                    partial(expect_unordered, [
                        ("/message[@to='{jid_one}/{resource_one}']/body[text()='Mode #foo [+v-o {nick_two} {nick_two}] by {nick_one}']",),
                        ("/message[@to='{jid_two}/{resource_one}']/body[text()='Mode #foo [+v-o {nick_two} {nick_two}] by {nick_one}']",),
                        ("/presence[@to='{jid_two}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='member'][@role='participant']",),
                        ("/presence[@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}/{nick_two}']/muc_user:x/muc_user:item[@affiliation='member'][@role='participant']",),
                        ("/iq[@id='id1'][@type='result'][@to='{jid_one}/{resource_one}'][@from='#foo%{irc_server_one}']",),
                        ]),

                    # using an iq, an a non-existant nick
                    partial(send_stanza,
                            "<iq from='{jid_one}/{resource_one}' id='id1' to='#foo%{irc_server_one}' type='set'><query xmlns='http://jabber.org/protocol/muc#admin'><item affiliation='admin' nick='blectre'/></query></iq>"),
                    partial(expect_stanza, "/iq[@type='error']"),

                    # using an iq, without the rights to do it
                    partial(send_stanza,
                            "<iq from='{jid_two}/{resource_one}' id='id1' to='#foo%{irc_server_one}' type='set'><query xmlns='http://jabber.org/protocol/muc#admin'><item affiliation='admin' nick='{nick_one}'/></query></iq>"),
                    partial(expect_unordered, [
                        ("/iq[@type='error']",),
                        ("/message[@type='chat'][@to='{jid_two}/{resource_one}']",),
                    ]),

                    # using an iq, with an unknown mode
                    partial(send_stanza,
                            "<iq from='{jid_two}/{resource_one}' id='id1' to='#foo%{irc_server_one}' type='set'><query xmlns='http://jabber.org/protocol/muc#admin'><item affiliation='owner' nick='{nick_one}'/></query></iq>"),
                    partial(expect_unordered, [
                        ("/iq[@type='error']",),
                        ("/message[@type='chat'][@to='{jid_two}/{resource_one}']",),
                    ]),

                ]),
                Scenario("multisession_kick",
                 [
                     handshake_sequence(),