aboutsummaryrefslogtreecommitdiffstats
path: root/core/modulemanager.lua
diff options
context:
space:
mode:
Diffstat (limited to 'core/modulemanager.lua')
-rw-r--r--core/modulemanager.lua120
1 files changed, 107 insertions, 13 deletions
diff --git a/core/modulemanager.lua b/core/modulemanager.lua
index a824d36a..0ddf175b 100644
--- a/core/modulemanager.lua
+++ b/core/modulemanager.lua
@@ -6,12 +6,16 @@
-- COPYING file in the source package for more information.
--
+local array = require "util.array";
local logger = require "util.logger";
local log = logger.init("modulemanager");
local config = require "core.configmanager";
local pluginloader = require "util.pluginloader";
+local envload = require "util.envload";
local set = require "util.set";
+local core_features = require "core.features".available;
+
local new_multitable = require "util.multitable".new;
local api = require "core.moduleapi"; -- Module API container
@@ -22,9 +26,28 @@ local xpcall = require "util.xpcall".xpcall;
local debug_traceback = debug.traceback;
local setmetatable, rawget = setmetatable, rawget;
local ipairs, pairs, type, t_insert = ipairs, pairs, type, table.insert;
-
-local autoload_modules = {prosody.platform, "presence", "message", "iq", "offline", "c2s", "s2s", "s2s_auth_certs"};
-local component_inheritable_modules = {"tls", "saslauth", "dialback", "iq", "s2s"};
+local lua_version = _VERSION:match("5%.%d$");
+
+local autoload_modules = {
+ prosody.platform,
+ "presence",
+ "message",
+ "iq",
+ "offline",
+ "c2s",
+ "s2s",
+ "s2s_auth_certs",
+};
+local component_inheritable_modules = {
+ "tls",
+ "saslauth",
+ "dialback",
+ "iq",
+ "s2s",
+ "s2s_bidi",
+ "smacks",
+ "server_contact_info",
+};
-- We need this to let modules access the real global namespace
local _G = _G;
@@ -32,6 +55,38 @@ local _G = _G;
local _ENV = nil;
-- luacheck: std none
+local loader = pluginloader.init({
+ load_filter_cb = function (path, content)
+ local metadata = {};
+ for line in content:gmatch("([^\r\n]+)\r?\n") do
+ local key, value = line:match("^%-%-%% *([%w_]+): *(.+)$");
+ if key then
+ value = value:gsub("%s+$", "");
+ metadata[key] = value;
+ end
+ end
+
+ if metadata.conflicts then
+ local conflicts_features = set.new(array.collect(metadata.conflicts:gmatch("[^, ]+")));
+ local conflicted_features = set.intersection(conflicts_features, core_features);
+ if not conflicted_features:empty() then
+ log("warn", "Not loading module, due to conflicting features '%s': %s", conflicted_features, path);
+ return; -- Don't load this module
+ end
+ end
+ if metadata.requires then
+ local required_features = set.new(array.collect(metadata.requires:gmatch("[^, ]+")));
+ local missing_features = required_features - core_features;
+ if not missing_features:empty() then
+ log("warn", "Not loading module, due to missing features '%s': %s", missing_features, path);
+ return; -- Don't load this module
+ end
+ end
+
+ return path, content, metadata;
+ end;
+});
+
local load_modules_for_host, load, unload, reload, get_module, get_items;
local get_modules, is_loaded, module_has_method, call_module_method;
@@ -56,13 +111,6 @@ local function get_modules_for_host(host)
end
local modules = (global_modules + set.new(host_modules_enabled)) - set.new(host_modules_disabled);
- -- COMPAT w/ pre 0.8
- if modules:contains("console") then
- log("error", "The mod_console plugin has been renamed to mod_admin_telnet. Please update your config.");
- modules:remove("console");
- modules:add("admin_telnet");
- end
-
if modules:contains("vcard") and modules:contains("vcard_legacy") then
log("error", "The mod_vcard_legacy plugin replaces mod_vcard but both are enabled. Please update your config.");
modules:remove("vcard");
@@ -141,6 +189,7 @@ local function do_load_module(host, module_name, state)
if module_has_method(mod, "add_host") then
local _log = logger.init(host..":"..module_name);
local host_module_api = setmetatable({
+ global = false,
host = host, event_handlers = new_multitable(), items = {};
_log = _log, log = function (self, ...) return _log(...); end; --luacheck: ignore 212/self
},{
@@ -171,13 +220,51 @@ local function do_load_module(host, module_name, state)
local pluginenv = setmetatable({ module = api_instance }, { __index = _G });
api_instance.environment = pluginenv;
- local mod, err = pluginloader.load_code(module_name, nil, pluginenv);
+ local mod, err, meta = loader:load_code(module_name, nil, pluginenv);
if not mod then
log("error", "Unable to load module '%s': %s", module_name or "nil", err or "nil");
+ api_instance:set_status("error", "Failed to load (see log)");
return nil, err;
end
api_instance.path = err;
+ api_instance.meta = meta;
+
+ local custom_plugins = prosody.paths.installer;
+ if custom_plugins and err:sub(1, #custom_plugins+1) == custom_plugins.."/" then
+ -- Stage 1: Make it work (you are here)
+ -- Stage 2: Make it less hacky (TODO)
+ local manifest = {};
+ local luarocks_path = custom_plugins.."/lib/luarocks/rocks-"..lua_version;
+ local manifest_filename = luarocks_path.."/manifest";
+ local load_manifest, err = envload.envloadfile(manifest_filename, manifest);
+ if not load_manifest then
+ -- COMPAT Luarocks 2.x
+ log("debug", "Could not load LuaRocks 3.x manifest, trying 2.x", err);
+ luarocks_path = custom_plugins.."/lib/luarocks/rocks";
+ manifest_filename = luarocks_path.."/manifest";
+ load_manifest, err = envload.envloadfile(manifest_filename, manifest);
+ end
+ if not load_manifest then
+ log("error", "Could not load manifest of installed plugins: %s", err, load_manifest);
+ else
+ local ok, err = xpcall(load_manifest, debug_traceback);
+ if not ok then
+ log("error", "Could not load manifest of installed plugins: %s", err);
+ elseif type(manifest.modules) ~= "table" then
+ log("debug", "Expected 'table' but manifest.modules = %q", manifest.modules);
+ log("error", "Can't look up resource path for mod_%s because '%s' does not appear to be a LuaRocks manifest", module_name, manifest_filename);
+ else
+ local versions = manifest.modules["mod_"..module_name];
+ if type(versions) == "table" and versions[1] then
+ -- Not going to deal with multiple installed versions
+ api_instance.resource_path = luarocks_path.."/"..versions[1];
+ else
+ log("debug", "mod_%s does not appear in the installation manifest", module_name);
+ end
+ end
+ end
+ end
modulemap[host][module_name] = pluginenv;
local ok, err = xpcall(mod, debug_traceback);
@@ -187,6 +274,7 @@ local function do_load_module(host, module_name, state)
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");
+ api_instance:set_status("warn", "Error during load (see log)");
end
end
api_instance.reloading, api_instance.saved_state = nil, nil;
@@ -209,6 +297,9 @@ local function do_load_module(host, module_name, state)
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");
+ api_instance:set_status("warn", "Error during load (see log)");
+ else
+ api_instance:set_status("core", "Loaded", false);
end
return ok and pluginenv, err;
end
@@ -217,7 +308,7 @@ local function do_reload_module(host, name)
local mod = get_module(host, name);
if not mod then return nil, "module-not-loaded"; end
- local _mod, err = pluginloader.load_code(name); -- checking for syntax errors
+ local _mod, err = loader:load_code(name); -- checking for syntax errors
if not _mod then
log("error", "Unable to load module '%s': %s", name or "nil", err or "nil");
return nil, err;
@@ -225,7 +316,8 @@ local function do_reload_module(host, name)
local saved;
if module_has_method(mod, "save") then
- local ok, ret, err = call_module_method(mod, "save");
+ -- FIXME What goes in 'err' here?
+ local ok, ret, err = call_module_method(mod, "save"); -- luacheck: ignore 211/err
if ok then
saved = ret;
else
@@ -340,4 +432,6 @@ return {
is_loaded = is_loaded;
module_has_method = module_has_method;
call_module_method = call_module_method;
+
+ loader = loader;
};