~singpolyma/biboumi

8cf0b833c47314ada66e6a25bbdb9a2178e096d0 — louiz’ 5 years ago 577a863
Make the IRC channel configuration form available from the MUC config

fix #3250
M CHANGELOG.rst => CHANGELOG.rst +2 -0
@@ 17,6 17,8 @@ Version 5.0
 - Configuration options can be overridden by values found in the process env.
 - Botan’s TLS policies can be customized by the administrator, for each
   IRC server, with simple text files.
 - The IRC channel configuration form is now also available using the MUC
   configuration, in addition to the ad-hoc command.

Version 4.3 - 2017-05-02
========================

M src/xmpp/biboumi_adhoc_commands.cpp => src/xmpp/biboumi_adhoc_commands.cpp +52 -31
@@ 418,11 418,18 @@ void ConfigureIrcChannelStep1(XmppComponent&, AdhocSession& session, XmlNode& co
{
  const Jid owner(session.get_owner_jid());
  const Jid target(session.get_target_jid());

  insert_irc_channel_configuration_form(command_node, owner, target);
}

void insert_irc_channel_configuration_form(XmlNode& node, const Jid& requester, const Jid& target)
{
  const Iid iid(target.local, {});
  auto options = Database::get_irc_channel_options_with_server_default(owner.local + "@" + owner.domain,

  auto options = Database::get_irc_channel_options_with_server_default(requester.local + "@" + requester.domain,
                                                                       iid.get_server(), iid.get_local());

  XmlSubNode x(command_node, "jabber:x:data:x");
  XmlSubNode x(node, "jabber:x:data:x");
  x["type"] = "form";
  XmlSubNode title(x, "title");
  title.set_inner("Configure the IRC channel "s + iid.get_local() + " on server "s + iid.get_server());


@@ 468,43 475,57 @@ void ConfigureIrcChannelStep1(XmppComponent&, AdhocSession& session, XmlNode& co

void ConfigureIrcChannelStep2(XmppComponent&, AdhocSession& session, XmlNode& command_node)
{
  const XmlNode* x = command_node.get_child("x", "jabber:x:data");
  const Jid owner(session.get_owner_jid());
  const Jid target(session.get_target_jid());

  if (handle_irc_channel_configuration_form(command_node, owner, target))
    {
      command_node.delete_all_children();
      XmlSubNode note(command_node, "note");
      note["type"] = "info";
      note.set_inner("Configuration successfully applied.");
    }
  else
    {
      XmlSubNode error(command_node, ADHOC_NS":error");
      error["type"] = "modify";
      XmlSubNode condition(error, STANZA_NS":bad-request");
      session.terminate();
    }
}

bool handle_irc_channel_configuration_form(const XmlNode& node, const Jid& requester, const Jid& target)
{
  const XmlNode* x = node.get_child("x", "jabber:x:data");
  if (x)
    {
      const Jid owner(session.get_owner_jid());
      const Jid target(session.get_target_jid());
      const Iid iid(target.local, {});
      auto options = Database::get_irc_channel_options(owner.local + "@" + owner.domain,
                                                       iid.get_server(), iid.get_local());
      for (const XmlNode* field: x->get_children("field", "jabber:x:data"))
      if (x->get_tag("type") == "submit")
        {
          const XmlNode* value = field->get_child("value", "jabber:x:data");

          if (field->get_tag("var") == "encoding_out" &&
              value && !value->get_inner().empty())
            options.encodingOut = value->get_inner();
          const Iid iid(target.local, {});
          auto options = Database::get_irc_channel_options(requester.local + "@" + requester.domain,
                                                           iid.get_server(), iid.get_local());
          for (const XmlNode *field: x->get_children("field", "jabber:x:data"))
            {
              const XmlNode *value = field->get_child("value", "jabber:x:data");

          else if (field->get_tag("var") == "encoding_in" &&
                   value && !value->get_inner().empty())
            options.encodingIn = value->get_inner();
              if (field->get_tag("var") == "encoding_out" &&
                  value && !value->get_inner().empty())
                options.encodingOut = value->get_inner();

          else if (field->get_tag("var") == "persistent" &&
                   value)
            options.persistent = to_bool(value->get_inner());
        }
              else if (field->get_tag("var") == "encoding_in" &&
                       value && !value->get_inner().empty())
                options.encodingIn = value->get_inner();

      options.update();
              else if (field->get_tag("var") == "persistent" &&
                       value)
                options.persistent = to_bool(value->get_inner());
            }

      command_node.delete_all_children();
      XmlSubNode note(command_node, "note");
      note["type"] = "info";
      note.set_inner("Configuration successfully applied.");
      return;
          options.update();
        }
      return true;
    }
  XmlSubNode error(command_node, ADHOC_NS":error");
  error["type"] = "modify";
  XmlSubNode condition(error, STANZA_NS":bad-request");
  session.terminate();
  return false;
}
#endif  // USE_DATABASE


M src/xmpp/biboumi_adhoc_commands.hpp => src/xmpp/biboumi_adhoc_commands.hpp +3 -0
@@ 4,6 4,7 @@
#include <xmpp/adhoc_command.hpp>
#include <xmpp/adhoc_session.hpp>
#include <xmpp/xmpp_stanza.hpp>
#include <xmpp/jid.hpp>

class XmppComponent;



@@ 17,7 18,9 @@ void ConfigureIrcServerStep1(XmppComponent&, AdhocSession& session, XmlNode& com
void ConfigureIrcServerStep2(XmppComponent&, AdhocSession& session, XmlNode& command_node);

void ConfigureIrcChannelStep1(XmppComponent&, AdhocSession& session, XmlNode& command_node);
void insert_irc_channel_configuration_form(XmlNode& node, const Jid& requester, const Jid& target);
void ConfigureIrcChannelStep2(XmppComponent&, AdhocSession& session, XmlNode& command_node);
bool handle_irc_channel_configuration_form(const XmlNode& node, const Jid& requester, const Jid& target);

void DisconnectUserFromServerStep1(XmppComponent&, AdhocSession& session, XmlNode& command_node);
void DisconnectUserFromServerStep2(XmppComponent&, AdhocSession& session, XmlNode& command_node);

M src/xmpp/biboumi_component.cpp => src/xmpp/biboumi_component.cpp +56 -0
@@ 392,6 392,11 @@ void BiboumiComponent::handle_iq(const Stanza& stanza)
          if (this->handle_mam_request(stanza))
            stanza_error.disable();
        }
      else if ((query = stanza.get_child("query", MUC_OWNER_NS)))
        {
          if (this->handle_room_configuration_form(*query, from, to, id))
            stanza_error.disable();
        }
#endif
    }
  else if (type == "get")


@@ 529,6 534,13 @@ void BiboumiComponent::handle_iq(const Stanza& stanza)
            }
          stanza_error.disable();
        }
