From 76628c7453e56d5e156214c142ca05f9908c0191 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 10 Dec 2011 17:21:19 +0000 Subject: configmanager: get(): Make section (core) optional (hurrah) --- core/configmanager.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'core') diff --git a/core/configmanager.lua b/core/configmanager.lua index 85919492..e2253171 100644 --- a/core/configmanager.lua +++ b/core/configmanager.lua @@ -41,6 +41,9 @@ function getconfig() end function get(host, section, key) + if not key then + section, key = "core", section; + end local sec = config[host][section]; if sec then return sec[key]; -- cgit v1.2.3 From 36542853efea8ca4a4767996408e46b1657ec93e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Jan 2012 18:41:55 +0000 Subject: modulemanager: Move in-module API functions to core.moduleapi (half the file size, yay) --- core/moduleapi.lua | 239 +++++++++++++++++++++++++++++++++++++++++++++++++ core/modulemanager.lua | 213 +------------------------------------------ 2 files changed, 240 insertions(+), 212 deletions(-) create mode 100644 core/moduleapi.lua (limited to 'core') diff --git a/core/moduleapi.lua b/core/moduleapi.lua new file mode 100644 index 00000000..ee960709 --- /dev/null +++ b/core/moduleapi.lua @@ -0,0 +1,239 @@ +-- Prosody IM +-- Copyright (C) 2008-2012 Matthew Wild +-- Copyright (C) 2008-2012 Waqas Hussain +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + +local config = require "core.configmanager"; +local modulemanager = require "modulemanager"; +local array = require "util.array"; +local set = require "util.set"; +local logger = require "util.logger"; +local pluginloader = require "util.pluginloader"; + +local multitable_new = require "util.multitable".new; + +local t_insert, t_remove, t_concat = table.insert, table.remove, table.concat; +local error, setmetatable, setfenv, type = error, setmetatable, setfenv, type; +local ipairs, pairs, select, unpack = ipairs, pairs, select, unpack; +local tonumber, tostring = tonumber, tostring; + +local prosody = prosody; +local hosts = prosody.hosts; + +-- Registry of shared module data +local shared_data = setmetatable({}, { __mode = "v" }); + +local NULL = {}; + +local api = {}; + +-- Returns the name of the current module +function api:get_name() + return self.name; +end + +-- Returns the host that the current module is serving +function api:get_host() + return self.host; +end + +function api:get_host_type() + return hosts[self.host].type; +end + +function api:set_global() + self.host = "*"; + -- Update the logger + local _log = logger.init("mod_"..self.name); + self.log = function (self, ...) return _log(...); end; + self._log = _log; +end + +function api:add_feature(xmlns) + self:add_item("feature", xmlns); +end +function api:add_identity(category, type, name) + self:add_item("identity", {category = category, type = type, name = name}); +end +function api:add_extension(data) + self:add_item("extension", data); +end + +function api:fire_event(...) + return (hosts[self.host] or prosody).events.fire_event(...); +end + +function api:hook(event, handler, priority) + hooks:set(self.host, self.name, event, handler, true); + (hosts[self.host] or prosody).events.add_handler(event, handler, priority); +end + +function api:hook_global(event, handler, priority) + hooks:set("*", self.name, event, handler, true); + prosody.events.add_handler(event, handler, priority); +end + +function api:hook_stanza(xmlns, name, handler, priority) + if not handler and type(name) == "function" then + -- If only 2 options then they specified no xmlns + xmlns, name, handler, priority = nil, xmlns, name, handler; + elseif not (handler and name) then + self:log("warn", "Error: Insufficient parameters to module:hook_stanza()"); + return; + end + return self:hook("stanza/"..(xmlns and (xmlns..":") or "")..name, function (data) return handler(data.origin, data.stanza, data); end, priority); +end + +function api:require(lib) + local f, n = pluginloader.load_code(self.name, lib..".lib.lua"); + if not f then + f, n = pluginloader.load_code(lib, lib..".lib.lua"); + end + if not f then error("Failed to load plugin library '"..lib.."', error: "..n); end -- FIXME better error message + setfenv(f, self.environment); + return f(); +end + +function api:get_option(name, default_value) + local value = config.get(self.host, self.name, name); + if value == nil then + value = config.get(self.host, "core", name); + if value == nil then + value = default_value; + end + end + return value; +end + +function api:get_option_string(name, default_value) + local value = self:get_option(name, default_value); + if type(value) == "table" then + if #value > 1 then + self:log("error", "Config option '%s' does not take a list, using just the first item", name); + end + value = value[1]; + end + if value == nil then + return nil; + end + return tostring(value); +end + +function api:get_option_number(name, ...) + local value = self:get_option(name, ...); + if type(value) == "table" then + if #value > 1 then + self:log("error", "Config option '%s' does not take a list, using just the first item", name); + end + value = value[1]; + end + local ret = tonumber(value); + if value ~= nil and ret == nil then + self:log("error", "Config option '%s' not understood, expecting a number", name); + end + return ret; +end + +function api:get_option_boolean(name, ...) + local value = self:get_option(name, ...); + if type(value) == "table" then + if #value > 1 then + self:log("error", "Config option '%s' does not take a list, using just the first item", name); + end + value = value[1]; + end + if value == nil then + return nil; + end + local ret = value == true or value == "true" or value == 1 or nil; + if ret == nil then + ret = (value == false or value == "false" or value == 0); + if ret then + ret = false; + else + ret = nil; + end + end + if ret == nil then + self:log("error", "Config option '%s' not understood, expecting true/false", name); + end + return ret; +end + +function api:get_option_array(name, ...) + local value = self:get_option(name, ...); + + if value == nil then + return nil; + end + + if type(value) ~= "table" then + return array{ value }; -- Assume any non-list is a single-item list + end + + return array():append(value); -- Clone +end + +function api:get_option_set(name, ...) + local value = self:get_option_array(name, ...); + + if value == nil then + return nil; + end + + return set.new(value); +end + +local module_items = multitable_new(); +function api:add_item(key, value) + self.items = self.items or {}; + self.items[key] = self.items[key] or {}; + t_insert(self.items[key], value); + self:fire_event("item-added/"..key, {source = self, item = value}); +end +function api:remove_item(key, value) + local t = self.items and self.items[key] or NULL; + for i = #t,1,-1 do + if t[i] == value then + t_remove(self.items[key], i); + self:fire_event("item-removed/"..key, {source = self, item = value}); + return value; + end + end +end + +function api:get_host_items(key) + local result = {}; + for mod_name, module in pairs(modulemanager.get_modules(self.host)) do + module = module.module; + if module.items then + for _, item in ipairs(module.items[key] or NULL) do + t_insert(result, item); + end + end + end + for mod_name, module in pairs(modulemanager.get_modules("*")) do + module = module.module; + if module.items then + for _, item in ipairs(module.items[key] or NULL) do + t_insert(result, item); + end + end + end + return result; +end + +function api:handle_items(type, added_cb, removed_cb, existing) + self:hook("item-added/"..type, added_cb); + self:hook("item-removed/"..type, removed_cb); + if existing ~= false then + for _, item in ipairs(self:get_host_items(type)) do + added_cb({ item = item }); + end + end +end + +return api; diff --git a/core/modulemanager.lua b/core/modulemanager.lua index c4d95695..bbe24e32 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -45,8 +45,7 @@ local _G = _G; module "modulemanager" -api = {}; -local api = api; -- Module API container +local api = _G.require "core.moduleapi"; -- Module API container local modulemap = { ["*"] = {} }; @@ -253,214 +252,4 @@ function call_module_method(module, method, ...) end end ------ API functions exposed to modules ----------- --- Must all be in api.* - --- Returns the name of the current module -function api:get_name() - return self.name; -end - --- Returns the host that the current module is serving -function api:get_host() - return self.host; -end - -function api:get_host_type() - return hosts[self.host].type; -end - -function api:set_global() - self.host = "*"; - -- Update the logger - local _log = logger.init("mod_"..self.name); - self.log = function (self, ...) return _log(...); end; - self._log = _log; -end - -function api:add_feature(xmlns) - self:add_item("feature", xmlns); -end -function api:add_identity(category, type, name) - self:add_item("identity", {category = category, type = type, name = name}); -end -function api:add_extension(data) - self:add_item("extension", data); -end - -function api:fire_event(...) - return (hosts[self.host] or prosody).events.fire_event(...); -end - -function api:hook(event, handler, priority) - hooks:set(self.host, self.name, event, handler, true); - (hosts[self.host] or prosody).events.add_handler(event, handler, priority); -end - -function api:hook_global(event, handler, priority) - hooks:set("*", self.name, event, handler, true); - prosody.events.add_handler(event, handler, priority); -end - -function api:hook_stanza(xmlns, name, handler, priority) - if not handler and type(name) == "function" then - -- If only 2 options then they specified no xmlns - xmlns, name, handler, priority = nil, xmlns, name, handler; - elseif not (handler and name) then - self:log("warn", "Error: Insufficient parameters to module:hook_stanza()"); - return; - end - return api.hook(self, "stanza/"..(xmlns and (xmlns..":") or "")..name, function (data) return handler(data.origin, data.stanza, data); end, priority); -end - -function api:require(lib) - local f, n = pluginloader.load_code(self.name, lib..".lib.lua"); - if not f then - f, n = pluginloader.load_code(lib, lib..".lib.lua"); - end - if not f then error("Failed to load plugin library '"..lib.."', error: "..n); end -- FIXME better error message - setfenv(f, self.environment); - return f(); -end - -function api:get_option(name, default_value) - local value = config.get(self.host, self.name, name); - if value == nil then - value = config.get(self.host, "core", name); - if value == nil then - value = default_value; - end - end - return value; -end - -function api:get_option_string(name, default_value) - local value = self:get_option(name, default_value); - if type(value) == "table" then - if #value > 1 then - self:log("error", "Config option '%s' does not take a list, using just the first item", name); - end - value = value[1]; - end - if value == nil then - return nil; - end - return tostring(value); -end - -function api:get_option_number(name, ...) - local value = self:get_option(name, ...); - if type(value) == "table" then - if #value > 1 then - self:log("error", "Config option '%s' does not take a list, using just the first item", name); - end - value = value[1]; - end - local ret = tonumber(value); - if value ~= nil and ret == nil then - self:log("error", "Config option '%s' not understood, expecting a number", name); - end - return ret; -end - -function api:get_option_boolean(name, ...) - local value = self:get_option(name, ...); - if type(value) == "table" then - if #value > 1 then - self:log("error", "Config option '%s' does not take a list, using just the first item", name); - end - value = value[1]; - end - if value == nil then - return nil; - end - local ret = value == true or value == "true" or value == 1 or nil; - if ret == nil then - ret = (value == false or value == "false" or value == 0); - if ret then - ret = false; - else - ret = nil; - end - end - if ret == nil then - self:log("error", "Config option '%s' not understood, expecting true/false", name); - end - return ret; -end - -function api:get_option_array(name, ...) - local value = self:get_option(name, ...); - - if value == nil then - return nil; - end - - if type(value) ~= "table" then - return array{ value }; -- Assume any non-list is a single-item list - end - - return array():append(value); -- Clone -end - -function api:get_option_set(name, ...) - local value = self:get_option_array(name, ...); - - if value == nil then - return nil; - end - - return set.new(value); -end - -local t_remove = _G.table.remove; -local module_items = multitable_new(); -function api:add_item(key, value) - self.items = self.items or {}; - self.items[key] = self.items[key] or {}; - t_insert(self.items[key], value); - self:fire_event("item-added/"..key, {source = self, item = value}); -end -function api:remove_item(key, value) - local t = self.items and self.items[key] or NULL; - for i = #t,1,-1 do - if t[i] == value then - t_remove(self.items[key], i); - self:fire_event("item-removed/"..key, {source = self, item = value}); - return value; - end - end -end - -function api:get_host_items(key) - local result = {}; - for mod_name, module in pairs(modulemap[self.host]) do - module = module.module; - if module.items then - for _, item in ipairs(module.items[key] or NULL) do - t_insert(result, item); - end - end - end - for mod_name, module in pairs(modulemap["*"]) do - module = module.module; - if module.items then - for _, item in ipairs(module.items[key] or NULL) do - t_insert(result, item); - end - end - end - return result; -end - -function api:handle_items(type, added_cb, removed_cb, existing) - self:hook("item-added/"..type, added_cb); - self:hook("item-removed/"..type, removed_cb); - if existing ~= false then - for _, item in ipairs(self:get_host_items(type)) do - added_cb({ item = item }); - end - end -end - return _M; -- cgit v1.2.3 From 184f681d3f1d8eef901e16f8d5bdc0c5a6c11edf Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Jan 2012 18:46:17 +0000 Subject: modulemanager: Some reorganisation. Only external change is (should be) that module-unloaded and module-loaded are no longer fired when reloading a module, the new event module-reloaded is fired instead. --- core/modulemanager.lua | 156 +++++++++++++++++++++++++++++-------------------- 1 file changed, 94 insertions(+), 62 deletions(-) (limited to 'core') diff --git a/core/modulemanager.lua b/core/modulemanager.lua index bbe24e32..1846e0f0 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -90,7 +90,45 @@ end prosody_events.add_handler("host-activated", load_modules_for_host); -- -function load(host, module_name, config) +--- Private helpers --- + +local function do_unload_module(host, name) + local mod = get_module(host, name); + if not mod then return nil, "module-not-loaded"; end + + if module_has_method(mod, "unload") then + local ok, err = call_module_method(mod, "unload"); + if (not ok) and err then + log("warn", "Non-fatal error unloading module '%s' on '%s': %s", name, host, err); + end + end + -- unhook event handlers hooked by module:hook + for event, handlers in pairs(hooks:get(host, name) or NULL) do + for handler in pairs(handlers or NULL) do + (hosts[host] or prosody).events.remove_handler(event, handler); + end + end + -- unhook event handlers hooked by module:hook_global + for event, handlers in pairs(hooks:get("*", name) or NULL) do + for handler in pairs(handlers or NULL) do + prosody.events.remove_handler(event, handler); + end + end + hooks:remove(host, name); + if mod.module.items then -- remove items + for key,t in pairs(mod.module.items) do + for i = #t,1,-1 do + local value = t[i]; + t[i] = nil; + hosts[host].events.fire_event("item-removed/"..key, {source = mod.module, item = value}); + end + end + end + modulemap[host][name] = nil; + return true; +end + +local function do_load_module(host, module_name) if not (host and module_name) then return nil, "insufficient-parameters"; elseif not hosts[host] then @@ -116,7 +154,9 @@ function load(host, module_name, config) end local _log = logger.init(host..":"..module_name); - local api_instance = setmetatable({ name = module_name, host = host, path = err, _log = _log, log = function (self, ...) return _log(...); end }, { __index = api }); + local api_instance = setmetatable({ name = module_name, host = host, path = err, + _log = _log, log = function (self, ...) return _log(...); end } + , { __index = api }); local pluginenv = setmetatable({ module = api_instance }, { __index = _G }); api_instance.environment = pluginenv; @@ -125,11 +165,12 @@ function load(host, module_name, config) hosts[host].modules = modulemap[host]; modulemap[host][module_name] = pluginenv; - local success, err = pcall(mod); - if success then + local ok, err = pcall(mod); + if ok then + -- Call module's "load" if module_has_method(pluginenv, "load") then - success, err = call_module_method(pluginenv, "load"); - if not success then + ok, err = call_module_method(pluginenv, "load"); + if not ok then log("warn", "Error loading module '%s' on '%s': %s", module_name, host, err or "nil"); end end @@ -142,62 +183,12 @@ function load(host, module_name, config) end else log("error", "Error initializing module '%s' on '%s': %s", module_name, host, err or "nil"); + do_unload_module(api_instance.host, module_name); -- Ignore error, module may be partially-loaded end - if success then - (hosts[api_instance.host] or prosody).events.fire_event("module-loaded", { module = module_name, host = host }); - return true; - else -- load failed, unloading - unload(api_instance.host, module_name); - return nil, err; - end -end - -function get_module(host, name) - return modulemap[host] and modulemap[host][name]; -end - -function is_loaded(host, name) - return modulemap[host] and modulemap[host][name] and true; + return ok and mod, err; end -function unload(host, name, ...) - local mod = get_module(host, name); - if not mod then return nil, "module-not-loaded"; end - - if module_has_method(mod, "unload") then - local ok, err = call_module_method(mod, "unload"); - if (not ok) and err then - log("warn", "Non-fatal error unloading module '%s' on '%s': %s", name, host, err); - end - end - -- unhook event handlers hooked by module:hook - for event, handlers in pairs(hooks:get(host, name) or NULL) do - for handler in pairs(handlers or NULL) do - (hosts[host] or prosody).events.remove_handler(event, handler); - end - end - -- unhook event handlers hooked by module:hook_global - for event, handlers in pairs(hooks:get("*", name) or NULL) do - for handler in pairs(handlers or NULL) do - prosody.events.remove_handler(event, handler); - end - end - hooks:remove(host, name); - if mod.module.items then -- remove items - for key,t in pairs(mod.module.items) do - for i = #t,1,-1 do - local value = t[i]; - t[i] = nil; - hosts[host].events.fire_event("item-removed/"..key, {source = mod.module, item = value}); - end - end - end - modulemap[host][name] = nil; - (hosts[host] or prosody).events.fire_event("module-unloaded", { module = name, host = host }); - return true; -end - -function reload(host, name, ...) +local function do_reload_module(host, name) local mod = get_module(host, name); if not mod then return nil, "module-not-loaded"; end @@ -224,8 +215,8 @@ function reload(host, name, ...) end end - unload(host, name, ...); - local ok, err = load(host, name, ...); + do_unload_module(host, name); + local ok, err = do_load_module(host, name); if ok then mod = get_module(host, name); if module_has_method(mod, "restore") then @@ -234,11 +225,52 @@ function reload(host, name, ...) log("warn", "Error restoring module '%s' from '%s': %s", name, host, err); end end - return true; + end + return ok and mod, err; +end + +--- Public API --- + +-- Load a module and fire module-loaded event +function load(host, name) + local mod, err = do_load_module(host, name); + if mod then + (hosts[mod.host] or prosody).events.fire_event("module-loaded", { module = module_name, host = host }); + end + return mod, err; +end + +-- Unload a module and fire module-unloaded +function unload(host, name) + local ok, err = do_unload_module(host, name); + if ok then + (hosts[host] or prosody).events.fire_event("module-unloaded", { module = name, host = host }); + end + return ok, err; +end + +function reload(host, name) + local ok, err = do_reload_module(host, name); + if ok then + (hosts[host] or prosody).events.fire_event("module-reloaded", { module = name, host = host }); + elseif not is_loaded(host, name) then + (hosts[host] or prosody).events.fire_event("module-unloaded", { module = name, host = host }); end return ok, err; end +function get_module(host, name) + return modulemap[host] and modulemap[host][name]; +end + +function get_modules(host) + return modulemap[host]; +end + +function is_loaded(host, name) + return modulemap[host] and modulemap[host][name] and true; +end + function module_has_method(module, method) return type(module.module[method]) == "function"; end -- cgit v1.2.3 From 6924d7bfb5c9bcd125a5b872bd9fb7fc6d1b5322 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Jan 2012 18:47:33 +0000 Subject: modulemanager: Drop unnecessary prosody_events local --- core/modulemanager.lua | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'core') diff --git a/core/modulemanager.lua b/core/modulemanager.lua index 1846e0f0..9f90e34e 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -15,7 +15,6 @@ local pluginloader = require "util.pluginloader"; local hosts = hosts; local prosody = prosody; -local prosody_events = prosody.events; local loadfile, pcall, xpcall = loadfile, pcall, xpcall; local setmetatable, setfenv, getfenv = setmetatable, setfenv, getfenv; @@ -87,8 +86,7 @@ function load_modules_for_host(host) load(host, module); end end -prosody_events.add_handler("host-activated", load_modules_for_host); --- +prosody.events.add_handler("host-activated", load_modules_for_host); --- Private helpers --- -- cgit v1.2.3 From 15a073672027cdf47822ad280e00185970ba0bdd Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Jan 2012 18:49:11 +0000 Subject: modulemanager, moduleapi: Replace hooks multitable with an event_handlers map stored in individual modules. Also adds module:hook_object_event() to hook events on any util.events compatible object. --- core/moduleapi.lua | 11 +++++++---- core/modulemanager.lua | 22 +++++----------------- 2 files changed, 12 insertions(+), 21 deletions(-) (limited to 'core') diff --git a/core/moduleapi.lua b/core/moduleapi.lua index ee960709..3a28ec62 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -66,14 +66,17 @@ function api:fire_event(...) return (hosts[self.host] or prosody).events.fire_event(...); end +function api:hook_object_event(object, event, handler, priority) + self.event_handlers[handler] = { name = event, priority = priority, object = object }; + return object.add_handler(event, handler, priority); +end + function api:hook(event, handler, priority) - hooks:set(self.host, self.name, event, handler, true); - (hosts[self.host] or prosody).events.add_handler(event, handler, priority); + return self:hook_object_event((hosts[self.host] or prosody).events, event, handler, priority); end function api:hook_global(event, handler, priority) - hooks:set("*", self.name, event, handler, true); - prosody.events.add_handler(event, handler, priority); + return self:hook_object_event(prosody.events, event, handler, priority); end function api:hook_stanza(xmlns, name, handler, priority) diff --git a/core/modulemanager.lua b/core/modulemanager.lua index 9f90e34e..f86298c9 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -48,10 +48,6 @@ local api = _G.require "core.moduleapi"; -- Module API container local modulemap = { ["*"] = {} }; -local modulehelpers = setmetatable({}, { __index = _G }); - -local hooks = multitable_new(); - local NULL = {}; -- Load modules when a host is activated @@ -100,19 +96,11 @@ local function do_unload_module(host, name) log("warn", "Non-fatal error unloading module '%s' on '%s': %s", name, host, err); end end - -- unhook event handlers hooked by module:hook - for event, handlers in pairs(hooks:get(host, name) or NULL) do - for handler in pairs(handlers or NULL) do - (hosts[host] or prosody).events.remove_handler(event, handler); - end - end - -- unhook event handlers hooked by module:hook_global - for event, handlers in pairs(hooks:get("*", name) or NULL) do - for handler in pairs(handlers or NULL) do - prosody.events.remove_handler(event, handler); - end + + for handler, event in pairs(mod.module.event_handlers) do + event.object.remove_handler(event.name, handler); end - hooks:remove(host, name); + if mod.module.items then -- remove items for key,t in pairs(mod.module.items) do for i = #t,1,-1 do @@ -153,7 +141,7 @@ local function do_load_module(host, module_name) local _log = logger.init(host..":"..module_name); local api_instance = setmetatable({ name = module_name, host = host, path = err, - _log = _log, log = function (self, ...) return _log(...); end } + _log = _log, log = function (self, ...) return _log(...); end, event_handlers = {} } , { __index = api }); local pluginenv = setmetatable({ module = api_instance }, { __index = _G }); -- cgit v1.2.3 From c240995e515d44142f8c2808085198536bdcb15b Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Jan 2012 18:49:49 +0000 Subject: modulemanager: Cleanup some unused variables, imports, whitespace and add a comment. --- core/modulemanager.lua | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'core') diff --git a/core/modulemanager.lua b/core/modulemanager.lua index f86298c9..a6d1ad35 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -9,8 +9,6 @@ local logger = require "util.logger"; local log = logger.init("modulemanager"); local config = require "core.configmanager"; -local multitable_new = require "util.multitable".new; -local st = require "util.stanza"; local pluginloader = require "util.pluginloader"; local hosts = hosts; @@ -46,6 +44,7 @@ module "modulemanager" local api = _G.require "core.moduleapi"; -- Module API container +-- [host] = { [module] = module_env } local modulemap = { ["*"] = {} }; local NULL = {}; @@ -185,7 +184,6 @@ local function do_reload_module(host, name) end local saved; - if module_has_method(mod, "save") then local ok, ret, err = call_module_method(mod, "save"); if ok then -- cgit v1.2.3 From 11bf5edc1d5395822ab2dfe1edc84af85bc6dc57 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Jan 2012 19:27:06 +0000 Subject: modulemanager: load(): Return and use the correct module object --- core/modulemanager.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'core') diff --git a/core/modulemanager.lua b/core/modulemanager.lua index a6d1ad35..a192e637 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -170,7 +170,7 @@ local function do_load_module(host, module_name) log("error", "Error initializing module '%s' on '%s': %s", module_name, host, err or "nil"); do_unload_module(api_instance.host, module_name); -- Ignore error, module may be partially-loaded end - return ok and mod, err; + return ok and pluginenv, err; end local function do_reload_module(host, name) @@ -219,7 +219,7 @@ end function load(host, name) local mod, err = do_load_module(host, name); if mod then - (hosts[mod.host] or prosody).events.fire_event("module-loaded", { module = module_name, host = host }); + (hosts[mod.module.host] or prosody).events.fire_event("module-loaded", { module = name, host = host }); end return mod, err; end -- cgit v1.2.3 From 2f397255c26a57c36102ece3883c3765870c0bf9 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Jan 2012 19:35:50 +0000 Subject: moduleapi: Add module:depends(), a way to safely depend upon another module at runtime --- core/moduleapi.lua | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'core') diff --git a/core/moduleapi.lua b/core/moduleapi.lua index 3a28ec62..44ae9a07 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -100,6 +100,35 @@ function api:require(lib) return f(); end +function api:depends(name) + if not self.dependencies then + self.dependencies = {}; + self:hook("module-reloaded", function (event) + if self.dependencies[event.module] then + self:log("info", "Auto-reloading due to reload of %s:%s", event.host, event.module); + modulemanager.reload(self.host, self.name); + return; + end + end); + self:hook("module-unloaded", function (event) + if self.dependencies[event.module] then + self:log("info", "Auto-unloading due to unload of %s:%s", event.host, event.module); + modulemanager.unload(self.host, self.name); + end + end); + end + local mod = modulemanager.get_module(self.host, name) or modulemanager.get_module("*", name); + if not mod then + local err; + mod, err = modulemanager.load(self.host, name); + if not mod then + return error(("Unable to load required module, mod_%s: %s"):format(name, ((err or "unknown error"):gsub("%-", " ")) )); + end + end + self.dependencies[name] = true; + return mod; +end + function api:get_option(name, default_value) local value = config.get(self.host, self.name, name); if value == nil then -- cgit v1.2.3 From 7c5700ac5402bf47a6c55d55a7f1fa1a8b8cd0d0 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Jan 2012 19:48:53 +0000 Subject: moduleapi: Add module:shared(), a way to easily share data between multiple loaded modules --- core/moduleapi.lua | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'core') diff --git a/core/moduleapi.lua b/core/moduleapi.lua index 44ae9a07..7a4d1fa6 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -129,6 +129,29 @@ function api:depends(name) return mod; end +-- Returns one or more shared tables at the specified virtual paths +-- Intentionally does not allow the table at a path to be _set_, it +-- is auto-created if it does not exist. +function api:shared(...) + local paths = { n = select("#", ...), ... }; + local data_array = {}; + local default_path_components = { self.host, self.name }; + for i = 1, paths.n do + local path = paths[i]; + if path:sub(1,1) ~= "/" then -- Prepend default components + local n_components = select(2, path:gsub("/", "%1")); + path = (n_components<#default_path_components and "/" or "")..t_concat(default_path_components, "/", 1, #default_path_components-n_components).."/"..path; + end + local shared = shared_data[path]; + if not shared then + shared = {}; + shared_data[path] = shared; + end + t_insert(data_array, shared); + end + return unpack(data_array); +end + function api:get_option(name, default_value) local value = config.get(self.host, self.name, name); if value == nil then -- cgit v1.2.3 From 29f37bbccc6d928adc4cf118aebf250974918eab Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Jan 2012 23:55:48 +0000 Subject: portmanager: One manager to, in the darkness, bind them --- core/portmanager.lua | 132 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 core/portmanager.lua (limited to 'core') diff --git a/core/portmanager.lua b/core/portmanager.lua new file mode 100644 index 00000000..a460cd6a --- /dev/null +++ b/core/portmanager.lua @@ -0,0 +1,132 @@ + +local multitable = require "util.multitable"; +local fire_event = prosody.events.fire_event; + +--- Config + +local default_interfaces = { "*" }; +local default_local_interfaces = { "127.0.0.1" }; +if config.get("*", "use_ipv6") then + table.insert(default_interfaces, "::"); + table.insert(default_local_interfaces, "::1"); +end + +--- Private state + +-- service_name -> service_info +local services = {}; + +-- service_name, interface (string), port (number) +local active_services = multitable.new(); + +--- Private helpers + +local function error_to_friendly_message(service_name, err) + local friendly_message = err; + if err:match(" in use") then + -- FIXME: Use service_name here + if port == 5222 or port == 5223 or port == 5269 then + friendly_message = "check that Prosody or another XMPP server is " + .."not already running and using this port"; + elseif port == 80 or port == 81 then + friendly_message = "check that a HTTP server is not already using " + .."this port"; + elseif port == 5280 then + friendly_message = "check that Prosody or a BOSH connection manager " + .."is not already running"; + else + friendly_message = "this port is in use by another application"; + end + elseif err:match("permission") then + friendly_message = "Prosody does not have sufficient privileges to use this port"; + elseif err == "no ssl context" then + if not config.get("*", "core", "ssl") then + friendly_message = "there is no 'ssl' config under Host \"*\" which is " + .."require for legacy SSL ports"; + else + friendly_message = "initializing SSL support failed, see previous log entries"; + end + end + return friendly_message; +end + +module("portmanager", package.seeall); + +--- Public API + +function activate_service(service_name) + local service_info = services[service_name]; + if not service_info then + return nil, "Unknown service: "..service_name; + end + + local bind_interfaces = set.new(config.get("*", service_name.."_interfaces") + or config.get("*", service_name.."_interface") -- COMPAT w/pre-0.9 + or config.get("*", "interfaces") + or config.get("*", "interface") -- COMPAT w/pre-0.9 + or (service_info.private and default_local_interfaces) + or service_info.default_interface -- COMPAT w/pre0.9 + or default_interfaces); + + local bind_ports = set.new(config.get("*", service_name.."_ports") + or (service_info.multiplex and config.get("*", "ports")) + or service_info.default_ports + or {service_info.default_port}); + + local listener = service_info.listener; + local mode = listener.default_mode or "*a"; + local ssl; + if service_info.encryption == "ssl" then + ssl = prosody.global_ssl_ctx; + if not ssl then + return nil, "global-ssl-context-required"; + end + end + + for interface in bind_interfaces do + for port in bind_ports do + if not service_info.multiplex and #active_services:search(nil, interface, port) > 0 then + log("error", "Multiple services configured to listen on the same port: %s, %s", table.concat(active_services:search(nil, interface, port), ", "), service_name); + else + local handler, err = server.addserver(interface, port, listener, mode, ssl); + if not handler then + log("error", "Failed to open server port %d on %s, %s", port, interface, error_to_friendly_message(service_name, err)); + else + log("debug", "Added listening service %s to [%s]:%d", service_name, interface, port); + active_services:add(service_name, interface, port, { + server = handler; + service = service_info; + }); + end + end + end + end + log("info", "Activated service '%s'", service_name); +end + +function deactivate(service_name) + local active = active_services:search(service_name)[1]; + if not active then return; end + for interface, ports in pairs(active) do + for port, active_service in pairs(ports) do + active_service:close(); + active_services:remove(service_name, interface, port, active_service); + log("debug", "Removed listening service %s from [%s]:%d", service_name, interface, port); + end + end + log("info", "Deactivated service '%s'", service_name); +end + +function register_service(service_name, service_info) + services[service_name] = service_info; + + if not active_services[service_name] then + activate_service(service_name); + end + + fire_event("service-added", { name = service_name, service = service_info }); + return true; +end + + +return _M; -- cgit v1.2.3 From 13b36f8ded00794f3142887c41ac8b31efb01e4b Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Jan 2012 23:57:13 +0000 Subject: mod_c2s, sessionmanager, xmppclient_listener: Move all c2s network and stream logic into a new module, mod_c2s --- core/sessionmanager.lua | 49 ------------------------------------------------- 1 file changed, 49 deletions(-) (limited to 'core') diff --git a/core/sessionmanager.lua b/core/sessionmanager.lua index b1ec819f..17c718dc 100644 --- a/core/sessionmanager.lua +++ b/core/sessionmanager.lua @@ -6,11 +6,8 @@ -- COPYING file in the source package for more information. -- - - local tonumber, tostring, setmetatable = tonumber, tostring, setmetatable; local ipairs, pairs, print, next= ipairs, pairs, print, next; -local format = string.format; local hosts = hosts; local full_sessions = full_sessions; @@ -19,10 +16,8 @@ local bare_sessions = bare_sessions; local logger = require "util.logger"; local log = logger.init("sessionmanager"); local error = error; -local uuid_generate = require "util.uuid".generate; local rm_load_roster = require "core.rostermanager".load_roster; local config_get = require "core.configmanager".get; -local nameprep = require "util.encodings".stringprep.nameprep; local resourceprep = require "util.encodings".stringprep.resourceprep; local nodeprep = require "util.encodings".stringprep.nodeprep; @@ -216,50 +211,6 @@ function bind_resource(session, resource) return true; end -function streamopened(session, attr) - local send = session.send; - session.host = attr.to; - if not session.host then - session:close{ condition = "improper-addressing", - text = "A 'to' attribute is required on stream headers" }; - return; - end - session.host = nameprep(session.host); - session.version = tonumber(attr.version) or 0; - session.streamid = uuid_generate(); - (session.log or session)("debug", "Client sent opening to %s", session.host); - - if not hosts[session.host] then - -- We don't serve this host... - session:close{ condition = "host-unknown", text = "This server does not serve "..tostring(session.host)}; - return; - end - - send(""); - send(format("", session.streamid, session.host)); - - (session.log or log)("debug", "Sent reply to client"); - session.notopen = nil; - - -- If session.secure is *false* (not nil) then it means we /were/ encrypting - -- since we now have a new stream header, session is secured - if session.secure == false then - session.secure = true; - end - - local features = st.stanza("stream:features"); - hosts[session.host].events.fire_event("stream-features", { origin = session, features = features }); - fire_event("stream-features", session, features); - - send(features); - -end - -function streamclosed(session) - session.log("debug", "Received "); - session:close(); -end - function send_to_available_resources(user, host, stanza) local jid = user.."@"..host; local count = 0; -- cgit v1.2.3 From 1374dd01213e64739c9f555ba3d46bb875015fb3 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Jan 2012 00:07:15 +0000 Subject: portmanager: Pass port to friendly_error_message() --- core/portmanager.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'core') diff --git a/core/portmanager.lua b/core/portmanager.lua index a460cd6a..353c4a89 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -21,7 +21,7 @@ local active_services = multitable.new(); --- Private helpers -local function error_to_friendly_message(service_name, err) +local function error_to_friendly_message(service_name, port, err) local friendly_message = err; if err:match(" in use") then -- FIXME: Use service_name here @@ -90,7 +90,7 @@ function activate_service(service_name) else local handler, err = server.addserver(interface, port, listener, mode, ssl); if not handler then - log("error", "Failed to open server port %d on %s, %s", port, interface, error_to_friendly_message(service_name, err)); + log("error", "Failed to open server port %d on %s, %s", port, interface, error_to_friendly_message(service_name, port, err)); else log("debug", "Added listening service %s to [%s]:%d", service_name, interface, port); active_services:add(service_name, interface, port, { -- cgit v1.2.3 From b14f0af41d94b5ee137d48e269ad29b45ee765d9 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Jan 2012 00:56:57 +0000 Subject: sessionmanager, mod_c2s: Move timeout logic to mod_c2s --- core/sessionmanager.lua | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'core') diff --git a/core/sessionmanager.lua b/core/sessionmanager.lua index 17c718dc..276cd8f5 100644 --- a/core/sessionmanager.lua +++ b/core/sessionmanager.lua @@ -28,8 +28,6 @@ local gettime = require "socket".gettime; local st = require "util.stanza"; -local c2s_timeout = config_get("*", "core", "c2s_timeout"); - local newproxy = newproxy; local getmetatable = getmetatable; @@ -62,14 +60,6 @@ function new_session(conn) session.ip = conn:ip(); local conn_name = "c2s"..tostring(conn):match("[a-f0-9]+$"); session.log = logger.init(conn_name); - - if c2s_timeout then - add_task(c2s_timeout, function () - if session.type == "c2s_unauthed" then - session:close("connection-timeout"); - end - end); - end return session; end -- cgit v1.2.3 From b1b28066ba73cf808af9f2b895ece7428c61a4b5 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Jan 2012 02:23:23 +0000 Subject: sessionmanager: Require uuid_generate() --- core/sessionmanager.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'core') diff --git a/core/sessionmanager.lua b/core/sessionmanager.lua index 276cd8f5..c101bf4e 100644 --- a/core/sessionmanager.lua +++ b/core/sessionmanager.lua @@ -20,6 +20,7 @@ local rm_load_roster = require "core.rostermanager".load_roster; local config_get = require "core.configmanager".get; local resourceprep = require "util.encodings".stringprep.resourceprep; local nodeprep = require "util.encodings".stringprep.nodeprep; +local uuid_generate = require "util.uuid".generate; local initialize_filters = require "util.filters".initialize; local fire_event = prosody.events.fire_event; -- cgit v1.2.3 From 3e2d3f18754e6b367ff69b46f138af9770c92b62 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Jan 2012 16:24:06 +0000 Subject: stanza_router: Remove obsolete commented code --- core/stanza_router.lua | 5 ----- 1 file changed, 5 deletions(-) (limited to 'core') diff --git a/core/stanza_router.lua b/core/stanza_router.lua index 406ad2f0..4a30279d 100644 --- a/core/stanza_router.lua +++ b/core/stanza_router.lua @@ -105,11 +105,6 @@ function core_process_stanza(origin, stanza) stanza.attr.from = from; end - --[[if to and not(hosts[to]) and not(hosts[to_bare]) and (hosts[host] and hosts[host].type ~= "local") then -- not for us? - log("warn", "stanza recieved for a non-local server"); - return; -- FIXME what should we do here? - end]] -- FIXME - if (origin.type == "s2sin" or origin.type == "c2s" or origin.type == "component") and xmlns == nil then if origin.type == "s2sin" and not origin.dummy then local host_status = origin.hosts[from_host]; -- cgit v1.2.3 From 3c5d5f95aded549e619dca86b7ff5689413646f9 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Jan 2012 16:25:21 +0000 Subject: stanza_router: Replace s2s send logic with firing of a 'route/remote' event --- core/stanza_router.lua | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) (limited to 'core') diff --git a/core/stanza_router.lua b/core/stanza_router.lua index 4a30279d..54c5a1a6 100644 --- a/core/stanza_router.lua +++ b/core/stanza_router.lua @@ -11,7 +11,6 @@ local log = require "util.logger".init("stanzarouter") local hosts = _G.prosody.hosts; local tostring = tostring; local st = require "util.stanza"; -local send_s2s = require "core.s2smanager".send_to_host; local jid_split = require "util.jid".split; local jid_prepped_split = require "util.jid".prepped_split; @@ -184,26 +183,18 @@ function core_route_stanza(origin, stanza) if hosts[host] then -- old stanza routing code removed core_post_stanza(origin, stanza); - elseif origin.type == "c2s" then - -- Remote host + else + log("debug", "Routing to remote..."); if not hosts[from_host] then log("error", "No hosts[from_host] (please report): %s", tostring(stanza)); - end - if (not hosts[from_host]) or (not hosts[from_host].disallow_s2s) then + else local xmlns = stanza.attr.xmlns; - --stanza.attr.xmlns = "jabber:server"; stanza.attr.xmlns = nil; - log("debug", "sending s2s stanza: %s", tostring(stanza.top_tag and stanza:top_tag()) or stanza); - send_s2s(origin.host, host, stanza); -- TODO handle remote routing errors + local routed = prosody.events.fire_event("route/remote", { origin = origin, stanza = stanza, from_host = from_host, to_host = host }); --FIXME: Should be per-host (shared modules!) stanza.attr.xmlns = xmlns; -- reset - else - core_route_stanza(hosts[from_host], st.error_reply(stanza, "cancel", "not-allowed", "Communication with remote servers is not allowed")); + if routed == nil then + core_route_stanza(hosts[from_host], st.error_reply(stanza, "cancel", "not-allowed", "Communication with remote domains is not enabled")); + end end - elseif origin.type == "component" or origin.type == "local" then - -- Route via s2s for components and modules - log("debug", "Routing outgoing stanza for %s to %s", from_host, host); - send_s2s(from_host, host, stanza); - else - log("warn", "received %s stanza from unhandled connection type: %s", tostring(stanza.name), tostring(origin.type)); end end -- cgit v1.2.3 From f851289311ce15196d3dc7ae0d912b17586901b9 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Jan 2012 16:28:20 +0000 Subject: s2smanager, mod_s2s, mod_s2s/s2sout: Split connection handling out of s2smanager into mod_s2s, and further split connection logic for s2sout to a module lib, s2sout.lib.lua --- core/s2smanager.lua | 581 +--------------------------------------------------- 1 file changed, 11 insertions(+), 570 deletions(-) (limited to 'core') diff --git a/core/s2smanager.lua b/core/s2smanager.lua index f44921c3..e61aaccb 100644 --- a/core/s2smanager.lua +++ b/core/s2smanager.lua @@ -9,18 +9,13 @@ local hosts = hosts; -local sessions = sessions; local core_process_stanza = function(a, b) core_process_stanza(a, b); end -local add_task = require "util.timer".add_task; -local socket = require "socket"; local format = string.format; local t_insert, t_sort = table.insert, table.sort; local get_traceback = debug.traceback; local tostring, pairs, ipairs, getmetatable, newproxy, type, error, tonumber, setmetatable = tostring, pairs, ipairs, getmetatable, newproxy, type, error, tonumber, setmetatable; -local idna_to_ascii = require "util.encodings".idna.to_ascii; -local connlisteners_get = require "net.connlisteners".get; local initialize_filters = require "util.filters".initialize; local wrapclient = require "net.server".wrapclient; local st = require "stanza"; @@ -41,11 +36,13 @@ local sha256_hash = require "util.hashes".sha256; local adns, dns = require "net.adns", require "net.dns"; local config = require "core.configmanager"; -local connect_timeout = config.get("*", "core", "s2s_timeout") or 60; local dns_timeout = config.get("*", "core", "dns_timeout") or 15; local max_dns_depth = config.get("*", "core", "dns_max_depth") or 3; +local cfg_sources = config.get("*", "core", "s2s_interface") + or config.get("*", "core", "interface"); local sources; +--FIXME: s2sout should create its own resolver w/ timeout dns.settimeout(dns_timeout); local prosody = _G.prosody; @@ -55,89 +52,6 @@ local incoming_s2s = incoming_s2s; module "s2smanager" -function compare_srv_priorities(a,b) - return a.priority < b.priority or (a.priority == b.priority and a.weight > b.weight); -end - -local bouncy_stanzas = { message = true, presence = true, iq = true }; -local function bounce_sendq(session, reason) - local sendq = session.sendq; - if sendq then - session.log("info", "sending error replies for "..#sendq.." queued stanzas because of failed outgoing connection to "..tostring(session.to_host)); - local dummy = { - type = "s2sin"; - send = function(s) - (session.log or log)("error", "Replying to to an s2s error reply, please report this! Traceback: %s", get_traceback()); - end; - dummy = true; - }; - for i, data in ipairs(sendq) do - local reply = data[2]; - if reply and not(reply.attr.xmlns) and bouncy_stanzas[reply.name] then - reply.attr.type = "error"; - reply:tag("error", {type = "cancel"}) - :tag("remote-server-not-found", {xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas"}):up(); - if reason then - reply:tag("text", {xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas"}) - :text("Server-to-server connection failed: "..reason):up(); - end - core_process_stanza(dummy, reply); - end - sendq[i] = nil; - end - session.sendq = nil; - end -end - -function send_to_host(from_host, to_host, data) - if not hosts[from_host] then - log("warn", "Attempt to send stanza from %s - a host we don't serve", from_host); - return false; - end - local host = hosts[from_host].s2sout[to_host]; - if host then - -- We have a connection to this host already - if host.type == "s2sout_unauthed" and (data.name ~= "db:verify" or not host.dialback_key) then - (host.log or log)("debug", "trying to send over unauthed s2sout to "..to_host); - - -- Queue stanza until we are able to send it - if host.sendq then t_insert(host.sendq, {tostring(data), data.attr.type ~= "error" and data.attr.type ~= "result" and st.reply(data)}); - else host.sendq = { {tostring(data), data.attr.type ~= "error" and data.attr.type ~= "result" and st.reply(data)} }; end - host.log("debug", "stanza [%s] queued ", data.name); - elseif host.type == "local" or host.type == "component" then - log("error", "Trying to send a stanza to ourselves??") - log("error", "Traceback: %s", get_traceback()); - log("error", "Stanza: %s", tostring(data)); - return false; - else - (host.log or log)("debug", "going to send stanza to "..to_host.." from "..from_host); - -- FIXME - if host.from_host ~= from_host then - log("error", "WARNING! This might, possibly, be a bug, but it might not..."); - log("error", "We are going to send from %s instead of %s", tostring(host.from_host), tostring(from_host)); - end - host.sends2s(data); - host.log("debug", "stanza sent over "..host.type); - end - else - log("debug", "opening a new outgoing connection for this stanza"); - local host_session = new_outgoing(from_host, to_host); - - -- Store in buffer - host_session.sendq = { {tostring(data), data.attr.type ~= "error" and data.attr.type ~= "result" and st.reply(data)} }; - log("debug", "stanza [%s] queued until connection complete", tostring(data.name)); - if (not host_session.connecting) and (not host_session.conn) then - log("warn", "Connection to %s failed already, destroying session...", to_host); - if not destroy_session(host_session, "Connection failed") then - -- Already destroyed, we need to bounce our stanza - bounce_sendq(host_session, host_session.destruction_reason); - end - return false; - end - end - return true; -end - local open_sessions = 0; function new_incoming(conn) @@ -147,491 +61,18 @@ function new_incoming(conn) getmetatable(session.trace).__gc = function () open_sessions = open_sessions - 1; end; end open_sessions = open_sessions + 1; - local w, log = conn.write, logger_init("s2sin"..tostring(conn):match("[a-f0-9]+$")); - session.log = log; - local filter = initialize_filters(session); - session.sends2s = function (t) - log("debug", "sending: %s", t.top_tag and t:top_tag() or t:match("^([^>]*>?)")); - if t.name then - t = filter("stanzas/out", t); - end - if t then - t = filter("bytes/out", tostring(t)); - if t then - return w(conn, t); - end - end - end + session.log = logger_init("s2sin"..tostring(conn):match("[a-f0-9]+$")); incoming_s2s[session] = true; - add_task(connect_timeout, function () - if session.conn ~= conn or - session.type == "s2sin" then - return; -- Ok, we're connect[ed|ing] - end - -- Not connected, need to close session and clean up - (session.log or log)("debug", "Destroying incomplete session %s->%s due to inactivity", - session.from_host or "(unknown)", session.to_host or "(unknown)"); - session:close("connection-timeout"); - end); return session; end function new_outgoing(from_host, to_host, connect) - local host_session = { to_host = to_host, from_host = from_host, host = from_host, - notopen = true, type = "s2sout_unauthed", direction = "outgoing", - open_stream = session_open_stream }; - - hosts[from_host].s2sout[to_host] = host_session; - - host_session.close = destroy_session; -- This gets replaced by xmppserver_listener later - - local log; - do - local conn_name = "s2sout"..tostring(host_session):match("[a-f0-9]*$"); - log = logger_init(conn_name); - host_session.log = log; - end - - initialize_filters(host_session); - - if connect ~= false then - -- Kick the connection attempting machine into life - if not attempt_connection(host_session) then - -- Intentionally not returning here, the - -- session is needed, connected or not - destroy_session(host_session); - end - end - - if not host_session.sends2s then - -- A sends2s which buffers data (until the stream is opened) - -- note that data in this buffer will be sent before the stream is authed - -- and will not be ack'd in any way, successful or otherwise - local buffer; - function host_session.sends2s(data) - if not buffer then - buffer = {}; - host_session.send_buffer = buffer; - end - log("debug", "Buffering data on unconnected s2sout to %s", to_host); - buffer[#buffer+1] = data; - log("debug", "Buffered item %d: %s", #buffer, tostring(data)); - end - end - - return host_session; -end - - -function attempt_connection(host_session, err) - local from_host, to_host = host_session.from_host, host_session.to_host; - local connect_host, connect_port = to_host and idna_to_ascii(to_host), 5269; - - if not connect_host then - return false; - end - - if not err then -- This is our first attempt - log("debug", "First attempt to connect to %s, starting with SRV lookup...", to_host); - host_session.connecting = true; - local handle; - handle = adns.lookup(function (answer) - handle = nil; - host_session.connecting = nil; - if answer then - log("debug", to_host.." has SRV records, handling..."); - local srv_hosts = {}; - host_session.srv_hosts = srv_hosts; - for _, record in ipairs(answer) do - t_insert(srv_hosts, record.srv); - end - if #srv_hosts == 1 and srv_hosts[1].target == "." then - log("debug", to_host.." does not provide a XMPP service"); - destroy_session(host_session, err); -- Nothing to see here - return; - end - t_sort(srv_hosts, compare_srv_priorities); - - local srv_choice = srv_hosts[1]; - host_session.srv_choice = 1; - if srv_choice then - connect_host, connect_port = srv_choice.target or to_host, srv_choice.port or connect_port; - log("debug", "Best record found, will connect to %s:%d", connect_host, connect_port); - end - else - log("debug", to_host.." has no SRV records, falling back to A"); - end - -- Try with SRV, or just the plain hostname if no SRV - local ok, err = try_connect(host_session, connect_host, connect_port); - if not ok then - if not attempt_connection(host_session, err) then - -- No more attempts will be made - destroy_session(host_session, err); - end - end - end, "_xmpp-server._tcp."..connect_host..".", "SRV"); - - return true; -- Attempt in progress - elseif host_session.ip_hosts then - return try_connect(host_session, connect_host, connect_port, err); - elseif host_session.srv_hosts and #host_session.srv_hosts > host_session.srv_choice then -- Not our first attempt, and we also have SRV - host_session.srv_choice = host_session.srv_choice + 1; - local srv_choice = host_session.srv_hosts[host_session.srv_choice]; - connect_host, connect_port = srv_choice.target or to_host, srv_choice.port or connect_port; - host_session.log("info", "Connection failed (%s). Attempt #%d: This time to %s:%d", tostring(err), host_session.srv_choice, connect_host, connect_port); - else - host_session.log("info", "Out of connection options, can't connect to %s", tostring(host_session.to_host)); - -- We're out of options - return false; - end - - if not (connect_host and connect_port) then - -- Likely we couldn't resolve DNS - log("warn", "Hmm, we're without a host (%s) and port (%s) to connect to for %s, giving up :(", tostring(connect_host), tostring(connect_port), tostring(to_host)); - return false; - end - - return try_connect(host_session, connect_host, connect_port); -end - -function try_next_ip(host_session) - host_session.connecting = nil; - host_session.ip_choice = host_session.ip_choice + 1; - local ip = host_session.ip_hosts[host_session.ip_choice]; - local ok, err= make_connect(host_session, ip.ip, ip.port); - if not ok then - if not attempt_connection(host_session, err or "closed") then - err = err and (": "..err) or ""; - destroy_session(host_session, "Connection failed"..err); - end - end -end - -function try_connect(host_session, connect_host, connect_port, err) - host_session.connecting = true; - - if not err then - local IPs = {}; - host_session.ip_hosts = IPs; - local handle4, handle6; - local has_other = false; - - if not sources then - sources = {}; - local cfg_sources = config.get("*", "core", "interface") or connlisteners_get("xmppserver").default_interface; - if type(cfg_sources) == "string" then - cfg_sources = { cfg_sources }; - end - for i, source in ipairs(cfg_sources) do - if source == "*" then - sources[i] = new_ip("0.0.0.0", "IPv4"); - else - sources[i] = new_ip(source, (source:find(":") and "IPv6") or "IPv4"); - end - end - end - - handle4 = adns.lookup(function (reply, err) - handle4 = nil; - - -- COMPAT: This is a compromise for all you CNAME-(ab)users :) - if not (reply and reply[#reply] and reply[#reply].a) then - local count = max_dns_depth; - reply = dns.peek(connect_host, "CNAME", "IN"); - while count > 0 and reply and reply[#reply] and not reply[#reply].a and reply[#reply].cname do - log("debug", "Looking up %s (DNS depth is %d)", tostring(reply[#reply].cname), count); - reply = dns.peek(reply[#reply].cname, "A", "IN") or dns.peek(reply[#reply].cname, "CNAME", "IN"); - count = count - 1; - end - end - -- end of CNAME resolving - - if reply and reply[#reply] and reply[#reply].a then - for _, ip in ipairs(reply) do - log("debug", "DNS reply for %s gives us %s", connect_host, ip.a); - IPs[#IPs+1] = new_ip(ip.a, "IPv4"); - end - end - - if has_other then - if #IPs > 0 then - rfc3484_dest(host_session.ip_hosts, sources); - for i = 1, #IPs do - IPs[i] = {ip = IPs[i], port = connect_port}; - end - host_session.ip_choice = 0; - try_next_ip(host_session); - else - log("debug", "DNS lookup failed to get a response for %s", connect_host); - host_session.ip_hosts = nil; - if not attempt_connection(host_session, "name resolution failed") then -- Retry if we can - log("debug", "No other records to try for %s - destroying", host_session.to_host); - err = err and (": "..err) or ""; - destroy_session(host_session, "DNS resolution failed"..err); -- End of the line, we can't - end - end - else - has_other = true; - end - end, connect_host, "A", "IN"); - - handle6 = adns.lookup(function (reply, err) - handle6 = nil; - - if reply and reply[#reply] and reply[#reply].aaaa then - for _, ip in ipairs(reply) do - log("debug", "DNS reply for %s gives us %s", connect_host, ip.aaaa); - IPs[#IPs+1] = new_ip(ip.aaaa, "IPv6"); - end - end - - if has_other then - if #IPs > 0 then - rfc3484_dest(host_session.ip_hosts, sources); - for i = 1, #IPs do - IPs[i] = {ip = IPs[i], port = connect_port}; - end - host_session.ip_choice = 0; - try_next_ip(host_session); - else - log("debug", "DNS lookup failed to get a response for %s", connect_host); - host_session.ip_hosts = nil; - if not attempt_connection(host_session, "name resolution failed") then -- Retry if we can - log("debug", "No other records to try for %s - destroying", host_session.to_host); - err = err and (": "..err) or ""; - destroy_session(host_session, "DNS resolution failed"..err); -- End of the line, we can't - end - end - else - has_other = true; - end - end, connect_host, "AAAA", "IN"); - - return true; - elseif host_session.ip_hosts and #host_session.ip_hosts > host_session.ip_choice then -- Not our first attempt, and we also have IPs left to try - try_next_ip(host_session); - else - host_session.ip_hosts = nil; - if not attempt_connection(host_session, "out of IP addresses") then -- Retry if we can - log("debug", "No other records to try for %s - destroying", host_session.to_host); - err = err and (": "..err) or ""; - destroy_session(host_session, "Connecting failed"..err); -- End of the line, we can't - return false; - end - end - - return true; -end - -function make_connect(host_session, connect_host, connect_port) - (host_session.log or log)("info", "Beginning new connection attempt to %s ([%s]:%d)", host_session.to_host, connect_host.addr, connect_port); - -- Ok, we're going to try to connect - - local from_host, to_host = host_session.from_host, host_session.to_host; - - local conn, handler; - if connect_host.proto == "IPv4" then - conn, handler = socket.tcp(); - else - conn, handler = socket.tcp6(); - end - - if not conn then - log("warn", "Failed to create outgoing connection, system error: %s", handler); - return false, handler; - end - - conn:settimeout(0); - local success, err = conn:connect(connect_host.addr, connect_port); - if not success and err ~= "timeout" then - log("warn", "s2s connect() to %s (%s:%d) failed: %s", host_session.to_host, connect_host.addr, connect_port, err); - return false, err; - end - - local cl = connlisteners_get("xmppserver"); - conn = wrapclient(conn, connect_host.addr, connect_port, cl, cl.default_mode or 1 ); - host_session.conn = conn; - - local filter = initialize_filters(host_session); - local w, log = conn.write, host_session.log; - host_session.sends2s = function (t) - log("debug", "sending: %s", (t.top_tag and t:top_tag()) or t:match("^[^>]*>?")); - if t.name then - t = filter("stanzas/out", t); - end - if t then - t = filter("bytes/out", tostring(t)); - if t then - return w(conn, tostring(t)); - end - end - end - - -- Register this outgoing connection so that xmppserver_listener knows about it - -- otherwise it will assume it is a new incoming connection - cl.register_outgoing(conn, host_session); - - host_session:open_stream(from_host, to_host); - - log("debug", "Connection attempt in progress..."); - add_task(connect_timeout, function () - if host_session.conn ~= conn or - host_session.type == "s2sout" or - host_session.connecting then - return; -- Ok, we're connect[ed|ing] - end - -- Not connected, need to close session and clean up - (host_session.log or log)("warn", "Destroying incomplete session %s->%s due to inactivity", - host_session.from_host or "(unknown)", host_session.to_host or "(unknown)"); - host_session:close("connection-timeout"); - end); - return true; -end - -function session_open_stream(session, from, to) - session.sends2s(st.stanza("stream:stream", { - xmlns='jabber:server', ["xmlns:db"]='jabber:server:dialback', - ["xmlns:stream"]='http://etherx.jabber.org/streams', - from=from, to=to, version='1.0', ["xml:lang"]='en'}):top_tag()); -end - -local function check_cert_status(session) - local conn = session.conn:socket() - local cert - if conn.getpeercertificate then - cert = conn:getpeercertificate() - end - - if cert then - local chain_valid, errors = conn:getpeerverification() - -- Is there any interest in printing out all/the number of errors here? - if not chain_valid then - (session.log or log)("debug", "certificate chain validation result: invalid"); - session.cert_chain_status = "invalid"; - else - (session.log or log)("debug", "certificate chain validation result: valid"); - session.cert_chain_status = "valid"; - - local host = session.direction == "incoming" and session.from_host or session.to_host - - -- We'll go ahead and verify the asserted identity if the - -- connecting server specified one. - if host then - if cert_verify_identity(host, "xmpp-server", cert) then - session.cert_identity_status = "valid" - else - session.cert_identity_status = "invalid" - end - end - end - end -end - -function streamopened(session, attr) - local send = session.sends2s; - - -- TODO: #29: SASL/TLS on s2s streams - session.version = tonumber(attr.version) or 0; - - -- TODO: Rename session.secure to session.encrypted - if session.secure == false then - session.secure = true; - end - - if session.direction == "incoming" then - -- Send a reply stream header - session.to_host = attr.to and nameprep(attr.to); - session.from_host = attr.from and nameprep(attr.from); - - session.streamid = uuid_gen(); - (session.log or log)("debug", "Incoming s2s received "); - if session.to_host then - if not hosts[session.to_host] then - -- Attempting to connect to a host we don't serve - session:close({ - condition = "host-unknown"; - text = "This host does not serve "..session.to_host - }); - return; - elseif hosts[session.to_host].disallow_s2s then - -- Attempting to connect to a host that disallows s2s - session:close({ - condition = "policy-violation"; - text = "Server-to-server communication is not allowed to this host"; - }); - return; - end - end - - if session.secure and not session.cert_chain_status then check_cert_status(session); end - - send(""); - send(stanza("stream:stream", { xmlns='jabber:server', ["xmlns:db"]='jabber:server:dialback', - ["xmlns:stream"]='http://etherx.jabber.org/streams', id=session.streamid, from=session.to_host, to=session.from_host, version=(session.version > 0 and "1.0" or nil) }):top_tag()); - if session.version >= 1.0 then - local features = st.stanza("stream:features"); - - if session.to_host then - hosts[session.to_host].events.fire_event("s2s-stream-features", { origin = session, features = features }); - else - (session.log or log)("warn", "No 'to' on stream header from %s means we can't offer any features", session.from_host or "unknown host"); - end - - log("debug", "Sending stream features: %s", tostring(features)); - send(features); - end - elseif session.direction == "outgoing" then - -- If we are just using the connection for verifying dialback keys, we won't try and auth it - if not attr.id then error("stream response did not give us a streamid!!!"); end - session.streamid = attr.id; - - if session.secure and not session.cert_chain_status then check_cert_status(session); end - - -- Send unauthed buffer - -- (stanzas which are fine to send before dialback) - -- Note that this is *not* the stanza queue (which - -- we can only send if auth succeeds) :) - local send_buffer = session.send_buffer; - if send_buffer and #send_buffer > 0 then - log("debug", "Sending s2s send_buffer now..."); - for i, data in ipairs(send_buffer) do - session.sends2s(tostring(data)); - send_buffer[i] = nil; - end - end - session.send_buffer = nil; - - -- If server is pre-1.0, don't wait for features, just do dialback - if session.version < 1.0 then - if not session.dialback_verifying then - log("debug", "Initiating dialback..."); - initiate_dialback(session); - else - mark_connected(session); - end - end - end - session.notopen = nil; -end - -function streamclosed(session) - (session.log or log)("debug", "Received "); - session:close(); -end - -function initiate_dialback(session) - -- generate dialback key - session.dialback_key = generate_dialback(session.streamid, session.to_host, session.from_host); - session.sends2s(format("%s", session.from_host, session.to_host, session.dialback_key)); - session.log("info", "sent dialback key on outgoing s2s stream"); -end - -function generate_dialback(id, to, from) - return sha256_hash(id..to..from..hosts[from].dialback_secret, true); -end - -function verify_dialback(id, to, from, key) - return key == generate_dialback(id, to, from); + local host_session = { to_host = to_host, from_host = from_host, host = from_host, + notopen = true, type = "s2sout_unauthed", direction = "outgoing" }; + hosts[from_host].s2sout[to_host] = host_session; + local conn_name = "s2sout"..tostring(host_session):match("[a-f0-9]*$"); + host_session.log = logger_init(conn_name); + return host_session; end function make_authenticated(session, host) @@ -733,7 +174,7 @@ function destroy_session(session, reason) if session.direction == "outgoing" then hosts[session.from_host].s2sout[session.to_host] = nil; - bounce_sendq(session, reason); + session:bounce_sendq(reason); elseif session.direction == "incoming" then incoming_s2s[session] = nil; end -- cgit v1.2.3 From 4c9c8363bca9bbe87c34f102567cff7a9e539107 Mon Sep 17 00:00:00 2001 From: Florian Zeitz Date: Fri, 24 Feb 2012 15:14:07 +0000 Subject: modulemanager: include mod_c2s and mod_s2s into autoloaded modules. --- core/modulemanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core') diff --git a/core/modulemanager.lua b/core/modulemanager.lua index a192e637..0ca37105 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -34,7 +34,7 @@ end local array, set = require "util.array", require "util.set"; -local autoload_modules = {"presence", "message", "iq", "offline"}; +local autoload_modules = {"presence", "message", "iq", "offline", "c2s", "s2s"}; local component_inheritable_modules = {"tls", "dialback", "iq"}; -- We need this to let modules access the real global namespace -- cgit v1.2.3 From 8f06587c5070d361bb41d39c4c92a57b49c4dfea Mon Sep 17 00:00:00 2001 From: Florian Zeitz Date: Fri, 24 Feb 2012 15:15:43 +0000 Subject: s2smanager: remove send_to_host. --- core/s2smanager.lua | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'core') diff --git a/core/s2smanager.lua b/core/s2smanager.lua index e61aaccb..6877ee18 100644 --- a/core/s2smanager.lua +++ b/core/s2smanager.lua @@ -114,10 +114,7 @@ function mark_connected(session) local from, to = session.from_host, session.to_host; session.log("info", session.direction.." s2s connection "..from.."->"..to.." complete"); - - local send_to_host = send_to_host; - function session.send(data) return send_to_host(to, from, data); end - + local event_data = { session = session }; if session.type == "s2sout" then prosody.events.fire_event("s2sout-established", event_data); -- cgit v1.2.3 From 74bff42057bbe1dbf278959cff1ae8cab077ac19 Mon Sep 17 00:00:00 2001 From: Marco Cirillo Date: Fri, 24 Feb 2012 18:03:27 +0000 Subject: s2smanager, mod_s2s: clear up ip_hosts after s2s is marked as established, remove useless space from mod_s2s code --- core/s2smanager.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'core') diff --git a/core/s2smanager.lua b/core/s2smanager.lua index 6877ee18..c05f7229 100644 --- a/core/s2smanager.lua +++ b/core/s2smanager.lua @@ -134,6 +134,7 @@ function mark_connected(session) session.sendq = nil; end + session.ip_hosts = nil; session.srv_hosts = nil; end end -- cgit v1.2.3 From 25b408fcaf0f3b11fe550d68681d13b64cc2a222 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 3 Mar 2012 00:54:19 +0100 Subject: core.portmanager: Make sure the private flag takes precedence over global interfaces --- core/portmanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core') diff --git a/core/portmanager.lua b/core/portmanager.lua index 353c4a89..3d6dada4 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -62,9 +62,9 @@ function activate_service(service_name) local bind_interfaces = set.new(config.get("*", service_name.."_interfaces") or config.get("*", service_name.."_interface") -- COMPAT w/pre-0.9 + or (service_info.private and default_local_interfaces) or config.get("*", "interfaces") or config.get("*", "interface") -- COMPAT w/pre-0.9 - or (service_info.private and default_local_interfaces) or service_info.default_interface -- COMPAT w/pre0.9 or default_interfaces); -- cgit v1.2.3 From 4c152e2f3beafe251107fd66d0476d66e3fcc65c Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 11 Mar 2012 12:40:32 +0000 Subject: portmanager: Add get_service() --- core/portmanager.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'core') diff --git a/core/portmanager.lua b/core/portmanager.lua index 3d6dada4..f655659d 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -128,5 +128,8 @@ function register_service(service_name, service_info) return true; end +function get_service(service_name) + return services[service_name]; +end return _M; -- cgit v1.2.3 From 26768f3e54805074211fc0846221acce5765f570 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 11 Mar 2012 19:14:28 +0100 Subject: s2smanager, mod_s2s: Move import of dns_max_depth to mod_s2s --- core/s2smanager.lua | 1 - 1 file changed, 1 deletion(-) (limited to 'core') diff --git a/core/s2smanager.lua b/core/s2smanager.lua index c05f7229..158b5461 100644 --- a/core/s2smanager.lua +++ b/core/s2smanager.lua @@ -37,7 +37,6 @@ local sha256_hash = require "util.hashes".sha256; local adns, dns = require "net.adns", require "net.dns"; local config = require "core.configmanager"; local dns_timeout = config.get("*", "core", "dns_timeout") or 15; -local max_dns_depth = config.get("*", "core", "dns_max_depth") or 3; local cfg_sources = config.get("*", "core", "s2s_interface") or config.get("*", "core", "interface"); local sources; -- cgit v1.2.3 From 30314b5891a1eb96c6b5fc7d9235695c3077e54e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 11 Mar 2012 18:35:27 +0000 Subject: portmanager: Add get_active_services() --- core/portmanager.lua | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'core') diff --git a/core/portmanager.lua b/core/portmanager.lua index f655659d..103c5d44 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -132,4 +132,8 @@ function get_service(service_name) return services[service_name]; end +function get_active_services(...) + return active_services; +end + return _M; -- cgit v1.2.3 From 121a7f3acddc52eb911688b5f23d3efee9178ea5 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 14 Mar 2012 17:03:48 +0000 Subject: modulemanager: Use appropriate events object for global modules when firing item-removed on unload --- core/modulemanager.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'core') diff --git a/core/modulemanager.lua b/core/modulemanager.lua index 0ca37105..328224ae 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -101,11 +101,12 @@ local function do_unload_module(host, name) end if mod.module.items then -- remove items + local events = (host == "*" and prosody.events) or hosts[host].events; for key,t in pairs(mod.module.items) do for i = #t,1,-1 do local value = t[i]; t[i] = nil; - hosts[host].events.fire_event("item-removed/"..key, {source = mod.module, item = value}); + events.fire_event("item-removed/"..key, {source = mod.module, item = value}); end end end -- cgit v1.2.3 From 2c3103e84ae364bc20782575a8113c9b96c1cbd7 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 14 Mar 2012 21:33:15 +0000 Subject: moduleapi: Set module.global = true when module:set_global() is called --- core/moduleapi.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'core') diff --git a/core/moduleapi.lua b/core/moduleapi.lua index 7a4d1fa6..b11fd7b3 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -50,6 +50,7 @@ function api:set_global() local _log = logger.init("mod_"..self.name); self.log = function (self, ...) return _log(...); end; self._log = _log; + self.global = true; end function api:add_feature(xmlns) -- cgit v1.2.3 From ce27bd5fe59bc640673abb52daa43110cfcd5c55 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 14 Mar 2012 21:37:00 +0000 Subject: modulemanager: Some refactoring. Deprecate module.host = "*", modules should call module:set_global() (which has been around since forever) --- core/modulemanager.lua | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'core') diff --git a/core/modulemanager.lua b/core/modulemanager.lua index 328224ae..f9f3a8b8 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -123,6 +123,7 @@ local function do_load_module(host, module_name) if not modulemap[host] then modulemap[host] = {}; + hosts[host].modules = modulemap[host]; end if modulemap[host][module_name] then @@ -148,8 +149,6 @@ local function do_load_module(host, module_name) api_instance.environment = pluginenv; setfenv(mod, pluginenv); - hosts[host].modules = modulemap[host]; - modulemap[host][module_name] = pluginenv; local ok, err = pcall(mod); if ok then @@ -161,15 +160,18 @@ local function do_load_module(host, module_name) end end - -- Use modified host, if the module set one - if api_instance.host == "*" and host ~= "*" then - modulemap[host][module_name] = nil; - modulemap["*"][module_name] = pluginenv; - api_instance:set_global(); + modulemap[pluginenv.module.host][module_name] = pluginenv; + if pluginenv.module.host == "*" then + if not pluginenv.module.global then -- COMPAT w/pre-0.9 + log("warn", "mod_%s: Setting module.host = '*' deprecated, call module:set_global() instead", module_name); + api_instance:set_global(); + end + else + hosts[host].modules[module_name] = pluginenv; end - else + end + if not ok then log("error", "Error initializing module '%s' on '%s': %s", module_name, host, err or "nil"); - do_unload_module(api_instance.host, module_name); -- Ignore error, module may be partially-loaded end return ok and pluginenv, err; end -- cgit v1.2.3 From 0270a3a90107621947d18bead0b5ed66054317ca Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 14 Mar 2012 21:39:02 +0000 Subject: portmanager: Add unregister_service(), and allow multiple services with the same name (they get queued) --- core/portmanager.lua | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) (limited to 'core') diff --git a/core/portmanager.lua b/core/portmanager.lua index 103c5d44..30597628 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -13,8 +13,8 @@ end --- Private state --- service_name -> service_info -local services = {}; +-- service_name -> { service_info, ... } +local services = setmetatable({}, { __index = function (t, k) rawset(t, k, {}); return rawget(t, k); end }); -- service_name, interface (string), port (number) local active_services = multitable.new(); @@ -55,7 +55,7 @@ module("portmanager", package.seeall); --- Public API function activate_service(service_name) - local service_info = services[service_name]; + local service_info = services[service_name][1]; if not service_info then return nil, "Unknown service: "..service_name; end @@ -102,6 +102,7 @@ function activate_service(service_name) end end log("info", "Activated service '%s'", service_name); + return true; end function deactivate(service_name) @@ -118,16 +119,35 @@ function deactivate(service_name) end function register_service(service_name, service_info) - services[service_name] = service_info; + table.insert(services[service_name], service_info); - if not active_services[service_name] then - activate_service(service_name); + if not active_services:get(service_name) then + log("debug", "No active service for %s, activating...", service_name); + local ok, err = activate_service(service_name); + if not ok then + log("error", "Failed to activate service '%s': %s", service_name, err or "unknown error"); + end end fire_event("service-added", { name = service_name, service = service_info }); return true; end +function unregister_service(service_name, service_info) + local service_info_list = services[service_name]; + for i, service in ipairs(service_info_list) do + if service == service_info then + table.remove(service_info_list, i); + end + end + if active_services[service_name] == service_info then + deactivate(service_name); + if #service_info_list > 0 then -- Other services registered with this name + activate(service_name); -- Re-activate with the next available one + end + end +end + function get_service(service_name) return services[service_name]; end -- cgit v1.2.3 From f70f82d6a711b3eb94e800baed00bb87c09eaecd Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 14 Mar 2012 21:39:45 +0000 Subject: portmanager: Support item-added/net-provider (global and shared modules only!) --- core/portmanager.lua | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'core') diff --git a/core/portmanager.lua b/core/portmanager.lua index 30597628..2870815c 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -52,6 +52,15 @@ end module("portmanager", package.seeall); +prosody.events.add_handler("item-added/net-provider", function (event) + local item = event.item; + register_service(item.name, item); +end); +prosody.events.add_handler("item-removed/net-provider", function (event) + local item = event.item; + unregister_service(item.name, item); +end); + --- Public API function activate_service(service_name) -- cgit v1.2.3 From dd638dc7173768121e53034170b4bf85943018c7 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 14 Mar 2012 21:40:14 +0000 Subject: portmanager: Fix log message when multiple services are configured to use the same port --- core/portmanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core') diff --git a/core/portmanager.lua b/core/portmanager.lua index 2870815c..a7ba8a49 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -95,7 +95,7 @@ function activate_service(service_name) for interface in bind_interfaces do for port in bind_ports do if not service_info.multiplex and #active_services:search(nil, interface, port) > 0 then - log("error", "Multiple services configured to listen on the same port: %s, %s", table.concat(active_services:search(nil, interface, port), ", "), service_name); + log("error", "Multiple services configured to listen on the same port ([%s]:%d): %s, %s", interface, port, active_services:search(nil, interface, port)[1][1].service.name or "", service_name or ""); else local handler, err = server.addserver(interface, port, listener, mode, ssl); if not handler then -- cgit v1.2.3 From 7366c85253a3e1dddb35f8eea2c2fd45996c7f1b Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 14 Mar 2012 23:44:24 +0000 Subject: portmanager: Fire service-removed on unregister --- core/portmanager.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'core') diff --git a/core/portmanager.lua b/core/portmanager.lua index a7ba8a49..605e1e30 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -155,6 +155,7 @@ function unregister_service(service_name, service_info) activate(service_name); -- Re-activate with the next available one end end + fire_event("service-removed", { name = service_name, service = service_info }); end function get_service(service_name) -- cgit v1.2.3 From 775261ef27a572da6e1c1ccacf79f59f6ead58ef Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 15 Mar 2012 02:52:31 +0000 Subject: moduleapi: Add module:provides(), a shortcut to add an item with the current module's name --- core/moduleapi.lua | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'core') diff --git a/core/moduleapi.lua b/core/moduleapi.lua index b11fd7b3..2177378f 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -292,4 +292,18 @@ function api:handle_items(type, added_cb, removed_cb, existing) end end +function api:provides(name, item) + if not item then item = self.environment; end + if not item.name then + local item_name = module.name; + -- Strip a provider prefix to find the item name + -- (e.g. "auth_foo" -> "foo" for an auth provider) + if item_name:find(name.."_", 1, true) == 1 then + item_name = item_name:sub(#name+2); + end + item.name = item_name; + end + self:add_item(name, item); +end + return api; -- cgit v1.2.3 From 78cd9a0ddcb2e3af03173eeceec270ef804603ec Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 15 Mar 2012 02:53:05 +0000 Subject: moduleapi: Add module:send() as an alias for core_post_stanza() from the current host's origin --- core/moduleapi.lua | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'core') diff --git a/core/moduleapi.lua b/core/moduleapi.lua index 2177378f..a577c07a 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -22,6 +22,7 @@ local tonumber, tostring = tonumber, tostring; local prosody = prosody; local hosts = prosody.hosts; +local core_post_stanza = prosody.core_post_stanza; -- Registry of shared module data local shared_data = setmetatable({}, { __mode = "v" }); @@ -306,4 +307,8 @@ function api:provides(name, item) self:add_item(name, item); end +function api:send(stanza) + return core_post_stanza(hosts[self.host], stanza); +end + return api; -- cgit v1.2.3 From eafbf7d47eb4e65a8efb9b9daea112b470d3f4c8 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 15 Mar 2012 02:56:44 +0000 Subject: portmanager: Allow services to specify their config option prefix --- core/portmanager.lua | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'core') diff --git a/core/portmanager.lua b/core/portmanager.lua index 605e1e30..a87e14eb 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -68,19 +68,23 @@ function activate_service(service_name) if not service_info then return nil, "Unknown service: "..service_name; end + + local config_prefix = (service_info.config_prefix or service_name).."_"; + if config_prefix == "_" then + config_prefix = ""; + end - local bind_interfaces = set.new(config.get("*", service_name.."_interfaces") - or config.get("*", service_name.."_interface") -- COMPAT w/pre-0.9 + local bind_interfaces = set.new(config.get("*", config_prefix.."interfaces") + or config.get("*", config_prefix.."interface") -- COMPAT w/pre-0.9 or (service_info.private and default_local_interfaces) or config.get("*", "interfaces") or config.get("*", "interface") -- COMPAT w/pre-0.9 or service_info.default_interface -- COMPAT w/pre0.9 or default_interfaces); - local bind_ports = set.new(config.get("*", service_name.."_ports") - or (service_info.multiplex and config.get("*", "ports")) or service_info.default_ports or {service_info.default_port}); + local bind_ports = set.new(config.get("*", config_prefix.."ports") local listener = service_info.listener; local mode = listener.default_mode or "*a"; -- cgit v1.2.3 From 373fb34e948918f0a74732337e6357a92a59e669 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 15 Mar 2012 03:02:09 +0000 Subject: portmanager: Fix pre-0.9 compatibility by taking default_interface and default_port from the listener instead of service table --- core/portmanager.lua | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'core') diff --git a/core/portmanager.lua b/core/portmanager.lua index a87e14eb..a7c3c44e 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -69,6 +69,8 @@ function activate_service(service_name) return nil, "Unknown service: "..service_name; end + local listener = service_info.listener; + local config_prefix = (service_info.config_prefix or service_name).."_"; if config_prefix == "_" then config_prefix = ""; @@ -79,14 +81,13 @@ function activate_service(service_name) or (service_info.private and default_local_interfaces) or config.get("*", "interfaces") or config.get("*", "interface") -- COMPAT w/pre-0.9 - or service_info.default_interface -- COMPAT w/pre0.9 + or listener.default_interface -- COMPAT w/pre0.9 or default_interfaces); - or service_info.default_ports - or {service_info.default_port}); local bind_ports = set.new(config.get("*", config_prefix.."ports") + or service_info.default_ports + or {listener.default_port}); -- COMPAT w/pre-0.9 - local listener = service_info.listener; local mode = listener.default_mode or "*a"; local ssl; if service_info.encryption == "ssl" then -- cgit v1.2.3 From f0b8af1484581188305a3d0abac0f8b6c15bfb53 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 15 Mar 2012 03:02:36 +0000 Subject: portmanager: Remove check for service_info.multiplex (now implemented in mod_net_multiplex) --- core/portmanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core') diff --git a/core/portmanager.lua b/core/portmanager.lua index a7c3c44e..b4ceaef9 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -99,7 +99,7 @@ function activate_service(service_name) for interface in bind_interfaces do for port in bind_ports do - if not service_info.multiplex and #active_services:search(nil, interface, port) > 0 then + if #active_services:search(nil, interface, port) > 0 then log("error", "Multiple services configured to listen on the same port ([%s]:%d): %s, %s", interface, port, active_services:search(nil, interface, port)[1][1].service.name or "", service_name or ""); else local handler, err = server.addserver(interface, port, listener, mode, ssl); -- cgit v1.2.3 From 0d6cb88a1eb002675334b16b5a3c627282cb2c61 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 15 Mar 2012 03:02:51 +0000 Subject: portmanager: Add get_registered_services() to the public API --- core/portmanager.lua | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'core') diff --git a/core/portmanager.lua b/core/portmanager.lua index b4ceaef9..914a8e2f 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -171,4 +171,8 @@ function get_active_services(...) return active_services; end +function get_registered_services() + return services; +end + return _M; -- cgit v1.2.3 From 60b203533dbbb0404d9641e6455aaf6dad916dcb Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 15 Mar 2012 16:29:30 +0000 Subject: portmanager: Support 'default_port' in service options --- core/portmanager.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'core') diff --git a/core/portmanager.lua b/core/portmanager.lua index 914a8e2f..c5bb936a 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -86,7 +86,9 @@ function activate_service(service_name) local bind_ports = set.new(config.get("*", config_prefix.."ports") or service_info.default_ports - or {listener.default_port}); -- COMPAT w/pre-0.9 + or {service_info.default_port + or listener.default_port -- COMPAT w/pre-0.9 + }); local mode = listener.default_mode or "*a"; local ssl; -- cgit v1.2.3 From fe16dde20be2082bb44788113d416ecb35955ad1 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 15 Mar 2012 19:09:24 +0000 Subject: loggingmanager, util.logger: Remove name sinks and the ability to filter logs by source name (lots of code, hardly used if at all, and possibly broken) --- core/loggingmanager.lua | 34 ++++++---------------------------- 1 file changed, 6 insertions(+), 28 deletions(-) (limited to 'core') diff --git a/core/loggingmanager.lua b/core/loggingmanager.lua index 426425c1..56a3ee2c 100644 --- a/core/loggingmanager.lua +++ b/core/loggingmanager.lua @@ -48,34 +48,12 @@ local logging_levels = { "debug", "info", "warn", "error" } local function add_rule(sink_config) local sink_maker = log_sink_types[sink_config.to]; if sink_maker then - if sink_config.levels and not sink_config.source then - -- Create sink - local sink = sink_maker(sink_config); - - -- Set sink for all chosen levels - for level in pairs(get_levels(sink_config.levels)) do - logger.add_level_sink(level, sink); - end - elseif sink_config.source and not sink_config.levels then - logger.add_name_sink(sink_config.source, sink_maker(sink_config)); - elseif sink_config.source and sink_config.levels then - local levels = get_levels(sink_config.levels); - local sink = sink_maker(sink_config); - logger.add_name_sink(sink_config.source, - function (name, level, ...) - if levels[level] then - return sink(name, level, ...); - end - end); - else - -- All sources - -- Create sink - local sink = sink_maker(sink_config); - - -- Set sink for all levels - for _, level in pairs(logging_levels) do - logger.add_level_sink(level, sink); - end + -- Create sink + local sink = sink_maker(sink_config); + + -- Set sink for all chosen levels + for level in pairs(get_levels(sink_config.levels or logging_levels)) do + logger.add_level_sink(level, sink); end else -- No such sink type -- cgit v1.2.3 From 9792c6566b3416e7aef11bd3628f15ca00418132 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Apr 2012 20:00:30 +0100 Subject: modulemanager: Allow loading a module onto "*" (part-fixes #228) --- core/modulemanager.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'core') diff --git a/core/modulemanager.lua b/core/modulemanager.lua index f9f3a8b8..f00532a0 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -117,13 +117,15 @@ end local function do_load_module(host, module_name) if not (host and module_name) then return nil, "insufficient-parameters"; - elseif not hosts[host] then + elseif not hosts[host] and host ~= "*"then return nil, "unknown-host"; end if not modulemap[host] then modulemap[host] = {}; - hosts[host].modules = modulemap[host]; + if host ~= "*" then + hosts[host].modules = modulemap[host]; + end end if modulemap[host][module_name] then -- cgit v1.2.3 From e0805574e18fddde821ba9ec6ade8f234f368f1b Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Apr 2012 20:01:40 +0100 Subject: modulemanager: Use api_instance rather than pluginenv.module (same thing) --- core/modulemanager.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'core') diff --git a/core/modulemanager.lua b/core/modulemanager.lua index f00532a0..f5173f00 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -162,9 +162,9 @@ local function do_load_module(host, module_name) end end - modulemap[pluginenv.module.host][module_name] = pluginenv; - if pluginenv.module.host == "*" then - if not pluginenv.module.global then -- COMPAT w/pre-0.9 + modulemap[api_instance.host][module_name] = pluginenv; + if api_instance.host == "*" then + if not api_instance.global then -- COMPAT w/pre-0.9 log("warn", "mod_%s: Setting module.host = '*' deprecated, call module:set_global() instead", module_name); api_instance:set_global(); end -- cgit v1.2.3 From bf4925029fade8287314c858029413aacc5a9a48 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Apr 2012 20:02:45 +0100 Subject: modulemanager: Use modulemap rather than hosts[host] (fix for when host == "*") --- core/modulemanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core') diff --git a/core/modulemanager.lua b/core/modulemanager.lua index f5173f00..f7594cd9 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -169,7 +169,7 @@ local function do_load_module(host, module_name) api_instance:set_global(); end else - hosts[host].modules[module_name] = pluginenv; + modulemap[host][module_name] = pluginenv; end end if not ok then -- cgit v1.2.3 From 3f130834834b04f47b0c00bb5504e67e0744fa16 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Apr 2012 20:04:07 +0100 Subject: modulemanager: Make module_has_method and module_call_method use rawget() --- core/modulemanager.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'core') diff --git a/core/modulemanager.lua b/core/modulemanager.lua index f7594cd9..769041f9 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -261,12 +261,12 @@ function is_loaded(host, name) end function module_has_method(module, method) - return type(module.module[method]) == "function"; + return type(rawget(module.module, method)) == "function"; end function call_module_method(module, method, ...) - if module_has_method(module, method) then - local f = module.module[method]; + local f = rawget(module.module, method); + if type(f) == "function" then return pcall(f, ...); else return false, "no-such-method"; -- cgit v1.2.3 From f51d5c0bd00c83ed10eae136e95dc49a0d61b684 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Apr 2012 20:09:03 +0100 Subject: modulemanager: Support for shared modules - function module.add_host(host_module) in a global module --- core/modulemanager.lua | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'core') diff --git a/core/modulemanager.lua b/core/modulemanager.lua index 769041f9..e76f2624 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -132,6 +132,21 @@ local function do_load_module(host, module_name) log("warn", "%s is already loaded for %s, so not loading again", module_name, host); return nil, "module-already-loaded"; elseif modulemap["*"][module_name] then + local mod = modulemap["*"][module_name]; + if module_has_method(mod, "add_host") then + local _log = logger.init(host..":"..module_name); + local host_module_api = setmetatable({ + host = host, event_handlers = {}, items = {}; + _log = _log, log = function (self, ...) return _log(...); end; + },{ + __index = modulemap["*"][module_name].module; + }); + local ok, result, module_err = call_module_method(mod, "add_host", host_module_api); + if not ok or result == false then return nil, ok and module_err or result; end + local host_module = setmetatable({ module = host_module_api }, { __index = mod }); + modulemap[host][module_name] = host_module; + return host_module; + end return nil, "global-module-already-loaded"; end -- cgit v1.2.3 From 1894252bd4d000923088e4db37de91c0270b323a Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Apr 2012 20:37:06 +0100 Subject: modulemanager: When a shared module becomes global, ensure it still gets loaded onto the original target host --- core/modulemanager.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'core') diff --git a/core/modulemanager.lua b/core/modulemanager.lua index e76f2624..f263816b 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -183,8 +183,10 @@ local function do_load_module(host, module_name) log("warn", "mod_%s: Setting module.host = '*' deprecated, call module:set_global() instead", module_name); api_instance:set_global(); end - else - modulemap[host][module_name] = pluginenv; + if host ~= api_instance.host and module_has_method(pluginenv, "add_host") then + -- Now load the module again onto the host it was originally being loaded on + do_load_module(host, module_name); + end end end if not ok then -- cgit v1.2.3 From e0762790fdb004d359450908dc8b94b3725daf74 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 21 Apr 2012 23:11:59 +0200 Subject: core.certmanager: Log a message when a password is required but not supplied. fixes #214 --- core/certmanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core') diff --git a/core/certmanager.lua b/core/certmanager.lua index 8b82ac47..cccf3098 100644 --- a/core/certmanager.lua +++ b/core/certmanager.lua @@ -35,7 +35,7 @@ function create_context(host, mode, user_ssl_config) mode = mode; protocol = user_ssl_config.protocol or "sslv23"; key = resolve_path(config_path, user_ssl_config.key); - password = user_ssl_config.password; + password = user_ssl_config.password or function() log("error", "Encrypted certificate for %s requires 'ssl' 'password' to be set in config", host); end; certificate = resolve_path(config_path, user_ssl_config.certificate); capath = resolve_path(config_path, user_ssl_config.capath or default_capath); cafile = resolve_path(config_path, user_ssl_config.cafile); -- cgit v1.2.3 From 2d053569057683dbba3f960522d760cee3a1aae3 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Apr 2012 22:52:15 +0100 Subject: moduleapi: Have modules internally store a reference to shared tables they use, to ensure they don't get collected while any module that had access to that table is still loaded (thanks Zash) --- core/moduleapi.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'core') diff --git a/core/moduleapi.lua b/core/moduleapi.lua index a577c07a..7a3bd1c8 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -135,6 +135,7 @@ end -- Intentionally does not allow the table at a path to be _set_, it -- is auto-created if it does not exist. function api:shared(...) + if not self.shared_data then self.shared_data = {}; end local paths = { n = select("#", ...), ... }; local data_array = {}; local default_path_components = { self.host, self.name }; @@ -150,6 +151,7 @@ function api:shared(...) shared_data[path] = shared; end t_insert(data_array, shared); + self.shared_data[path] = shared; end return unpack(data_array); end -- cgit v1.2.3 From 6202ef36ecb18cfcf2aee207ccbccec24899513e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Apr 2012 22:53:22 +0100 Subject: modulemanager: For children of shared modules, set module.environment to the empty environment for that module (useful to expose data or APIs at host.modules[module]) --- core/modulemanager.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'core') diff --git a/core/modulemanager.lua b/core/modulemanager.lua index f263816b..aca314c0 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -144,6 +144,7 @@ local function do_load_module(host, module_name) local ok, result, module_err = call_module_method(mod, "add_host", host_module_api); if not ok or result == false then return nil, ok and module_err or result; end local host_module = setmetatable({ module = host_module_api }, { __index = mod }); + host_module.module.environment = host_module; modulemap[host][module_name] = host_module; return host_module; end -- cgit v1.2.3 From 5b5c8c83b395e259924d28f14b65133bb9a5791c Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Apr 2012 14:09:35 +0100 Subject: moduleapi: module:provides(): Fix usage of wrong table --- core/moduleapi.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core') diff --git a/core/moduleapi.lua b/core/moduleapi.lua index 7a3bd1c8..61926017 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -298,7 +298,7 @@ end function api:provides(name, item) if not item then item = self.environment; end if not item.name then - local item_name = module.name; + local item_name = self.name; -- Strip a provider prefix to find the item name -- (e.g. "auth_foo" -> "foo" for an auth provider) if item_name:find(name.."_", 1, true) == 1 then -- cgit v1.2.3 From de1168f2885e614882839f754e039da55fdbe42f Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Apr 2012 14:10:04 +0100 Subject: moduleapi: module:provides(): Add "-provider" onto the key name --- core/moduleapi.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core') diff --git a/core/moduleapi.lua b/core/moduleapi.lua index 61926017..2223f4d0 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -306,7 +306,7 @@ function api:provides(name, item) end item.name = item_name; end - self:add_item(name, item); + self:add_item(name.."-provider", item); end function api:send(stanza) -- cgit v1.2.3 From c40ee817429487a325e871da5ddc3e43eab19bb5 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Apr 2012 14:11:10 +0100 Subject: modulemanager: Report errors that happen when loading a shared module onto its original host --- core/modulemanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core') diff --git a/core/modulemanager.lua b/core/modulemanager.lua index aca314c0..f0648667 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -186,7 +186,7 @@ local function do_load_module(host, module_name) end if host ~= api_instance.host and module_has_method(pluginenv, "add_host") then -- Now load the module again onto the host it was originally being loaded on - do_load_module(host, module_name); + ok, err = do_load_module(host, module_name); end end end -- cgit v1.2.3 From 33ffda321d20938d4a28b15716d4f85871b4e6a7 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Apr 2012 14:15:07 +0100 Subject: moduleapi: module:depends(): Load shared modules onto the current host even if they are loaded globally already --- core/moduleapi.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'core') diff --git a/core/moduleapi.lua b/core/moduleapi.lua index 2223f4d0..e680f615 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -120,6 +120,9 @@ function api:depends(name) end); end local mod = modulemanager.get_module(self.host, name) or modulemanager.get_module("*", name); + if mod and mod.module.host == "*" and modulemanager.module_has_method(mod, "add_host") then + mod = nil; -- This is a shared module, so we still want to load it on our host + end if not mod then local err; mod, err = modulemanager.load(self.host, name); -- cgit v1.2.3 From 91d1035d7d501855d595d80aa985adfac6addfda Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Apr 2012 15:38:36 +0100 Subject: modulemanager: Set module.loaded = false on unload --- core/modulemanager.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'core') diff --git a/core/modulemanager.lua b/core/modulemanager.lua index f0648667..bf1e1924 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -110,6 +110,7 @@ local function do_unload_module(host, name) end end end + mod.module.loaded = false; modulemap[host][name] = nil; return true; end -- cgit v1.2.3 From b6a2692f3f90ed40f7e9a5103b1bb141646a7259 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Apr 2012 15:38:40 +0100 Subject: moduleapi: Add module:add_timer(delay, callback) - automatically halts the timer on module unload --- core/moduleapi.lua | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'core') diff --git a/core/moduleapi.lua b/core/moduleapi.lua index e680f615..d16ee410 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -316,4 +316,11 @@ function api:send(stanza) return core_post_stanza(hosts[self.host], stanza); end +function api:add_timer(delay, callback) + return timer.add_task(delay, function (t) + if self.loaded == false then return; end + return callback(t); + end); +end + return api; -- cgit v1.2.3 From 64d127307adc8aa401d7ae4a964298cae4d08016 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 24 Apr 2012 16:00:20 +0100 Subject: portmanager: Add get_service_at(interface, port) and close(interface, port) --- core/portmanager.lua | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'core') diff --git a/core/portmanager.lua b/core/portmanager.lua index c5bb936a..da238dba 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -126,9 +126,7 @@ function deactivate(service_name) if not active then return; end for interface, ports in pairs(active) do for port, active_service in pairs(ports) do - active_service:close(); - active_services:remove(service_name, interface, port, active_service); - log("debug", "Removed listening service %s from [%s]:%d", service_name, interface, port); + close(interface, port); end end log("info", "Deactivated service '%s'", service_name); @@ -165,6 +163,22 @@ function unregister_service(service_name, service_info) fire_event("service-removed", { name = service_name, service = service_info }); end +function close(interface, port) + local service, server = get_service_at(interface, port); + if not service then + return false, "port-not-open"; + end + server:close(); + active_services:remove(service.name, interface, port); + log("debug", "Removed listening service %s from [%s]:%d", service.name, interface, port); + return true; +end + +function get_service_at(interface, port) + local data = active_services:search(nil, interface, port)[1][1]; + return data.service, data.server; +end + function get_service(service_name) return services[service_name]; end -- cgit v1.2.3