view mod_storage_muconference_readonly/mod_storage_muconference_readonly.lua @ 5585:5b316088bef5

mod_rest: Use logger of HTTP request in trunk In Prosody trunk rev c975dafa4303 each HTTP request gained its own log sink, to make it easy to log things related to each request and group those messages. Especially where async is used, spreading the request and response apart as mod_rest does with iq stanzas, this grouped logging should help find related messages.
author Kim Alvefur <zash@zash.se>
date Fri, 07 Jul 2023 00:10:37 +0200
parents a0727d23ee65
children
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