mod_storage_ejabberdsql_readonly/mod_storage_ejabberdsql_readonly.lua
changeset 2227 c3ad652cb71f
child 2248 e0663dcd934d
equal deleted inserted replaced
2226:51596d73157e 2227:c3ad652cb71f
       
     1 
       
     2 -- luacheck: ignore 212/self
       
     3 
       
     4 local sql = require "util.sql";
       
     5 local xml_parse = require "util.xml".parse;
       
     6 local resolve_relative_path = require "util.paths".resolve_relative_path;
       
     7 local stanza_preserialize = require "util.stanza".preserialize;
       
     8 
       
     9 local unpack = unpack
       
    10 local function iterator(result)
       
    11 	return function(result_)
       
    12 		local row = result_();
       
    13 		if row ~= nil then
       
    14 			return unpack(row);
       
    15 		end
       
    16 	end, result, nil;
       
    17 end
       
    18 
       
    19 local default_params = { driver = "SQLite3" };
       
    20 
       
    21 local engine;
       
    22 
       
    23 local host = module.host;
       
    24 local user, store;
       
    25 
       
    26 local function keyval_store_get()
       
    27 	if store == "accounts" then
       
    28 		--for row in engine:select("SELECT `password`,`created_at` FROM `users` WHERE `username`=?", user or "") do
       
    29 		local result;
       
    30 		for row in engine:select("SELECT `password` FROM `users` WHERE `username`=? LIMIT 1", user or "") do result = row end
       
    31 		local password = result[1];
       
    32 		--local created_at = result[2];
       
    33 		return { password = password };
       
    34 
       
    35 	elseif store == "roster" then
       
    36 		local roster = {};
       
    37 		local pending = nil;
       
    38 		--for row in engine:select("SELECT `jid`,`nick`,`subscription`,`ask`,`askmessage`,`server`,`subscribe`,`type`,`created_at` FROM `rosterusers` WHERE `username`=?", user or "") do
       
    39 		for row in engine:select("SELECT `jid`,`nick`,`subscription`,`ask` FROM `rosterusers` WHERE `username`=?", user or "") do
       
    40 			local contact = row[1];
       
    41 			local name = row[2];
       
    42 			if name == "" then name = nil; end
       
    43 			local subscription = row[3];
       
    44 			if subscription == "N" then
       
    45 				subscription = "none"
       
    46 			elseif subscription == "B" then
       
    47 				subscription = "both"
       
    48 			elseif subscription == "F" then
       
    49 				subscription = "from"
       
    50 			elseif subscription == "T" then
       
    51 				subscription = "to"
       
    52 			else error("Unknown subscription type: "..subscription) end;
       
    53 			local ask = row[4];
       
    54 			if ask == "N" then
       
    55 				ask = nil;
       
    56 			elseif ask == "O" then
       
    57 				ask = "subscribe";
       
    58 			elseif ask == "I" then
       
    59 				if pending == nil then pending = {} end;
       
    60 				pending[contact] = true;
       
    61 				ask = nil;
       
    62 			elseif ask == "B" then
       
    63 				if pending == nil then pending = {} end;
       
    64 				pending[contact] = true;
       
    65 				ask = "subscribe";
       
    66 			else error("Unknown ask type: "..ask); end
       
    67 
       
    68 			--local askmessage = row[5];
       
    69 			--local server = row[6];
       
    70 			--local subscribe = row[7];
       
    71 			--local type = row[8];
       
    72 			--local created_at = row[9];
       
    73 
       
    74 			local groups = {};
       
    75 			for row in engine:select("SELECT `grp` FROM `rostergroups` WHERE `username`=? AND `jid`=?", user or "", contact) do
       
    76 				local group = row[1];
       
    77 				groups[group] = true;
       
    78 			end
       
    79 
       
    80 			roster[contact] = { name = name, ask = ask, subscription = subscription, groups = groups };
       
    81 		end
       
    82 		return roster;
       
    83 
       
    84 	elseif store == "vcard" then
       
    85 		local result = nil;
       
    86 		for row in engine:select("SELECT `vcard` FROM `vcard` WHERE `username`=? LIMIT 1", user or "") do result = row end
       
    87 		if not result then
       
    88 			return nil;
       
    89 		end
       
    90 		local data, err = xml_parse(result[1]);
       
    91 		if data then
       
    92 			return stanza_preserialize(data);
       
    93 		end
       
    94 
       
    95 	elseif store == "private" then
       
    96 		local private = nil;
       
    97 		local result;
       
    98 		for row in engine:select("SELECT `namespace`,`data` FROM `private_storage` WHERE `username`=?", user or "") do
       
    99 			if private == nil then private = {} end;
       
   100 			local namespace = row[1];
       
   101 			local data, err = xml_parse(row[2]);
       
   102 			if data then
       
   103 				private[namespace] = stanza_preserialize(data);
       
   104 			end
       
   105 		end
       
   106 		return private;
       
   107 	end
       
   108 end
       
   109 
       
   110 --- Key/value store API (default store type)
       
   111 
       
   112 local keyval_store = {};
       
   113 keyval_store.__index = keyval_store;
       
   114 function keyval_store:get(username)
       
   115 	user, store = username, self.store;
       
   116 	local ok, result = engine:transaction(keyval_store_get);
       
   117 	if not ok then
       
   118 		module:log("error", "Unable to read from database %s store for %s: %s", store, username or "<host>", result);
       
   119 		return nil, result;
       
   120 	end
       
   121 	return result;
       
   122 end
       
   123 
       
   124 function keyval_store:users()
       
   125 	local ok, result = engine:transaction(function()
       
   126 		return engine:select("SELECT `username` FROM `users`");
       
   127 	end);
       
   128 	if not ok then return ok, result end
       
   129 	return iterator(result);
       
   130 end
       
   131 
       
   132 local stores = {
       
   133 	keyval = keyval_store;
       
   134 };
       
   135 
       
   136 --- Implement storage driver API
       
   137 
       
   138 -- FIXME: Some of these operations need to operate on the archive store(s) too
       
   139 
       
   140 local driver = {};
       
   141 
       
   142 function driver:open(store, typ)
       
   143 	local store_mt = stores[typ or "keyval"];
       
   144 	if store_mt then
       
   145 		return setmetatable({ store = store }, store_mt);
       
   146 	end
       
   147 	return nil, "unsupported-store";
       
   148 end
       
   149 
       
   150 function driver:stores(username)
       
   151 	local query = "SELECT 'accounts', 'roster', 'vcard', 'private'";
       
   152 	if username == true or not username then
       
   153 		username = "";
       
   154 	end
       
   155 	local ok, result = engine:transaction(function()
       
   156 		return engine:select(query, host, username);
       
   157 	end);
       
   158 	if not ok then return ok, result end
       
   159 	return iterator(result);
       
   160 end
       
   161 
       
   162 --- Initialization
       
   163 
       
   164 
       
   165 local function normalize_params(params)
       
   166 	if params.driver == "SQLite3" then
       
   167 		if params.database ~= ":memory:" then
       
   168 			params.database = resolve_relative_path(prosody.paths.data or ".", params.database or "prosody.sqlite");
       
   169 		end
       
   170 	end
       
   171 	assert(params.driver and params.database, "Configuration error: Both the SQL driver and the database need to be specified");
       
   172 	return params;
       
   173 end
       
   174 
       
   175 function module.load()
       
   176 	if prosody.prosodyctl then return; end
       
   177 	local engines = module:shared("/*/sql/connections");
       
   178 	local params = normalize_params(module:get_option("sql", default_params));
       
   179 	engine = engines[sql.db2uri(params)];
       
   180 	if not engine then
       
   181 		module:log("debug", "Creating new engine");
       
   182 		engine = sql:create_engine(params);
       
   183 		engines[sql.db2uri(params)] = engine;
       
   184 	end
       
   185 
       
   186 	module:provides("storage", driver);
       
   187 end