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