diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/certmanager.lua | 6 | ||||
-rw-r--r-- | core/hostmanager.lua | 10 | ||||
-rw-r--r-- | core/moduleapi.lua | 40 | ||||
-rw-r--r-- | core/modulemanager.lua | 80 | ||||
-rw-r--r-- | core/portmanager.lua | 59 | ||||
-rw-r--r-- | core/s2smanager.lua | 40 | ||||
-rw-r--r-- | core/sessionmanager.lua | 4 | ||||
-rw-r--r-- | core/stanza_router.lua | 9 | ||||
-rw-r--r-- | core/storagemanager.lua | 2 | ||||
-rw-r--r-- | core/usermanager.lua | 5 |
10 files changed, 159 insertions, 96 deletions
diff --git a/core/certmanager.lua b/core/certmanager.lua index 8b82ac47..84fdddf4 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); @@ -75,9 +75,9 @@ function create_context(host, mode, user_ssl_config) else reason = "Reason: "..tostring(reason):lower(); end - log("error", "SSL/TLS: Failed to load %s: %s (host: %s)", file, reason, host); + log("error", "SSL/TLS: Failed to load %s: %s (for %s)", file, reason, host); else - log("error", "SSL/TLS: Error initialising for host %s: %s (host: %s)", host, err, host); + log("error", "SSL/TLS: Error initialising for %s: %s", host, err); end end return ctx, err; diff --git a/core/hostmanager.lua b/core/hostmanager.lua index 0dd1d426..66275d96 100644 --- a/core/hostmanager.lua +++ b/core/hostmanager.lua @@ -12,6 +12,7 @@ local events_new = require "util.events".new; local disco_items = require "util.multitable".new(); local NULL = {}; +local jid_split = require "util.jid".split; local uuid_gen = require "util.uuid".generate; local log = require "util.logger".init("hostmanager"); @@ -23,7 +24,7 @@ if not _G.prosody.incoming_s2s then end local incoming_s2s = _G.prosody.incoming_s2s; -local pairs, setmetatable = pairs, setmetatable; +local pairs, select = pairs, select; local tostring, type = tostring, type; module "hostmanager" @@ -73,7 +74,6 @@ function activate(host, host_config) s2sout = {}; events = events_new(); dialback_secret = configmanager.get(host, "core", "dialback_secret") or uuid_gen(); - disallow_s2s = configmanager.get(host, "core", "disallow_s2s"); send = host_send; }; if not host_config.core.component_module then -- host @@ -93,7 +93,7 @@ function activate(host, host_config) end log((hosts_loaded_once and "info") or "debug", "Activated host: %s", host); - prosody_events.fire_event("host-activated", host, host_config); + prosody_events.fire_event("host-activated", host); return true; end @@ -101,13 +101,14 @@ function deactivate(host, reason) local host_session = hosts[host]; if not host_session then return nil, "The host "..tostring(host).." is not activated"; end log("info", "Deactivating host: %s", host); - prosody_events.fire_event("host-deactivating", host, host_session); + prosody_events.fire_event("host-deactivating", { host = host, host_session = host_session, reason = reason }); if type(reason) ~= "table" then reason = { condition = "host-gone", text = tostring(reason or "This server has stopped serving "..host) }; end -- Disconnect local users, s2s connections + -- TODO: These should move to mod_c2s and mod_s2s (how do they know they're being unloaded and not reloaded?) if host_session.sessions then for username, user in pairs(host_session.sessions) do for resource, session in pairs(user.sessions) do @@ -132,6 +133,7 @@ function deactivate(host, reason) end end + -- TODO: This should be done in modulemanager if host_session.modules then for module in pairs(host_session.modules) do modulemanager.unload(host, module); diff --git a/core/moduleapi.lua b/core/moduleapi.lua index a577c07a..44c84de1 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -12,6 +12,7 @@ local array = require "util.array"; local set = require "util.set"; local logger = require "util.logger"; local pluginloader = require "util.pluginloader"; +local timer = require "util.timer"; local multitable_new = require "util.multitable".new; @@ -42,7 +43,7 @@ function api:get_host() end function api:get_host_type() - return hosts[self.host].type; + return self.host ~= "*" and hosts[self.host].type or nil; end function api:set_global() @@ -73,6 +74,10 @@ function api:hook_object_event(object, event, handler, priority) return object.add_handler(event, handler, priority); end +function api:unhook_object_event(object, event, handler) + return object.remove_handler(event, handler); +end + function api:hook(event, handler, priority) return self:hook_object_event((hosts[self.host] or prosody).events, event, handler, priority); end @@ -81,7 +86,7 @@ function api:hook_global(event, handler, priority) return self:hook_object_event(prosody.events, event, handler, priority); end -function api:hook_stanza(xmlns, name, handler, priority) +function api:hook_tag(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; @@ -91,6 +96,7 @@ function api:hook_stanza(xmlns, name, handler, priority) end return self:hook("stanza/"..(xmlns and (xmlns..":") or "")..name, function (data) return handler(data.origin, data.stanza, data); end, priority); end +api.hook_stanza = api.hook_tag; -- COMPAT w/pre-0.9 function api:require(lib) local f, n = pluginloader.load_code(self.name, lib..".lib.lua"); @@ -106,7 +112,7 @@ function api:depends(name) if not self.dependencies then self.dependencies = {}; self:hook("module-reloaded", function (event) - if self.dependencies[event.module] then + if self.dependencies[event.module] and not self.reloading then self:log("info", "Auto-reloading due to reload of %s:%s", event.host, event.module); modulemanager.reload(self.host, self.name); return; @@ -120,6 +126,10 @@ 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 self.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); @@ -135,6 +145,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 +161,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 @@ -244,7 +256,6 @@ function api:get_option_set(name, ...) 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 {}; @@ -296,7 +307,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 @@ -304,11 +315,28 @@ 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) 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 + +local path_sep = package.config:sub(1,1); +function api:get_directory() + return self.path and (self.path:gsub("%"..path_sep.."[^"..path_sep.."]*$", "")) or nil; +end + +function api:load_resource(path, mode) + path = config.resolve_relative_path(self:get_directory(), path); + return io.open(path, mode); +end + return api; diff --git a/core/modulemanager.lua b/core/modulemanager.lua index f9f3a8b8..46a27dd4 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -14,15 +14,9 @@ local pluginloader = require "util.pluginloader"; local hosts = hosts; local prosody = prosody; -local loadfile, pcall, xpcall = loadfile, pcall, xpcall; -local setmetatable, setfenv, getfenv = setmetatable, setfenv, getfenv; -local pairs, ipairs = pairs, ipairs; -local t_insert, t_concat = table.insert, table.concat; -local type = type; -local next = next; -local rawget = rawget; -local error = error; -local tostring, tonumber = tostring, tonumber; +local pcall, xpcall = pcall, xpcall; +local setmetatable, rawget, setfenv = setmetatable, rawget, setfenv; +local pairs, type, tostring = pairs, type, tostring; local debug_traceback = debug.traceback; local unpack, select = unpack, select; @@ -32,7 +26,7 @@ pcall = function(f, ...) return xpcall(function() return f(unpack(params, 1, n)) end, function(e) return tostring(e).."\n"..debug_traceback(); end); end -local array, set = require "util.array", require "util.set"; +local set = require "util.set"; local autoload_modules = {"presence", "message", "iq", "offline", "c2s", "s2s"}; local component_inheritable_modules = {"tls", "dialback", "iq"}; @@ -47,8 +41,6 @@ local api = _G.require "core.moduleapi"; -- Module API container -- [host] = { [module] = module_env } local modulemap = { ["*"] = {} }; -local NULL = {}; - -- Load modules when a host is activated function load_modules_for_host(host) local component = config.get(host, "core", "component_module"); @@ -82,6 +74,9 @@ function load_modules_for_host(host) end end prosody.events.add_handler("host-activated", load_modules_for_host); +prosody.events.add_handler("host-deactivated", function (host) + modulemap[host] = nil; +end); --- Private helpers --- @@ -110,6 +105,7 @@ local function do_unload_module(host, name) end end end + mod.module.loaded = false; modulemap[host][name] = nil; return true; end @@ -117,19 +113,40 @@ 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 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 host_module = setmetatable({ module = host_module_api }, { __index = mod }); + host_module_api.environment = host_module; + modulemap[host][module_name] = host_module; + local ok, result, module_err = call_module_method(mod, "add_host", host_module_api); + if not ok or result == false then + modulemap[host][module_name] = nil; + return nil, ok and module_err or result; + end + return host_module; + end return nil, "global-module-already-loaded"; end @@ -150,6 +167,7 @@ local function do_load_module(host, module_name) setfenv(mod, pluginenv); + modulemap[host][module_name] = pluginenv; local ok, err = pcall(mod); if ok then -- Call module's "load" @@ -160,17 +178,23 @@ 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 - log("warn", "mod_%s: Setting module.host = '*' deprecated, call module:set_global() instead", module_name); + if api_instance.host == "*" then + if not api_instance.global then -- COMPAT w/pre-0.9 + if host ~= "*" then + log("warn", "mod_%s: Setting module.host = '*' deprecated, call module:set_global() instead", module_name); + end api_instance:set_global(); end - else - hosts[host].modules[module_name] = pluginenv; + modulemap[host][module_name] = nil; + modulemap[api_instance.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 + ok, err = do_load_module(host, module_name); + end end end if not ok then + modulemap[api_instance.host][module_name] = nil; log("error", "Error initializing module '%s' on '%s': %s", module_name, host, err or "nil"); end return ok and pluginenv, err; @@ -222,7 +246,7 @@ end function load(host, name) local mod, err = do_load_module(host, name); if mod then - (hosts[mod.module.host] or prosody).events.fire_event("module-loaded", { module = name, host = host }); + (hosts[mod.module.host] or prosody).events.fire_event("module-loaded", { module = name, host = mod.module.host }); end return mod, err; end @@ -237,13 +261,15 @@ function unload(host, name) end function reload(host, name) - local ok, err = do_reload_module(host, name); - if ok then + local mod, err = do_reload_module(host, name); + if mod then + modulemap[host][name].module.reloading = true; (hosts[host] or prosody).events.fire_event("module-reloaded", { module = name, host = host }); + mod.module.reloading = nil; elseif not is_loaded(host, name) then (hosts[host] or prosody).events.fire_event("module-unloaded", { module = name, host = host }); end - return ok, err; + return mod, err; end function get_module(host, name) @@ -259,12 +285,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"; diff --git a/core/portmanager.lua b/core/portmanager.lua index c5bb936a..00f09c6b 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -1,7 +1,20 @@ +local config = require "core.configmanager"; +local certmanager = require "core.certmanager"; +local server = require "net.server"; +local log = require "util.logger".init("portmanager"); local multitable = require "util.multitable"; +local set = require "util.set"; + +local table = table; +local setmetatable, rawset, rawget = setmetatable, rawset, rawget; +local type, tonumber, ipairs, pairs = type, tonumber, ipairs, pairs; + +local prosody = prosody; local fire_event = prosody.events.fire_event; +module "portmanager"; + --- Config local default_interfaces = { "*" }; @@ -50,8 +63,6 @@ local function error_to_friendly_message(service_name, port, err) return friendly_message; end -module("portmanager", package.seeall); - prosody.events.add_handler("item-added/net-provider", function (event) local item = event.item; register_service(item.name, item); @@ -63,7 +74,7 @@ end); --- Public API -function activate_service(service_name) +function activate(service_name) local service_info = services[service_name][1]; if not service_info then return nil, "Unknown service: "..service_name; @@ -76,13 +87,14 @@ function activate_service(service_name) config_prefix = ""; end - local bind_interfaces = set.new(config.get("*", config_prefix.."interfaces") + local bind_interfaces = 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 listener.default_interface -- COMPAT w/pre0.9 - or default_interfaces); + or default_interfaces + bind_interfaces = set.new(type(bind_interfaces)~="table" and {bind_interfaces} or bind_interfaces); local bind_ports = set.new(config.get("*", config_prefix.."ports") or service_info.default_ports @@ -91,19 +103,20 @@ function activate_service(service_name) }); 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 + port = tonumber(port); 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 "<unnamed>", service_name or "<unnamed>"); else + -- Create SSL context for this service/port + if service_info.encryption == "ssl" then + local ssl_config = config.get("*", config_prefix.."ssl"); + ssl = certmanager.create_context(service_info.name.." port "..port, "server", ssl_config and (ssl_config[port] + or (ssl_config.certificate and ssl_config))); + end + -- Start listening on interface+port 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, port, err)); @@ -126,9 +139,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); @@ -139,7 +150,7 @@ function register_service(service_name, service_info) 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); + local ok, err = activate(service_name); if not ok then log("error", "Failed to activate service '%s': %s", service_name, err or "unknown error"); end @@ -165,6 +176,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 diff --git a/core/s2smanager.lua b/core/s2smanager.lua index 158b5461..9e0a91d1 100644 --- a/core/s2smanager.lua +++ b/core/s2smanager.lua @@ -9,40 +9,15 @@ local hosts = hosts; -local core_process_stanza = function(a, b) core_process_stanza(a, b); end -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 initialize_filters = require "util.filters".initialize; -local wrapclient = require "net.server".wrapclient; -local st = require "stanza"; -local stanza = st.stanza; -local nameprep = require "util.encodings".stringprep.nameprep; -local cert_verify_identity = require "util.x509".verify_identity; -local new_ip = require "util.ip".new_ip; -local rfc3484_dest = require "util.rfc3484".destination; +local tostring, pairs, ipairs, getmetatable, newproxy, setmetatable + = tostring, pairs, ipairs, getmetatable, newproxy, setmetatable; local fire_event = prosody.events.fire_event; -local uuid_gen = require "util.uuid".generate; - local logger_init = require "util.logger".init; local log = logger_init("s2smanager"); -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 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; incoming_s2s = {}; @@ -99,7 +74,7 @@ function make_authenticated(session, host) else return false; end - session.log("debug", "connection %s->%s is now authenticated", session.from_host or "(unknown)", session.to_host or "(unknown)"); + session.log("debug", "connection %s->%s is now authenticated for %s", session.from_host or "(unknown)", session.to_host or "(unknown)", host); mark_connected(session); @@ -117,10 +92,15 @@ function mark_connected(session) local event_data = { session = session }; if session.type == "s2sout" then prosody.events.fire_event("s2sout-established", event_data); - hosts[session.from_host].events.fire_event("s2sout-established", event_data); + hosts[from].events.fire_event("s2sout-established", event_data); else + local host_session = hosts[to]; + session.send = function(stanza) + host_session.events.fire_event("route/remote", { from_host = to, to_host = from, stanza = stanza }); + end; + prosody.events.fire_event("s2sin-established", event_data); - hosts[session.to_host].events.fire_event("s2sin-established", event_data); + hosts[to].events.fire_event("s2sin-established", event_data); end if session.direction == "outgoing" then diff --git a/core/sessionmanager.lua b/core/sessionmanager.lua index c101bf4e..37c1626a 100644 --- a/core/sessionmanager.lua +++ b/core/sessionmanager.lua @@ -143,10 +143,6 @@ function bind_resource(session, resource) bare_sessions[session.username..'@'..session.host] = sessions; else local sessions = hosts[session.host].sessions[session.username].sessions; - local limit = config_get(session.host, "core", "max_resources") or 10; - if #sessions >= limit then - return nil, "cancel", "resource-constraint", "Resource limit reached; only "..limit.." resources allowed"; - end if sessions[resource] then -- Resource conflict local policy = config_get(session.host, "core", "conflict_resolve"); diff --git a/core/stanza_router.lua b/core/stanza_router.lua index 54c5a1a6..b4c65a10 100644 --- a/core/stanza_router.lua +++ b/core/stanza_router.lua @@ -185,15 +185,16 @@ function core_route_stanza(origin, stanza) core_post_stanza(origin, stanza); else log("debug", "Routing to remote..."); - if not hosts[from_host] then + local host_session = hosts[from_host]; + if not host_session then log("error", "No hosts[from_host] (please report): %s", tostring(stanza)); else local xmlns = stanza.attr.xmlns; stanza.attr.xmlns = nil; - 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!) + local routed = host_session.events.fire_event("route/remote", { origin = origin, stanza = stanza, from_host = from_host, to_host = host }); stanza.attr.xmlns = xmlns; -- reset - if routed == nil then - core_route_stanza(hosts[from_host], st.error_reply(stanza, "cancel", "not-allowed", "Communication with remote domains is not enabled")); + if not routed then + core_route_stanza(host_session, st.error_reply(stanza, "cancel", "not-allowed", "Communication with remote domains is not enabled")); end end end diff --git a/core/storagemanager.lua b/core/storagemanager.lua index c96ef3ec..71e79271 100644 --- a/core/storagemanager.lua +++ b/core/storagemanager.lua @@ -47,7 +47,7 @@ prosody.events.add_handler("host-activated", initialize_host, 101); function load_driver(host, driver_name) if driver_name == "null" then - return null_storage_provider; + return null_storage_driver; end local driver = stores_available:get(host, driver_name); if driver then return driver; end diff --git a/core/usermanager.lua b/core/usermanager.lua index 9e5a016c..50aee701 100644 --- a/core/usermanager.lua +++ b/core/usermanager.lua @@ -41,7 +41,10 @@ function initialize_host(host) host_session.events.add_handler("item-added/auth-provider", function (event) local provider = event.item; local auth_provider = config.get(host, "core", "authentication") or default_provider; - if config.get(host, "core", "anonymous_login") then auth_provider = "anonymous"; end -- COMPAT 0.7 + if config.get(host, "core", "anonymous_login") then + log("error", "Deprecated config option 'anonymous_login'. Use authentication = 'anonymous' instead."); + auth_provider = "anonymous"; + end -- COMPAT 0.7 if provider.name == auth_provider then host_session.users = setmetatable(provider, provider_mt); end |