File

plugins/mod_mimicking.lua @ 13114:025c38ee885d

util.dependencies: Print tables itself to reduce number of imports Rationale: See diffstat When this module is imported, it ends up calling stty via term_width() in util.human.io.table(). When this happens outside of a terminal, the following message is sent to stdout: stty: 'standard input': Inappropriate ioctl for device Not importing this module avoids that. Furthermore three is value in this module having minimal dependencies as they might not be available when it does the checks. Ref a1fed82c44b9
author Kim Alvefur <zash@zash.se>
date Fri, 26 May 2023 21:18:27 +0200
parent 12977:74b9e05af71e
line wrap: on
line source

-- Prosody IM
-- Copyright (C) 2012 Florian Zeitz
-- Copyright (C) 2019 Kim Alvefur
--
-- This project is MIT/X11 licensed. Please see the
-- COPYING file in the source package for more information.
--

local encodings = require "prosody.util.encodings";
assert(encodings.confusable, "This module requires that Prosody be built with ICU");
local skeleton = encodings.confusable.skeleton;

local usage = require "prosody.util.prosodyctl".show_usage;
local usermanager = require "prosody.core.usermanager";
local storagemanager = require "prosody.core.storagemanager";

local skeletons
function module.load()
	if module.host ~= "*" then
		skeletons = module:open_store("skeletons");
	end
end

module:hook("user-registered", function(user)
	local skel = skeleton(user.username);
	local ok, err = skeletons:set(skel, { username = user.username });
	if not ok then
		module:log("error", "Unable to store mimicry data (%q => %q): %s", user.username, skel, err);
	end
end);

module:hook_global("user-deleted", function(user)
	if user.host ~= module.host then return end
	local skel = skeleton(user.username);
	local ok, err = skeletons:set(skel, nil);
	if not ok and err then
		module:log("error", "Unable to clear mimicry data (%q): %s", skel, err);
	end
end);

module:hook("user-registering", function(user)
	local existing, err = skeletons:get(skeleton(user.username));
	if existing then
		module:log("debug", "Attempt to register username '%s' which could be confused with '%s'", user.username, existing.username);
		user.allowed = false;
	elseif err then
		module:log("error", "Unable to check if new username '%s' can be confused with any existing user: %s", err);
	end
end);

function module.command(arg)
	if (arg[1] ~= "bootstrap" or not arg[2]) then
		usage("mod_mimicking bootstrap <host>", "Initialize username mimicry database");
		return;
	end

	local host = arg[2];

	local host_session = prosody.hosts[host];
	if not host_session then
		return "No such host";
	end

	storagemanager.initialize_host(host);
	usermanager.initialize_host(host);

	skeletons = storagemanager.open(host, "skeletons");

	local count = 0;
	for user in usermanager.users(host) do
		local skel = skeleton(user);
		local existing, err = skeletons:get(skel);
		if existing and existing.username ~= user then
			module:log("warn", "Existing usernames '%s' and '%s' are confusable", existing.username, user);
		elseif err then
			module:log("error", "Error checking for existing mimicry data (%q = %q): %s", user, skel, err);
		end
		local ok, err = skeletons:set(skel, { username = user });
		if ok then
			count = count + 1;
		elseif err then
			module:log("error", "Unable to store mimicry data (%q => %q): %s", user, skel, err);
		end
	end
	module:log("info", "%d usernames indexed", count);
end