M CHANGELOG.rst => CHANGELOG.rst +2 -0
@@ 3,6 3,8 @@ Version 6.0
- The LiteSQL dependency was removed. Only libsqlite3 is now necessary
to work with the database.
+ - The RecordHistory option can now also be configured for each IRC channel,
+ individually.
Version 5.0 - 2017-05-24
========================
M doc/biboumi.1.rst => doc/biboumi.1.rst +6 -1
@@ 358,7 358,7 @@ History
-------
Public channel messages are saved into archives, inside the database, unless
-the `record_history` option is set to false for that user `Ad-hoc commands`.
+the `record_history` option is set to false by that user (see `Ad-hoc commands`).
Private messages (messages that are sent directly to a nickname, not a
channel) are never stored in the database. When a channel is joined, biboumi
sends the `max_history_length` messages found in the database as the MUC
@@ 631,6 631,11 @@ On a channel JID (e.g on the JID #test%chat.freenode.org@biboumi.example.com)
the archiving of messages is enabled for this room, the client will
receive the messages that where sent in this channel. This option can be
used to make biboumi act as an IRC bouncer.
+ * Record History: whether or not history messages should be saved in
+ the database, for this specific channel. If the value is “unset” (the
+ default), then the value configured globally is used. This option is there,
+ for example, to be able to enable history recording globally while disabling
+ it for a few specific “private” channels.
Raw IRC messages
----------------
M src/utils/optional_bool.hpp => src/utils/optional_bool.hpp +10 -0
@@ 20,6 20,16 @@ struct OptionalBool
this->is_set = false;
}
+ std::string to_string()
+ {
+ if (this->is_set == false)
+ return "unset";
+ else if (this->value)
+ return "true";
+ else
+ return "false";
+ }
+
bool is_set{false};
bool value{false};
};
M src/xmpp/biboumi_adhoc_commands.cpp => src/xmpp/biboumi_adhoc_commands.cpp +49 -4
@@ 434,6 434,26 @@ void insert_irc_channel_configuration_form(XmlNode& node, const Jid& requester,
XmlSubNode instructions(x, "instructions");
instructions.set_inner("Edit the form, to configure the settings of the IRC channel "s + iid.get_local());
+ XmlSubNode record_history(x, "field");
+ record_history["var"] = "record_history";
+ record_history["type"] = "list-single";
+ record_history["label"] = "Record history for this channel";
+ record_history["desc"] = "If unset, the value is the one configured globally";
+
+ {
+ // Value selected by default
+ XmlSubNode value(record_history, "value");
+ value.set_inner(options.col<Database::RecordHistoryOptional>().to_string());
+ }
+ // All three possible values
+ for (const auto& val: {"unset", "true", "false"})
+ {
+ XmlSubNode option(record_history, "option");
+ option["label"] = val;
+ XmlSubNode value(option, "value");
+ value.set_inner(val);
+ }
+
XmlSubNode encoding_out(x, "field");
encoding_out["var"] = "encoding_out";
encoding_out["type"] = "text-single";
@@ 471,12 491,12 @@ void insert_irc_channel_configuration_form(XmlNode& node, const Jid& requester,
}
}
-void ConfigureIrcChannelStep2(XmppComponent&, AdhocSession& session, XmlNode& command_node)
+void ConfigureIrcChannelStep2(XmppComponent& xmpp_component, AdhocSession& session, XmlNode& command_node)
{
const Jid owner(session.get_owner_jid());
const Jid target(session.get_target_jid());
- if (handle_irc_channel_configuration_form(command_node, owner, target))
+ if (handle_irc_channel_configuration_form(xmpp_component, command_node, owner, target))
{
command_node.delete_all_children();
XmlSubNode note(command_node, "note");
@@ 492,7 512,7 @@ void ConfigureIrcChannelStep2(XmppComponent&, AdhocSession& session, XmlNode& co
}
}
-bool handle_irc_channel_configuration_form(const XmlNode& node, const Jid& requester, const Jid& target)
+bool handle_irc_channel_configuration_form(XmppComponent& xmpp_component, const XmlNode& node, const Jid& requester, const Jid& target)
{
const XmlNode* x = node.get_child("x", "jabber:x:data");
if (x)
@@ 500,7 520,7 @@ bool handle_irc_channel_configuration_form(const XmlNode& node, const Jid& reque
if (x->get_tag("type") == "submit")
{
const Iid iid(target.local, {});
- auto options = Database::get_irc_channel_options(requester.local + "@" + requester.domain,
+ auto options = Database::get_irc_channel_options(requester.bare(),
iid.get_server(), iid.get_local());
for (const XmlNode *field: x->get_children("field", "jabber:x:data"))
{
@@ 517,6 537,31 @@ bool handle_irc_channel_configuration_form(const XmlNode& node, const Jid& reque
else if (field->get_tag("var") == "persistent" &&
value)
options.col<Database::Persistent>() = to_bool(value->get_inner());
+ else if (field->get_tag("var") == "record_history" &&
+ value && !value->get_inner().empty())
+ {
+ OptionalBool& database_value = options.col<Database::RecordHistoryOptional>();
+ if (value->get_inner() == "true")
+ database_value.set_value(true);
+ else if (value->get_inner() == "false")
+ database_value.set_value(false);
+ else
+ database_value.unset();
+ auto& biboumi_component = dynamic_cast<BiboumiComponent&>(xmpp_component);
+ Bridge* bridge = biboumi_component.find_user_bridge(requester.bare());
+ if (bridge)
+ {
+ if (database_value.is_set)
+ bridge->set_record_history(database_value.value);
+ else
+ { // It is unset, we need to fetch the Global option, to
+ // know if it’s enabled or not
+ auto g_options = Database::get_global_options(requester.bare());
+ bridge->set_record_history(g_options.col<Database::RecordHistory>());
+ }
+ }
+ }
+
}
options.save(Database::db);
M src/xmpp/biboumi_adhoc_commands.hpp => src/xmpp/biboumi_adhoc_commands.hpp +1 -1
@@ 20,7 20,7 @@ void ConfigureIrcServerStep2(XmppComponent&, AdhocSession& session, XmlNode& com
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);
+bool handle_irc_channel_configuration_form(XmppComponent&, 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 +1 -1
@@ 719,7 719,7 @@ bool BiboumiComponent::handle_room_configuration_form(const XmlNode& query, cons
return false;
Jid requester(from);
- if (!handle_irc_channel_configuration_form(query, requester, to))
+ if (!handle_irc_channel_configuration_form(*this, query, requester, to))
return false;
Stanza iq("iq");
M tests/end_to_end/__main__.py => tests/end_to_end/__main__.py +3 -0
@@ 2467,6 2467,7 @@ if __name__ == '__main__':
partial(expect_stanza, ("/iq[@type='result']/commands:command[@node='configure'][@sessionid][@status='executing']",
"/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_in']",
"/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_out']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='list-single'][@var='record_history']/dataform:value[text()='unset']",
),
after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid"))
),
@@ 2476,6 2477,7 @@ if __name__ == '__main__':
"<field var='ports' />"
"<field var='encoding_out'><value>UTF-8</value></field>"
"<field var='encoding_in'><value>latin-1</value></field>"
+ "<field var='record_history'><value>true</value></field>"
"</x></command></iq>"),
partial(expect_stanza, "/iq[@type='result']/commands:command[@node='configure'][@status='completed']/commands:note[@type='info'][text()='Configuration successfully applied.']"),
@@ 2484,6 2486,7 @@ if __name__ == '__main__':
"/iq/commands:command/dataform:x[@type='form']/dataform:title[text()='Configure the IRC channel #foo on server irc.localhost']",
"/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_in']/dataform:value[text()='latin-1']",
"/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='text-single'][@var='encoding_out']/dataform:value[text()='UTF-8']",
+ "/iq/commands:command/dataform:x[@type='form']/dataform:field[@type='list-single'][@var='record_history']/dataform:value[text()='true']",
"/iq/commands:command/commands:actions/commands:next",
),
after = partial(save_value, "sessionid", partial(extract_attribute, "/iq[@type='result']/commands:command[@node='configure']", "sessionid"))