File

mod_storage_muconference_readonly/mod_storage_muconference_readonly.lua @ 4432:e83284d4d5c2

mod_auth_ccert/README: Add setting to ensure Prosdy asks for client certificate This used to be the default for all services, but since it triggers annoying popups in web browsers it was inverted in Prosody and only s2s enables it, so it needs to be explicitly enabled for c2s again. See trunk 115b5e32d960 Thanks debacle
author Kim Alvefur <zash@zash.se>
date Sat, 06 Feb 2021 21:34:25 +0100
parent 2245:a0727d23ee65
line wrap: on
line source


-- luacheck: ignore 212/self

local sql = require "util.sql";
local xml_parse = require "util.xml".parse;
local resolve_relative_path = require "util.paths".resolve_relative_path;
local stanza_preserialize = require "util.stanza".preserialize;

local unpack = unpack
local function iterator(result)
	return function(result_)
		local row = result_();
		if row ~= nil then
			return unpack(row);
		end
	end, result, nil;
end

local default_params = { driver = "MySQL" };

local engine;

local host = module.host;
local room, store;

local function get_best_affiliation(a, b)
	if a == 'owner' or b == 'owner' then
		return 'owner';
	elseif a == 'administrator' or b == 'administrator' then
		return 'administrator';
	elseif a == 'outcast' or b == 'outcast' then
		return 'outcast';
	elseif a == 'member' or b == 'member' then
		return 'member';
	end
	assert(false);
end

local function keyval_store_get()
	if store == "config" then
		local room_jid = room.."@"..host;
		local result;
		for row in engine:select("SELECT `name`,`desc`,`topic`,`public`,`secret` FROM `rooms` WHERE `jid`=? LIMIT 1", room_jid or "") do result = row end
		local name = result[1];
		local desc = result[2];
		local subject = result[3];
		local public = result[4];
		local hidden = public == 0 and true or nil;
		local secret = result[5];
		if secret == '' then secret = nil end
		local affiliations = {};
		for row in engine:select("SELECT `jid_user`,`affil` FROM `rooms_lists` WHERE `jid_room`=?", room_jid or "") do
			local jid_user = row[1];
			local affil = row[2];
			-- mu-conference has a bug where full JIDs get stored…
			local bare_jid = jid_user:gsub('/.*', '');
			local old_affil = affiliations[bare_jid];
			-- mu-conference has a bug where it can record multiple affiliations…
			if old_affil ~= nil and old_affil ~= affil then
				affil = get_best_affiliation(old_affil, affil);
			end
			-- terminology is clearly “admin”, not “administrator”.
			if affil == 'administrator' then
				affil = 'admin';
			end
			affiliations[bare_jid] = affil;
		end
		return {
			jid = room_jid,
			_data = {
				persistent = true,
				name = name,
				description = desc,
				subject = subject,
				password = secret,
				hidden = hidden,
			},
			_affiliations = affiliations,
		};
	end
end

--- Key/value store API (default store type)

local keyval_store = {};
keyval_store.__index = keyval_store;
function keyval_store:get(roomname)
	room, store = roomname, self.store;
	local ok, result = engine:transaction(keyval_store_get);
	if not ok then
		module:log("error", "Unable to read from database %s store for %s: %s", store, roomname or "<host>", result);
		return nil, result;
	end
	return result;
end

function keyval_store:users()
	local host_length = host:len() + 1;
	local ok, result = engine:transaction(function()
		return engine:select("SELECT SUBSTRING_INDEX(jid, '@', 1) FROM `rooms`");
	end);
	if not ok then return ok, result end
	return iterator(result);
end

local stores = {
	keyval = keyval_store;
};

--- Implement storage driver API

-- FIXME: Some of these operations need to operate on the archive store(s) too

local driver = {};

function driver:open(store, typ)
	local store_mt = stores[typ or "keyval"];
	if store_mt then
		return setmetatable({ store = store }, store_mt);
	end
	return nil, "unsupported-store";
end

function driver:stores(roomname)
	local query = "SELECT 'config'";
	if roomname == true or not roomname then
		roomname = "";
	end
	local ok, result = engine:transaction(function()
		return engine:select(query, host, roomname);
	end);
	if not ok then return ok, result end
	return iterator(result);
end

--- Initialization


local function normalize_params(params)
	assert(params.driver and params.database, "Configuration error: Both the SQL driver and the database need to be specified");
	return params;
end

function module.load()
	if prosody.prosodyctl then return; end
	local engines = module:shared("/*/sql/connections");
	local params = normalize_params(module:get_option("sql", default_params));
	engine = engines[sql.db2uri(params)];
	if not engine then
		module:log("debug", "Creating new engine");
		engine = sql:create_engine(params);
		engines[sql.db2uri(params)] = engine;
	end

	module:provides("storage", driver);
end