util.x509: Only collect commonNames that pass idna
[prosody.git] / core / storagemanager.lua
blobdea71733ade777ae5fb51a0303c296fbdabc5d24
2 local type, pairs = type, pairs;
3 local setmetatable = setmetatable;
4 local rawset = rawset;
6 local config = require "core.configmanager";
7 local datamanager = require "util.datamanager";
8 local modulemanager = require "core.modulemanager";
9 local multitable = require "util.multitable";
10 local log = require "util.logger".init("storagemanager");
11 local async = require "util.async";
12 local debug = debug;
14 local prosody = prosody;
15 local hosts = prosody.hosts;
17 local _ENV = nil;
18 -- luacheck: std none
20 local olddm = {}; -- maintain old datamanager, for backwards compatibility
21 for k,v in pairs(datamanager) do olddm[k] = v; end
23 local null_storage_method = function () return false, "no data storage active"; end
24 local null_storage_driver = setmetatable(
26 name = "null",
27 open = function (self) return self; end
28 }, {
29 __index = function (self, method) --luacheck: ignore 212
30 return null_storage_method;
31 end
35 local async_check = config.get("*", "storage_async_check") == true;
37 local stores_available = multitable.new();
39 local function check_async_wrapper(event)
40 local store = event.store;
41 event.store = setmetatable({}, {
42 __index = function (t, method_name)
43 local original_method = store[method_name];
44 if type(original_method) ~= "function" then
45 if original_method then
46 rawset(t, method_name, original_method);
47 end
48 return original_method;
49 end
50 local wrapped_method = function (...)
51 if not async.ready() then
52 log("warn", "ASYNC-01: Attempt to access storage outside async context, "
53 .."see https://prosody.im/doc/developers/async - %s", debug.traceback());
54 end
55 return original_method(...);
56 end
57 rawset(t, method_name, wrapped_method);
58 return wrapped_method;
59 end;
60 });
61 end
63 local function initialize_host(host)
64 local host_session = hosts[host];
65 host_session.events.add_handler("item-added/storage-provider", function (event)
66 local item = event.item;
67 stores_available:set(host, item.name, item);
68 end);
70 host_session.events.add_handler("item-removed/storage-provider", function (event)
71 local item = event.item;
72 stores_available:set(host, item.name, nil);
73 end);
74 if async_check then
75 host_session.events.add_handler("store-opened", check_async_wrapper);
76 end
77 end
78 prosody.events.add_handler("host-activated", initialize_host, 101);
80 local function load_driver(host, driver_name)
81 if driver_name == "null" then
82 return null_storage_driver;
83 end
84 local driver = stores_available:get(host, driver_name);
85 if driver then return driver; end
86 local ok, err = modulemanager.load(host, "storage_"..driver_name);
87 if not ok then
88 log("error", "Failed to load storage driver plugin %s on %s: %s", driver_name, host, err);
89 end
90 return stores_available:get(host, driver_name);
91 end
93 local function get_storage_config(host)
94 -- COMPAT w/ unreleased Prosody 0.10 and the once-experimental mod_storage_sql2 in peoples' config files
95 local storage_config = config.get(host, "storage");
96 local found_sql2;
97 if storage_config == "sql2" then
98 storage_config, found_sql2 = "sql", true;
99 elseif type(storage_config) == "table" then
100 for store_name, driver_name in pairs(storage_config) do
101 if driver_name == "sql2" then
102 storage_config[store_name] = "sql";
103 found_sql2 = true;
107 if found_sql2 then
108 log("error", "The temporary 'sql2' storage module has now been renamed to 'sql', "
109 .."please update your config file: https://prosody.im/doc/modules/mod_storage_sql2");
111 return storage_config;
114 local function get_driver(host, store)
115 local storage = get_storage_config(host);
116 local driver_name;
117 local option_type = type(storage);
118 if option_type == "string" then
119 driver_name = storage;
120 elseif option_type == "table" then
121 driver_name = storage[store];
123 if not driver_name then
124 driver_name = config.get(host, "default_storage") or "internal";
127 local driver = load_driver(host, driver_name);
128 if not driver then
129 log("warn", "Falling back to null driver for %s storage on %s", store, host);
130 driver_name = "null";
131 driver = null_storage_driver;
133 return driver, driver_name;
136 local map_shim_mt = {
137 __index = {
138 get = function(self, username, key)
139 local ret, err = self.keyval_store:get(username);
140 if ret == nil then return nil, err end
141 return ret[key];
142 end;
143 set = function(self, username, key, data)
144 local current, err = self.keyval_store:get(username);
145 if current == nil then
146 if err then
147 return nil, err;
148 else
149 current = {};
152 current[key] = data;
153 return self.keyval_store:set(username, current);
154 end;
155 set_keys = function (self, username, keydatas)
156 local current, err = self.keyval_store:get(username);
157 if current == nil then
158 if err then
159 return nil, err;
161 current = {};
163 for k,v in pairs(keydatas) do
164 if v == self.remove then v = nil; end
165 current[k] = v;
167 return self.keyval_store:set(username, current);
168 end;
169 remove = {};
173 local open; -- forward declaration
175 local function create_map_shim(host, store)
176 local keyval_store, err = open(host, store, "keyval");
177 if keyval_store == nil then return nil, err end
178 return setmetatable({
179 keyval_store = keyval_store;
180 }, map_shim_mt);
183 function open(host, store, typ)
184 local driver, driver_name = get_driver(host, store);
185 local ret, err = driver:open(store, typ);
186 if not ret then
187 if err == "unsupported-store" then
188 if typ == "map" then -- Use shim on top of keyval store
189 log("debug", "map storage driver unavailable, using shim on top of keyval store.");
190 ret, err = create_map_shim(host, store);
191 else
192 log("debug", "Storage driver %s does not support store %s (%s), falling back to null driver",
193 driver_name, store, typ or "<nil>");
194 ret, err = null_storage_driver, nil;
198 if ret then
199 local event_data = { host = host, store_name = store, store_type = typ, store = ret };
200 hosts[host].events.fire_event("store-opened", event_data);
201 ret, err = event_data.store, event_data.store_err;
203 return ret, err;
206 local function purge(user, host)
207 local storage = get_storage_config(host);
208 if type(storage) == "table" then
209 -- multiple storage backends in use that we need to purge
210 local purged = {};
211 for store, driver_name in pairs(storage) do
212 if not purged[driver_name] then
213 local driver = get_driver(host, store);
214 if driver.purge then
215 purged[driver_name] = driver:purge(user);
216 else
217 log("warn", "Storage driver %s does not support removing all user data, "
218 .."you may need to delete it manually", driver_name);
223 get_driver(host):purge(user); -- and the default driver
225 olddm.purge(user, host); -- COMPAT list stores, like offline messages end up in the old datamanager
227 return true;
230 function datamanager.load(username, host, datastore)
231 return open(host, datastore):get(username);
233 function datamanager.store(username, host, datastore, data)
234 return open(host, datastore):set(username, data);
236 function datamanager.users(host, datastore, typ)
237 local driver = open(host, datastore, typ);
238 if not driver.users then
239 return function() log("warn", "Storage driver %s does not support listing users", driver.name) end
241 return driver:users();
243 function datamanager.stores(username, host, typ)
244 return get_driver(host):stores(username, typ);
246 function datamanager.purge(username, host)
247 return purge(username, host);
250 return {
251 initialize_host = initialize_host;
252 load_driver = load_driver;
253 get_driver = get_driver;
254 open = open;
255 purge = purge;
257 olddm = olddm;