changeset 2518:0e1054c19f9d

mod_firewall: More meta! Allow simple functions to be applied to $<> expressions
author Matthew Wild <mwild1@gmail.com>
date Sun, 19 Feb 2017 21:06:57 +0000
parents 3990b1bca308
children d4bc434a60a4
files mod_firewall/mod_firewall.lua
diffstat 1 files changed, 55 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/mod_firewall/mod_firewall.lua	Sun Feb 19 21:06:08 2017 +0000
+++ b/mod_firewall/mod_firewall.lua	Sun Feb 19 21:06:57 2017 +0000
@@ -47,24 +47,71 @@
 	return name:match("^%a[%w_]*$")
 end
 
+local meta_funcs = {
+	bare = function (code)
+		return "jid_bare("..code..")", {"jid_bare"};
+	end;
+	node = function (code)
+		return "(jid_split("..code.."))", {"jid_split"};
+	end;
+	host = function (code)
+		return "(select(2, jid_split("..code..")))", {"jid_split"};
+	end;
+	resource = function (code)
+		return "(select(3, jid_split("..code..")))", {"jid_split"};
+	end;
+};
+
 -- Run quoted (%q) strings through this to allow them to contain code. e.g.: LOG=Received: $(stanza:top_tag())
-function meta(s, extra)
+function meta(s, deps, extra)
 	return (s:gsub("$(%b())", function (expr)
 			expr = expr:gsub("\\(.)", "%1");
 			return [["..tostring(]]..expr..[[).."]];
 		end)
 		:gsub("$(%b<>)", function (expr)
 			expr = expr:sub(2,-2);
-			local default = expr:match("||([^|]+)$");
-			if default then
-				expr = expr:sub(1, -(#default+2));
+			local func_chain = expr:match("|[%w|]+$");
+			if func_chain then
+				expr = expr:sub(1, -1-#func_chain);
+			end
+			local code;
+			if expr:match("^@") then
+				-- Skip stanza:find() for simple attribute lookup
+				local attr_name = expr:sub(2);
+				if deps and (attr_name == "to" or attr_name == "from" or attr_name == "type") then
+					-- These attributes may be cached in locals
+					code = attr_name;
+					table.insert(deps, attr_name);
+				else
+					code = "stanza.attr["..("%q"):format(attr_name).."]";
+				end
 			else
-				default = "<undefined>";
+				code = "(stanza:find("..("%q"):format(expr)..") or "..("%q"):format("<undefined>")..")";
 			end
-			if expr:match("^@") then
-				return "\"..(stanza.attr["..("%q"):format(expr:sub(2)).."] or "..("%q"):format(default)..")..\"";
+			if func_chain then
+				for func_name in func_chain:gmatch("|(%w+)") do
+					if code == "to" or code == "from" then
+						if func_name == "bare" then
+							code = "bare_"..code;
+							table.insert(deps, code);
+						elseif func_name == "node" or func_name == "host" or func_name == "resource" then
+							table.insert(deps, "split_"..code);
+							code = code.."_"..func_name;
+						end
+					else
+						assert(meta_funcs[func_name], "unknown function: "..func_name);
+						local new_code, new_deps = meta_funcs[func_name](code);
+						code = new_code;
+						if new_deps and #new_deps > 0 then
+							assert(deps, "function not supported here: "..func_name);
+							for _, dep in ipairs(new_deps) do
+								table.insert(deps, dep);
+							end
+						end
+					end
+				end
 			end
-			return "\"..(stanza:find("..("%q"):format(expr)..") or "..("%q"):format(default)..")..\"";
+			return "\"..(("..code..") or \"<undefined>\")..\"";
 		end)
 		:gsub("$$(%a+)", extra or {})
 		:gsub([[^""%.%.]], "")