File

mod_anti_spam/trie.lib.lua @ 6110:1a6cd0bbb7ab

mod_compliance_2023: Add 2023 Version of the compliance module, basis is the 2021 Version. diff --git a/mod_compliance_2023/README.md b/mod_compliance_2023/README.md new file mode 100644 --- /dev/null +++ b/mod_compliance_2023/README.md @@ -0,0 +1,22 @@ +--- +summary: XMPP Compliance Suites 2023 self-test +labels: +- Stage-Beta +rockspec: + dependencies: + - mod_cloud_notify + +... + +Compare the list of enabled modules with +[XEP-0479: XMPP Compliance Suites 2023] and produce basic report to the +Prosody log file. + +If installed with the Prosody plugin installer then all modules needed for a green checkmark should be included. (With prosody 0.12 only [mod_cloud_notify] is not included with prosody and we need the community module) + +# Compatibility + + Prosody-Version Status + --------------- ---------------------- + trunk Works as of 2024-12-21 + 0.12 Works diff --git a/mod_compliance_2023/mod_compliance_2023.lua b/mod_compliance_2023/mod_compliance_2023.lua new file mode 100644 --- /dev/null +++ b/mod_compliance_2023/mod_compliance_2023.lua @@ -0,0 +1,79 @@ +-- Copyright (c) 2021 Kim Alvefur +-- +-- This module is MIT licensed. + +local hostmanager = require "core.hostmanager"; + +local array = require "util.array"; +local set = require "util.set"; + +local modules_enabled = module:get_option_inherited_set("modules_enabled"); + +for host in pairs(hostmanager.get_children(module.host)) do + local component = module:context(host):get_option_string("component_module"); + if component then + modules_enabled:add(component); + modules_enabled:include(module:context(host):get_option_set("modules_enabled", {})); + end +end + +local function check(suggested, alternate, ...) + if set.intersection(modules_enabled, set.new({suggested; alternate; ...})):empty() then return suggested; end + return false; +end + +local compliance = { + array {"Server"; check("tls"); check("disco")}; + + array {"Advanced Server"; check("pep", "pep_simple")}; + + array {"Web"; check("bosh"); check("websocket")}; + + -- No Server requirements for Advanced Web + + array {"IM"; check("vcard_legacy", "vcard"); check("carbons"); check("http_file_share", "http_upload")}; + + array { + "Advanced IM"; + check("vcard_legacy", "vcard"); + check("blocklist"); + check("muc"); + check("private"); + check("smacks"); + check("mam"); + check("bookmarks"); + }; + + array {"Mobile"; check("smacks"); check("csi_simple", "csi_battery_saver")}; + + array {"Advanced Mobile"; check("cloud_notify")}; + + array {"A/V Calling"; check("turn_external", "external_services", "turncredentials", "extdisco")}; + +}; + +function check_compliance() + local compliant = true; + for _, suite in ipairs(compliance) do + local section = suite:pop(1); + if module:get_option_boolean("compliance_" .. section:lower():gsub("%A", "_"), true) then + local missing = set.new(suite:filter(function(m) return type(m) == "string" end):map(function(m) return "mod_" .. m end)); + if suite[1] then + if compliant then + compliant = false; + module:log("warn", "Missing some modules for XMPP Compliance 2023"); + end + module:log("info", "%s Compliance: %s", section, missing); + end + end + end + + if compliant then module:log("info", "XMPP Compliance 2023: Compliant ✔️"); end +end + +if prosody.start_time then + check_compliance() +else + module:hook_global("server-started", check_compliance); +end +
author Menel <menel@snikket.de>
date Sun, 22 Dec 2024 16:06:28 +0100
parent 5883:259ffdbf8906
child 6118:8338f42d2bc5
child 6208:e20901443eae
line wrap: on
line source

local bit = require "prosody.util.bitcompat";

local trie_methods = {};
local trie_mt = { __index = trie_methods };

local function new_node()
	return {};
end

function trie_methods:set(item, value)
	local node = self.root;
	for i = 1, #item do
		local c = item:byte(i);
		if not node[c] then
			node[c] = new_node();
		end
		node = node[c];
	end
	node.terminal = true;
	node.value = value;
end

local function _remove(node, item, i)
	if i > #item then
		if node.terminal then
			node.terminal = nil;
			node.value = nil;
		end
		if next(node) ~= nil then
			return node;
		end
		return nil;
	end
	local c = item:byte(i);
	local child = node[c];
	local ret;
	if child then
		ret = _remove(child, item, i+1);
		node[c] = ret;
	end
	if ret == nil and next(node) == nil then
		return nil;
	end
	return node;
end

function trie_methods:remove(item)
	return _remove(self.root, item, 1);
end

function trie_methods:get(item, partial)
	local value;
	local node = self.root;
	local len = #item;
	for i = 1, len do
		if partial and node.terminal then
			value = node.value;
		end
		local c = item:byte(i);
		node = node[c];
		if not node then
			return value, i - 1;
		end
	end
	return node.value, len;
end

function trie_methods:add(item)
	return self:set(item, true);
end

function trie_methods:contains(item, partial)
	return self:get(item, partial) ~= nil;
end

function trie_methods:longest_prefix(item)
	return select(2, self:get(item));
end

function trie_methods:add_subnet(item, bits)
	item = item.packed:sub(1, math.ceil(bits/8));
	local existing = self:get(item);
	if not existing then
		existing = { bits };
		return self:set(item, existing);
	end

	-- Simple insertion sort
	for i = 1, #existing do
		local v = existing[i];
		if v == bits then
			return; -- Already in there
		elseif v > bits then
			table.insert(existing, v, i);
			return;
		end
	end
end

function trie_methods:remove_subnet(item, bits)
	item = item.packed:sub(1, math.ceil(bits/8));
	local existing = self:get(item);
	if not existing then
		return;
	end

	-- Simple insertion sort
	for i = 1, #existing do
		local v = existing[i];
		if v == bits then
			table.remove(existing, i);
			break;
		elseif v > bits then
			return; -- Stop search
		end
	end

	if #existing == 0 then
		self:remove(item);
	end
end

function trie_methods:has_ip(item)
	item = item.packed;
	local node = self.root;
	local len = #item;
	for i = 1, len do
		if node.terminal then
			return true;
		end

		local c = item:byte(i);
		local child = node[c];
		if not child then
			for child_byte, child_node in pairs(node) do
				if type(child_byte) == "number" and child_node.terminal then
					local bits = child_node.value;
					for j = #bits, 1, -1 do
						local b = bits[j]-((i-1)*8);
						if b ~= 8 then
							local mask = bit.bnot(2^b-1);
							if bit.band(bit.bxor(c, child_byte), mask) == 0 then
								return true;
							end
						end
					end
				end
			end
			return false;
		end
		node = child;
	end
end

local function new()
	return setmetatable({
		root = new_node();
	}, trie_mt);
end

local function is_trie(o)
	return getmetatable(o) == trie_mt;
end

return {
	new = new;
	is_trie = is_trie;
};