File

mod_storage_muconference_readonly/mod_storage_muconference_readonly.lua @ 5511:0860497152af

mod_http_oauth2: Record hash of client_id to allow future verification RFC 6819 section 5.2.2.2 states that refresh tokens MUST be bound to the client. In order to do that, we must record something that can definitely tie the client to the grant. Since the full client_id is so large (why we have this client_subset function), a hash is stored instead.
author Kim Alvefur <zash@zash.se>
date Fri, 02 Jun 2023 10:14:16 +0200
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