# HG changeset patch
# User Kim Alvefur <zash@zash.se>
# Date 1519504856 -3600
# Node ID 165d2877eeac82b4fbefba0211e110f5b41bd3c0
# Parent  d958558e00585869e333614483ff2bf82d816584
mod_firewall: Add experimental user-centric persistent marks behind a feature flag

diff -r d958558e0058 -r 165d2877eeac mod_firewall/actions.lib.lua
--- a/mod_firewall/actions.lib.lua	Sat Feb 24 19:38:10 2018 +0100
+++ b/mod_firewall/actions.lib.lua	Sat Feb 24 21:40:56 2018 +0100
@@ -213,6 +213,14 @@
 	return [[session.firewall_marked_]]..idsafe(name)..[[ = nil;]]
 end
 
+function action_handlers.MARK_USER(name)
+	return [[if session.firewall_marks then session.firewall_marks.]]..idsafe(name)..[[ = current_timestamp; end]], { "timestamp" };
+end
+
+function action_handlers.UNMARK_USER(name)
+	return [[if session.firewall_marks then session.firewall_marks.]]..idsafe(name)..[[ = nil; end]], { "timestamp" };
+end
+
 function action_handlers.ADD_TO(spec)
 	local list_name, value = spec:match("(%S+) (.+)");
 	local meta_deps = {};
diff -r d958558e0058 -r 165d2877eeac mod_firewall/conditions.lib.lua
--- a/mod_firewall/conditions.lib.lua	Sat Feb 24 19:38:10 2018 +0100
+++ b/mod_firewall/conditions.lib.lua	Sat Feb 24 21:40:56 2018 +0100
@@ -276,6 +276,20 @@
 	return ("not not session.firewall_marked_"..idsafe(name));
 end
 
+function condition_handlers.USER_MARKED(name_and_time)
+	local name, time = name_and_time:match("^%s*([%w_]+)%s+%(([^)]+)s%)%s*$");
+	if not name then
+		name = name_and_time:match("^%s*([%w_]+)%s*$");
+	end
+	if not name then
+		error("Error parsing mark name, see documentation for usage examples");
+	end
+	if time then
+		return ("(current_timestamp - (session.firewall_marks and session.firewall_marks.%s or 0)) < %d"):format(idsafe(name), tonumber(time)), { "timestamp" };
+	end
+	return ("not not (session.firewall_marks and session.firewall_marks."..idsafe(name)..")");
+end
+
 function condition_handlers.SENT_DIRECTED_PRESENCE_TO_SENDER()
 	return "not not (session.directed and session.directed[from])", { "from" };
 end
diff -r d958558e0058 -r 165d2877eeac mod_firewall/marks.lib.lua
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_firewall/marks.lib.lua	Sat Feb 24 21:40:56 2018 +0100
@@ -0,0 +1,23 @@
+local mark_storage = module:open_store("firewall_marks");
+
+local user_sessions = prosody.hosts[module.host].sessions;
+
+module:hook("resource-bind", function (event)
+	local session = event.session;
+	local username = session.username;
+	local user = user_sessions[username];
+	local marks = user.firewall_marks;
+	if not marks then
+		marks = mark_storage:get(username) or {};
+		user.firewall_marks = marks; -- luacheck: ignore 122
+	end
+	session.firewall_marks = marks;
+end);
+
+module:hook("resource-unbind", function (event)
+	local session = event.session;
+	local username = session.username;
+	local marks = session.firewall_marks;
+	mark_storage:set(username, marks);
+end);
+
diff -r d958558e0058 -r 165d2877eeac mod_firewall/mod_firewall.lua
--- a/mod_firewall/mod_firewall.lua	Sat Feb 24 19:38:10 2018 +0100
+++ b/mod_firewall/mod_firewall.lua	Sat Feb 24 21:40:56 2018 +0100
@@ -303,6 +303,10 @@
 local condition_handlers = module:require("conditions");
 local action_handlers = module:require("actions");
 
+if module:get_option_boolean("firewall_experimental_user_marks", false) then
+	module:require"marks";
+end
+
 local function new_rule(ruleset, chain)
 	assert(chain, "no chain specified");
 	local rule = { conditions = {}, actions = {}, deps = {} };