xmlhandlers: Don't restrict CDATA
[prosody.git] / core / modulemanager.lua
1 -- Prosody IM
2 -- Copyright (C) 2008-2010 Matthew Wild
3 -- Copyright (C) 2008-2010 Waqas Hussain
4 -- 
5 -- This project is MIT/X11 licensed. Please see the
6 -- COPYING file in the source package for more information.
7 --
8
9 local plugin_dir = CFG_PLUGINDIR or "./plugins/";
10
11 local logger = require "util.logger";
12 local log = logger.init("modulemanager");
13 local eventmanager = require "core.eventmanager";
14 local config = require "core.configmanager";
15 local multitable_new = require "util.multitable".new;
16 local st = require "util.stanza";
17 local pluginloader = require "util.pluginloader";
18
19 local hosts = hosts;
20 local prosody = prosody;
21
22 local loadfile, pcall, xpcall = loadfile, pcall, xpcall;
23 local setmetatable, setfenv, getfenv = setmetatable, setfenv, getfenv;
24 local pairs, ipairs = pairs, ipairs;
25 local t_insert, t_concat = table.insert, table.concat;
26 local type = type;
27 local next = next;
28 local rawget = rawget;
29 local error = error;
30 local tostring, tonumber = tostring, tonumber;
31
32 local debug_traceback = debug.traceback;
33 local unpack, select = unpack, select;
34 pcall = function(f, ...)
35         local n = select("#", ...);
36         local params = {...};
37         return xpcall(function() f(unpack(params, 1, n)) end, function(e) return tostring(e).."\n"..debug_traceback(); end);
38 end
39
40 local array, set = require "util.array", require "util.set";
41
42 local autoload_modules = {"presence", "message", "iq"};
43
44 -- We need this to let modules access the real global namespace
45 local _G = _G;
46
47 module "modulemanager"
48
49 api = {};
50 local api = api; -- Module API container
51
52 local modulemap = { ["*"] = {} };
53
54 local stanza_handlers = multitable_new();
55 local handler_info = {};
56
57 local modulehelpers = setmetatable({}, { __index = _G });
58
59 local handler_table = multitable_new();
60 local hooked = multitable_new();
61 local hooks = multitable_new();
62 local event_hooks = multitable_new();
63
64 local NULL = {};
65
66 -- Load modules when a host is activated
67 function load_modules_for_host(host)
68         local disabled_set = {};
69         local modules_disabled = config.get(host, "core", "modules_disabled");
70         if modules_disabled then
71                 for _, module in ipairs(modules_disabled) do
72                         disabled_set[module] = true;
73                 end
74         end
75
76         -- Load auto-loaded modules for this host
77         if hosts[host].type == "local" then
78                 for _, module in ipairs(autoload_modules) do
79                         if not disabled_set[module] then
80                                 load(host, module);
81                         end
82                 end
83         end
84
85         -- Load modules from global section
86         if config.get(host, "core", "load_global_modules") ~= false then
87                 local modules_enabled = config.get("*", "core", "modules_enabled");
88                 if modules_enabled then
89                         for _, module in ipairs(modules_enabled) do
90                                 if not disabled_set[module] and not is_loaded(host, module) then
91                                         load(host, module);
92                                 end
93                         end
94                 end
95         end
96         
97         -- Load modules from just this host
98         local modules_enabled = config.get(host, "core", "modules_enabled");
99         if modules_enabled and modules_enabled ~= config.get("*", "core", "modules_enabled") then
100                 for _, module in pairs(modules_enabled) do
101                         if not is_loaded(host, module) then
102                                 load(host, module);
103                         end
104                 end
105         end
106 end
107 eventmanager.add_event_hook("host-activated", load_modules_for_host);
108 eventmanager.add_event_hook("component-activated", load_modules_for_host);
109 --
110
111 function load(host, module_name, config)
112         if not (host and module_name) then
113                 return nil, "insufficient-parameters";
114         end
115         
116         if not modulemap[host] then
117                 modulemap[host] = {};
118         end
119         
120         if modulemap[host][module_name] then
121                 log("warn", "%s is already loaded for %s, so not loading again", module_name, host);
122                 return nil, "module-already-loaded";
123         elseif modulemap["*"][module_name] then
124                 return nil, "global-module-already-loaded";
125         end
126         
127
128         local mod, err = pluginloader.load_code(module_name);
129         if not mod then
130                 log("error", "Unable to load module '%s': %s", module_name or "nil", err or "nil");
131                 return nil, err;
132         end
133
134         local _log = logger.init(host..":"..module_name);
135         local api_instance = setmetatable({ name = module_name, host = host, config = config,  _log = _log, log = function (self, ...) return _log(...); end }, { __index = api });
136
137         local pluginenv = setmetatable({ module = api_instance }, { __index = _G });
138         api_instance.environment = pluginenv;
139         
140         setfenv(mod, pluginenv);
141         if not hosts[host] then
142                 local create_component = _G.require "core.componentmanager".create_component;
143                 hosts[host] = create_component(host);
144                 hosts[host].connected = false;
145                 log("debug", "Created new component: %s", host);
146         end
147         hosts[host].modules = modulemap[host];
148         modulemap[host][module_name] = pluginenv;
149         
150         local success, err = pcall(mod);
151         if success then
152                 if module_has_method(pluginenv, "load") then
153                         success, err = call_module_method(pluginenv, "load");
154                         if not success then
155                                 log("warn", "Error loading module '%s' on '%s': %s", module_name, host, err or "nil");
156                         end
157                 end
158
159                 -- Use modified host, if the module set one
160                 if api_instance.host == "*" and host ~= "*" then
161                         modulemap[host][module_name] = nil;
162                         modulemap["*"][module_name] = pluginenv;
163                         api_instance:set_global();
164                 end
165         else
166                 log("error", "Error initializing module '%s' on '%s': %s", module_name, host, err or "nil");
167         end
168         if success then
169                 (hosts[api_instance.host] or prosody).events.fire_event("module-loaded", { module = module_name, host = host });
170                 return true;
171         else -- load failed, unloading
172                 unload(api_instance.host, module_name);
173                 return nil, err;
174         end
175 end
176
177 function get_module(host, name)
178         return modulemap[host] and modulemap[host][name];
179 end
180
181 function is_loaded(host, name)
182         return modulemap[host] and modulemap[host][name] and true;
183 end
184
185 function unload(host, name, ...)
186         local mod = get_module(host, name);
187         if not mod then return nil, "module-not-loaded"; end
188         
189         if module_has_method(mod, "unload") then
190                 local ok, err = call_module_method(mod, "unload");
191                 if (not ok) and err then
192                         log("warn", "Non-fatal error unloading module '%s' on '%s': %s", name, host, err);
193                 end
194         end
195         local params = handler_table:get(host, name); -- , {module.host, origin_type, tag, xmlns}
196         for _, param in pairs(params or NULL) do
197                 local handlers = stanza_handlers:get(param[1], param[2], param[3], param[4]);
198                 if handlers then
199                         handler_info[handlers[1]] = nil;
200                         stanza_handlers:remove(param[1], param[2], param[3], param[4]);
201                 end
202         end
203         event_hooks:remove(host, name);
204         -- unhook event handlers hooked by module:hook
205         for event, handlers in pairs(hooks:get(host, name) or NULL) do
206                 for handler in pairs(handlers or NULL) do
207                         (hosts[host] or prosody).events.remove_handler(event, handler);
208                 end
209         end
210         hooks:remove(host, name);
211         if mod.module.items then -- remove items
212                 for key,t in pairs(mod.module.items) do
213                         for i = #t,1,-1 do
214                                 local value = t[i];
215                                 t[i] = nil;
216                                 hosts[host].events.fire_event("item-removed/"..key, {source = self, item = value});
217                         end
218                 end
219         end
220         modulemap[host][name] = nil;
221         (hosts[host] or prosody).events.fire_event("module-unloaded", { module = name, host = host });
222         return true;
223 end
224
225 function reload(host, name, ...)
226         local mod = get_module(host, name);
227         if not mod then return nil, "module-not-loaded"; end
228
229         local _mod, err = pluginloader.load_code(name); -- checking for syntax errors
230         if not _mod then
231                 log("error", "Unable to load module '%s': %s", name or "nil", err or "nil");
232                 return nil, err;
233         end
234
235         local saved;
236
237         if module_has_method(mod, "save") then
238                 local ok, ret, err = call_module_method(mod, "save");
239                 if ok then
240                         saved = ret;
241                 else
242                         log("warn", "Error saving module '%s:%s' state: %s", host, module, ret);
243                         if not config.get(host, "core", "force_module_reload") then
244                                 log("warn", "Aborting reload due to error, set force_module_reload to ignore this");
245                                 return nil, "save-state-failed";
246                         else
247                                 log("warn", "Continuing with reload (using the force)");
248                         end
249                 end
250         end
251
252         unload(host, name, ...);
253         local ok, err = load(host, name, ...);
254         if ok then
255                 mod = get_module(host, name);
256                 if module_has_method(mod, "restore") then
257                         local ok, err = call_module_method(mod, "restore", saved or {})
258                         if (not ok) and err then
259                                 log("warn", "Error restoring module '%s' from '%s': %s", name, host, err);
260                         end
261                 end
262                 return true;
263         end
264         return ok, err;
265 end
266
267 function handle_stanza(host, origin, stanza)
268         local name, xmlns, origin_type = stanza.name, stanza.attr.xmlns or "jabber:client", origin.type;
269         if name == "iq" and xmlns == "jabber:client" then
270                 if stanza.attr.type == "get" or stanza.attr.type == "set" then
271                         xmlns = stanza.tags[1].attr.xmlns or "jabber:client";
272                         log("debug", "Stanza of type %s from %s has xmlns: %s", name, origin_type, xmlns);
273                 else
274                         log("debug", "Discarding %s from %s of type: %s", name, origin_type, stanza.attr.type);
275                         return true;
276                 end
277         end
278         local handlers = stanza_handlers:get(host, origin_type, name, xmlns);
279         if not handlers then handlers = stanza_handlers:get("*", origin_type, name, xmlns); end
280         if handlers then
281                 log("debug", "Passing stanza to mod_%s", handler_info[handlers[1]].name);
282                 (handlers[1])(origin, stanza);
283                 return true;
284         else
285                 if stanza.attr.xmlns == nil then
286                         log("debug", "Unhandled %s stanza: %s; xmlns=%s", origin.type, stanza.name, xmlns); -- we didn't handle it
287                         if stanza.attr.type ~= "error" and stanza.attr.type ~= "result" then
288                                 origin.send(st.error_reply(stanza, "cancel", "service-unavailable"));
289                         end
290                 elseif not((name == "features" or name == "error") and xmlns == "http://etherx.jabber.org/streams") then -- FIXME remove check once we handle S2S features
291                         log("warn", "Unhandled %s stream element: %s; xmlns=%s: %s", origin.type, stanza.name, xmlns, tostring(stanza)); -- we didn't handle it
292                         origin:close("unsupported-stanza-type");
293                 end
294         end
295 end
296
297 function module_has_method(module, method)
298         return type(module.module[method]) == "function";
299 end
300
301 function call_module_method(module, method, ...)
302         if module_has_method(module, method) then
303                 local f = module.module[method];
304                 return pcall(f, ...);
305         else
306                 return false, "no-such-method";
307         end
308 end
309
310 ----- API functions exposed to modules -----------
311 -- Must all be in api.*
312
313 -- Returns the name of the current module
314 function api:get_name()
315         return self.name;
316 end
317
318 -- Returns the host that the current module is serving
319 function api:get_host()
320         return self.host;
321 end
322
323 function api:get_host_type()
324         return hosts[self.host].type;
325 end
326
327 function api:set_global()
328         self.host = "*";
329         -- Update the logger
330         local _log = logger.init("mod_"..self.name);
331         self.log = function (self, ...) return _log(...); end;
332         self._log = _log;
333 end
334
335 local function _add_handler(module, origin_type, tag, xmlns, handler)
336         local handlers = stanza_handlers:get(module.host, origin_type, tag, xmlns);
337         local msg = (tag == "iq") and "namespace" or "payload namespace";
338         if not handlers then
339                 stanza_handlers:add(module.host, origin_type, tag, xmlns, handler);
340                 handler_info[handler] = module;
341                 handler_table:add(module.host, module.name, {module.host, origin_type, tag, xmlns});
342                 --module:log("debug", "I now handle tag '%s' [%s] with %s '%s'", tag, origin_type, msg, xmlns);
343         else
344                 module:log("warn", "I wanted to handle tag '%s' [%s] with %s '%s' but mod_%s already handles that", tag, origin_type, msg, xmlns, handler_info[handlers[1]].module.name);
345         end
346 end
347
348 function api:add_handler(origin_type, tag, xmlns, handler)
349         if not (origin_type and tag and xmlns and handler) then return false; end
350         if type(origin_type) == "table" then
351                 for _, origin_type in ipairs(origin_type) do
352                         _add_handler(self, origin_type, tag, xmlns, handler);
353                 end
354         else
355                 _add_handler(self, origin_type, tag, xmlns, handler);
356         end
357 end
358 function api:add_iq_handler(origin_type, xmlns, handler)
359         self:add_handler(origin_type, "iq", xmlns, handler);
360 end
361
362 function api:add_feature(xmlns)
363         self:add_item("feature", xmlns);
364 end
365 function api:add_identity(category, type, name)
366         self:add_item("identity", {category = category, type = type, name = name});
367 end
368
369 local event_hook = function(host, mod_name, event_name, ...)
370         if type((...)) == "table" and (...).host and (...).host ~= host then return; end
371         for handler in pairs(event_hooks:get(host, mod_name, event_name) or NULL) do
372                 handler(...);
373         end
374 end;
375 function api:add_event_hook(name, handler)
376         if not hooked:get(self.host, self.name, name) then
377                 eventmanager.add_event_hook(name, function(...) event_hook(self.host, self.name, name, ...); end);
378                 hooked:set(self.host, self.name, name, true);
379         end
380         event_hooks:set(self.host, self.name, name, handler, true);
381 end
382
383 function api:fire_event(...)
384         return (hosts[self.host] or prosody).events.fire_event(...);
385 end
386
387 function api:hook(event, handler, priority)
388         hooks:set(self.host, self.name, event, handler, true);
389         (hosts[self.host] or prosody).events.add_handler(event, handler, priority);
390 end
391
392 function api:hook_stanza(xmlns, name, handler, priority)
393         if not handler and type(name) == "function" then
394                 -- If only 2 options then they specified no xmlns
395                 xmlns, name, handler, priority = nil, xmlns, name, handler;
396         elseif not (handler and name) then
397                 self:log("warn", "Error: Insufficient parameters to module:hook_stanza()");
398                 return;
399         end
400         return api.hook(self, "stanza/"..(xmlns and (xmlns..":") or "")..name, function (data) return handler(data.origin, data.stanza, data); end, priority);
401 end
402
403 function api:require(lib)
404         local f, n = pluginloader.load_code(self.name, lib..".lib.lua");
405         if not f then
406                 f, n = pluginloader.load_code(lib, lib..".lib.lua");
407         end
408         if not f then error("Failed to load plugin library '"..lib.."', error: "..n); end -- FIXME better error message
409         setfenv(f, self.environment);
410         return f();
411 end
412
413 function api:get_option(name, default_value)
414         local value = config.get(self.host, self.name, name);
415         if value == nil then
416                 value = config.get(self.host, "core", name);
417                 if value == nil then
418                         value = default_value;
419                 end
420         end
421         return value;
422 end
423
424 function api:get_option_string(name, default_value)
425         local value = self:get_option(name, default_value);
426         if type(value) == "table" then
427                 if #value > 1 then
428                         self:log("error", "Config option '%s' does not take a list, using just the first item", name);
429                 end
430                 value = value[1];
431         end
432         if value == nil then
433                 return nil;
434         end
435         return tostring(value);
436 end
437
438 function api:get_option_number(name, ...)
439         local value = self:get_option(name, ...);
440         if type(value) == "table" then
441                 if #value > 1 then
442                         self:log("error", "Config option '%s' does not take a list, using just the first item", name);
443                 end
444                 value = value[1];
445         end
446         local ret = tonumber(value);
447         if value ~= nil and ret == nil then
448                 self:log("error", "Config option '%s' not understood, expecting a number", name);
449         end
450         return ret;
451 end
452
453 function api:get_option_boolean(name, ...)
454         local value = self:get_option(name, ...);
455         if type(value) == "table" then
456                 if #value > 1 then
457                         self:log("error", "Config option '%s' does not take a list, using just the first item", name);
458                 end
459                 value = value[1];
460         end
461         if value == nil then
462                 return nil;
463         end
464         local ret = value == true or value == "true" or value == 1 or nil;
465         if ret == nil then
466                 ret = (value == false or value == "false" or value == 0);
467                 if ret then
468                         ret = false;
469                 else
470                         ret = nil;
471                 end
472         end
473         if ret == nil then
474                 self:log("error", "Config option '%s' not understood, expecting true/false", name);
475         end
476         return ret;
477 end
478
479 function api:get_option_array(name, ...)
480         local value = self:get_option(name, ...);
481
482         if value == nil then
483                 return nil;
484         end
485         
486         if type(value) ~= "table" then
487                 return array{ value }; -- Assume any non-list is a single-item list
488         end
489         
490         return array():append(value); -- Clone
491 end
492
493 function api:get_option_set(name, ...)
494         local value = self:get_option_array(name, ...);
495         
496         if value == nil then
497                 return nil;
498         end
499         
500         return set.new(value);
501 end
502
503 local t_remove = _G.table.remove;
504 local module_items = multitable_new();
505 function api:add_item(key, value)
506         self.items = self.items or {};
507         self.items[key] = self.items[key] or {};
508         t_insert(self.items[key], value);
509         self:fire_event("item-added/"..key, {source = self, item = value});
510 end
511 function api:remove_item(key, value)
512         local t = self.items and self.items[key] or NULL;
513         for i = #t,1,-1 do
514                 if t[i] == value then
515                         t_remove(self.items[key], i);
516                         self:fire_event("item-removed/"..key, {source = self, item = value});
517                         return value;
518                 end
519         end
520 end
521
522 function api:get_host_items(key)
523         local result = {};
524         for mod_name, module in pairs(modulemap[self.host]) do
525                 module = module.module;
526                 if module.items then
527                         for _, item in ipairs(module.items[key] or NULL) do
528                                 t_insert(result, item);
529                         end
530                 end
531         end
532         for mod_name, module in pairs(modulemap["*"]) do
533                 module = module.module;
534                 if module.items then
535                         for _, item in ipairs(module.items[key] or NULL) do
536                                 t_insert(result, item);
537                         end
538                 end
539         end
540         return result;
541 end
542
543 return _M;