#ifdef USE_DATABASE
      else if ((query = stanza.get_child("query", MUC_OWNER_NS)))
        {
          if (this->handle_room_configuration_form_request(from, to, id))
            stanza_error.disable();
        }
#endif
    }
  else if (type == "result")
    {


@@ 679,6 691,50 @@ void BiboumiComponent::send_archived_message(const db::MucLogLine& log_line, con
  this->send_stanza(message);
}

bool BiboumiComponent::handle_room_configuration_form_request(const std::string& from, const Jid& to, const std::string& id)
{
  Iid iid(to.local, {'#', '&'});

  if (iid.type != Iid::Type::Channel)
    return false;

  Stanza iq("iq");
  {
    iq["from"] = to.full();
    iq["to"] = from;
    iq["id"] = id;
    iq["type"] = "result";
    XmlSubNode query(iq, "query");
    query["xmlns"] = MUC_OWNER_NS;
    Jid requester(from);
    insert_irc_channel_configuration_form(query, requester, to);
  }
  this->send_stanza(iq);
  return true;
}

bool BiboumiComponent::handle_room_configuration_form(const XmlNode& query, const std::string &from, const Jid &to, const std::string &id)
{
  Iid iid(to.local, {'#', '&'});

  if (iid.type != Iid::Type::Channel)
    return false;

  Jid requester(from);
  if (!handle_irc_channel_configuration_form(query, requester, to))
    return false;

  Stanza iq("iq");
  iq["type"] = "result";
  iq["from"] = to.full();
  iq["to"] = from;
  iq["id"] = id;

  this->send_stanza(iq);

  return true;
}

#endif

Bridge* BiboumiComponent::get_user_bridge(const std::string& user_jid)

M src/xmpp/biboumi_component.hpp => src/xmpp/biboumi_component.hpp +3 -0
@@ 2,6 2,7 @@


#include <xmpp/xmpp_component.hpp>
#include <xmpp/jid.hpp>

#include <bridge/bridge.hpp>



@@ 97,6 98,8 @@ public:
  bool handle_mam_request(const Stanza& stanza);
  void send_archived_message(const db::MucLogLine& log_line, const std::string& from, const std::string& to,
                             const std::string& queryid);
  bool handle_room_configuration_form_request(const std::string& from, const Jid& to, const std::string& id);
  bool handle_room_configuration_form(const XmlNode& query, const std::string& from, const Jid& to, const std::string& id);
#endif

  /**

M src/xmpp/xmpp_component.hpp => src/xmpp/xmpp_component.hpp +1 -0
@@ 17,6 17,7 @@
#define MUC_NS           "http://jabber.org/protocol/muc"
#define MUC_USER_NS      MUC_NS"#user"
#define MUC_ADMIN_NS     MUC_NS"#admin"
#define MUC_OWNER_NS     MUC_NS"#owner"
#define DISCO_NS         "http://jabber.org/protocol/disco"
#define DISCO_ITEMS_NS   DISCO_NS"#items"
#define DISCO_INFO_NS    DISCO_NS"#info"

M tests/end_to_end/__main__.py => tests/end_to_end/__main__.py +21 -0
@@ 118,6 118,7 @@ def match(stanza, xpath):
    tree = lxml.etree.parse(io.StringIO(str(stanza)))
    matched = tree.xpath(xpath, namespaces={'re': 'http://exslt.org/regular-expressions',
                                            'muc_user': 'http://jabber.org/protocol/muc#user',
                                            'muc_owner': 'http://jabber.org/protocol/muc#owner',
                                            'muc': 'http://jabber.org/protocol/muc',
                                            'disco_info': 'http://jabber.org/protocol/disco#info',
                                            'muc_traffic': 'http://jabber.org/protocol/muc#traffic',


@@ 2382,6 2383,26 @@ if __name__ == '__main__':
                     partial(send_stanza, "<iq type='set' id='id4' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'><command xmlns='http://jabber.org/protocol/commands' action='cancel' node='configure' sessionid='{sessionid}' /></iq>"),
                     partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='canceled']"),
                 ]),
        Scenario("irc_channel_configure_xep0045",
                 [
                     handshake_sequence(),
                     partial(send_stanza, "<iq type='get' id='id1' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'><query xmlns='http://jabber.org/protocol/muc#owner'/></iq>"),
                     partial(expect_stanza, ("/iq[@type='result']/muc_owner:query",
                                             "/iq/muc_owner:query/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_in']",
                                             "/iq/muc_owner:query/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_out']",
                                             ),
                             ),
                     partial(send_stanza, "<iq type='set' id='id2' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'>"
                                          "<query xmlns='http://jabber.org/protocol/muc#owner'>"
                                          "<x xmlns='jabber:x:data' type='submit'>"
                                          "<field var='ports' />"
                                          "<field var='encoding_out'><value>UTF-8</value></field>"
                                          "<field var='encoding_in'><value>latin-1</value></field>"
                                          "</x></query></iq>"),
                     partial(expect_stanza, "/iq[@type='result']"),
                     partial(send_stanza, "<iq type='set' id='id3' from='{jid_one}/{resource_one}' to='#foo%{irc_server_one}'><query xmlns='http://jabber.org/protocol/muc#owner'>    <x xmlns='jabber:x:data' type='cancel'/></query></iq>"),
                     partial(expect_stanza, "/iq[@type='result']"),
                 ]),
        Scenario("irc_channel_configure_fixed",
                 [
                     handshake_sequence(),