Merge 0.10->trunk
authorKim Alvefur <zash@zash.se>
Tue, 19 May 2015 14:33:47 +0000 (16:33 +0200)
committerKim Alvefur <zash@zash.se>
Tue, 19 May 2015 14:33:47 +0000 (16:33 +0200)
core/configmanager.lua
core/hostmanager.lua
core/loggingmanager.lua
plugins/mod_auth_internal_hashed.lua
plugins/mod_tls.lua
prosodyctl
util/interpolation.lua [new file with mode: 0644]
util/x509.lua

index 48f039eac8c3eea55ebe45c64efe23b10cdb93e7..5ee131ad772e921baac1fdb055cce9e3f1b6c94d 100644 (file)
@@ -28,7 +28,7 @@ _M.resolve_relative_path = resolve_relative_path; -- COMPAT
 
 local parsers = {};
 
-local config_mt = { __index = function (t, k) return rawget(t, "*"); end};
+local config_mt = { __index = function (t, _) return rawget(t, "*"); end};
 local config = setmetatable({ ["*"] = { } }, config_mt);
 
 -- When host not found, use global
@@ -54,11 +54,11 @@ function _M.rawget(host, key, _oldkey)
        end
 end
 
-local function set(config, host, key, value)
+local function set(config_table, host, key, value)
        if host and key then
-               local hostconfig = rawget(config, host);
+               local hostconfig = rawget(config_table, host);
                if not hostconfig then
-                       hostconfig = rawset(config, host, setmetatable({}, host_mt))[host];
+                       hostconfig = rawset(config_table, host, setmetatable({}, host_mt))[host];
                end
                hostconfig[key] = value;
                return true;
@@ -73,20 +73,20 @@ function _M.set(host, key, value, _oldvalue)
        return set(config, host, key, value);
 end
 
-function load(filename, format)
-       format = format or filename:match("%w+$");
+function load(filename, config_format)
+       config_format = config_format or filename:match("%w+$");
 
-       if parsers[format] and parsers[format].load then
+       if parsers[config_format] and parsers[config_format].load then
                local f, err = io.open(filename);
                if f then
                        local new_config = setmetatable({ ["*"] = { } }, config_mt);
-                       local ok, err = parsers[format].load(f:read("*a"), filename, new_config);
+                       local ok, err = parsers[config_format].load(f:read("*a"), filename, new_config);
                        f:close();
                        if ok then
                                config = new_config;
                                fire_event("config-reloaded", {
                                        filename = filename,
-                                       format = format,
+                                       format = config_format,
                                        config = config
                                });
                        end
@@ -95,65 +95,61 @@ function load(filename, format)
                return f, "file", err;
        end
 
-       if not format then
+       if not config_format then
                return nil, "file", "no parser specified";
        else
-               return nil, "file", "no parser for "..(format);
+               return nil, "file", "no parser for "..(config_format);
        end
 end
 
-function save(filename, format)
-end
-
-function addparser(format, parser)
-       if format and parser then
-               parsers[format] = parser;
+function addparser(config_format, parser)
+       if config_format and parser then
+               parsers[config_format] = parser;
        end
 end
 
 -- _M needed to avoid name clash with local 'parsers'
 function _M.parsers()
        local p = {};
-       for format in pairs(parsers) do
-               table.insert(p, format);
+       for config_format in pairs(parsers) do
+               table.insert(p, config_format);
        end
        return p;
 end
 
 -- Built-in Lua parser
 do
-       local pcall, setmetatable = _G.pcall, _G.setmetatable;
-       local rawget = _G.rawget;
+       local pcall = _G.pcall;
        parsers.lua = {};
-       function parsers.lua.load(data, config_file, config)
+       function parsers.lua.load(data, config_file, config_table)
                local env;
                -- The ' = true' are needed so as not to set off __newindex when we assign the functions below
                env = setmetatable({
                        Host = true, host = true, VirtualHost = true,
                        Component = true, component = true,
                        Include = true, include = true, RunScript = true }, {
-                               __index = function (t, k)
+                               __index = function (_, k)
                                        return rawget(_G, k);
                                end,
-                               __newindex = function (t, k, v)
-                                       set(config, env.__currenthost or "*", k, v);
+                               __newindex = function (_, k, v)
+                                       set(config_table, env.__currenthost or "*", k, v);
                                end
                });
 
                rawset(env, "__currenthost", "*") -- Default is global
                function env.VirtualHost(name)
                        name = nameprep(name);
-                       if rawget(config, name) and rawget(config[name], "component_module") then
+                       if rawget(config_table, name) and rawget(config_table[name], "component_module") then
                                error(format("Host %q clashes with previously defined %s Component %q, for services use a sub-domain like conference.%s",
-                                       name, config[name].component_module:gsub("^%a+$", { component = "external", muc = "MUC"}), name, name), 0);
+                                       name, config_table[name].component_module:gsub("^%a+$", { component = "external", muc = "MUC"}), name, name), 0);
                        end
                        rawset(env, "__currenthost", name);
                        -- Needs at least one setting to logically exist :)
-                       set(config, name or "*", "defined", true);
+                       set(config_table, name or "*", "defined", true);
                        return function (config_options)
                                rawset(env, "__currenthost", "*"); -- Return to global scope
                                for option_name, option_value in pairs(config_options) do
-                                       set(config, name or "*", option_name, option_value);
+                                       set(config_table, name or "*", option_name, option_value);
                                end
                        end;
                end
@@ -161,24 +157,24 @@ do
 
                function env.Component(name)
                        name = nameprep(name);
-                       if rawget(config, name) and rawget(config[name], "defined") and not rawget(config[name], "component_module") then
+                       if rawget(config_table, name) and rawget(config_table[name], "defined") and not rawget(config_table[name], "component_module") then
                                error(format("Component %q clashes with previously defined Host %q, for services use a sub-domain like conference.%s",
                                        name, name, name), 0);
                        end
-                       set(config, name, "component_module", "component");
+                       set(config_table, name, "component_module", "component");
                        -- Don't load the global modules by default
-                       set(config, name, "load_global_modules", false);
+                       set(config_table, name, "load_global_modules", false);
                        rawset(env, "__currenthost", name);
                        local function handle_config_options(config_options)
                                rawset(env, "__currenthost", "*"); -- Return to global scope
                                for option_name, option_value in pairs(config_options) do
-                                       set(config, name or "*", option_name, option_value);
+                                       set(config_table, name or "*", option_name, option_value);
                                end
                        end
 
                        return function (module)
                                        if type(module) == "string" then
-                                               set(config, name, "component_module", module);
+                                               set(config_table, name, "component_module", module);
                                                return handle_config_options;
                                        end
                                        return handle_config_options(module);
@@ -187,6 +183,7 @@ do
                env.component = env.Component;
 
                function env.Include(file)
+                       -- Check whether this is a wildcard Include
                        if file:match("[*?]") then
                                local lfs = deps.softreq "lfs";
                                if not lfs then
@@ -206,16 +203,17 @@ do
                                                env.Include(path..path_sep..f);
                                        end
                                end
-                       else
-                               local file = resolve_relative_path(config_file:gsub("[^"..path_sep.."]+$", ""), file);
-                               local f, err = io.open(file);
-                               if f then
-                                       local ret, err = parsers.lua.load(f:read("*a"), file, config);
-                                       if not ret then error(err:gsub("%[string.-%]", file), 0); end
-                               end
-                               if not f then error("Error loading included "..file..": "..err, 0); end
-                               return f, err;
+                               return;
+                       end
+                       -- Not a wildcard, so resolve (potentially) relative path and run through config parser
+                       file = resolve_relative_path(config_file:gsub("[^"..path_sep.."]+$", ""), file);
+                       local f, err = io.open(file);
+                       if f then
+                               local ret, err = parsers.lua.load(f:read("*a"), file, config_table);
+                               if not ret then error(err:gsub("%[string.-%]", file), 0); end
                        end
+                       if not f then error("Error loading included "..file..": "..err, 0); end
+                       return f, err;
                end
                env.include = env.Include;
 
index b13b1944497c48569bc89643b26c29e8ac82babf..3d581418fd2017bb121eab1f7e063b67d1adc375 100644 (file)
@@ -26,9 +26,23 @@ local core_route_stanza = _G.prosody.core_route_stanza;
 
 local pairs, select, rawget = pairs, select, rawget;
 local tostring, type = tostring, type;
+local setmetatable = setmetatable;
 
 module "hostmanager"
 
+local host_mt = { }
+function host_mt:__tostring()
+       if self.type == "component" then
+               local typ = configmanager.get(self.host, "component_module");
+               if typ == "component" then
+                       return ("Component %q"):format(self.host);
+               end
+               return ("Component %q %q"):format(self.host, typ);
+       elseif self.type == "local" then
+               return ("VirtualHost %q"):format(self.host);
+       end
+end
+
 local hosts_loaded_once;
 
 local function load_enabled_hosts(config)
@@ -69,6 +83,7 @@ function activate(host, host_config)
                send = host_send;
                modules = {};
        };
+       setmetatable(host_session, host_mt);
        if not host_config.component_module then -- host
                host_session.type = "local";
                host_session.sessions = {};
index f348dbdf1f0632d65f808024e42906dd10d400d7..57ed868775e45fdd1a620b9c6f893f505ea4e104 100644 (file)
@@ -177,8 +177,8 @@ end
 -- Column width for "source" (used by stdout and console)
 local sourcewidth = 20;
 
-function log_sink_types.stdout(config)
-       local timestamps = config.timestamps;
+function log_sink_types.stdout(sink_config)
+       local timestamps = sink_config.timestamps;
 
        if timestamps == true then
                timestamps = default_timestamp; -- Default format
@@ -207,13 +207,13 @@ do
                logstyles["warn"] = getstyle("bold", "yellow");
                logstyles["error"] = getstyle("bold", "red");
        end
-       function log_sink_types.console(config)
+       function log_sink_types.console(sink_config)
                -- Really if we don't want pretty colours then just use plain stdout
                if not do_pretty_printing then
-                       return log_sink_types.stdout(config);
+                       return log_sink_types.stdout(sink_config);
                end
 
-               local timestamps = config.timestamps;
+               local timestamps = sink_config.timestamps;
 
                if timestamps == true then
                        timestamps = default_timestamp; -- Default format
@@ -240,15 +240,15 @@ do
 end
 
 local empty_function = function () end;
-function log_sink_types.file(config)
-       local log = config.filename;
+function log_sink_types.file(sink_config)
+       local log = sink_config.filename;
        local logfile = io_open(log, "a+");
        if not logfile then
                return empty_function;
        end
        local write, flush = logfile.write, logfile.flush;
 
-       local timestamps = config.timestamps;
+       local timestamps = sink_config.timestamps;
 
        if timestamps == nil or timestamps == true then
                timestamps = default_timestamp; -- Default format
index 954392c96a738a8a1b7a10fff0c8dbc2f3ac6981..78abe50d04be08d7747e01435e317c381bdcbd8d 100644 (file)
@@ -13,31 +13,14 @@ local getAuthenticationDatabaseSHA1 = require "util.sasl.scram".getAuthenticatio
 local usermanager = require "core.usermanager";
 local generate_uuid = require "util.uuid".generate;
 local new_sasl = require "util.sasl".new;
+local hex = require"util.hex";
+local to_hex, from_hex = hex.to, hex.from;
 
 local log = module._log;
 local host = module.host;
 
 local accounts = module:open_store("accounts");
 
-local to_hex;
-do
-       local function replace_byte_with_hex(byte)
-               return ("%02x"):format(byte:byte());
-       end
-       function to_hex(binary_string)
-               return binary_string:gsub(".", replace_byte_with_hex);
-       end
-end
-
-local from_hex;
-do
-       local function replace_hex_with_byte(hex)
-               return string.char(tonumber(hex, 16));
-       end
-       function from_hex(hex_string)
-               return hex_string:gsub("..", replace_hex_with_byte);
-       end
-end
 
 
 -- Default; can be set per-user
index f9d2cee99af4c40ea574ca968df7c747e3d741c5..d1138e1c40e5dbf047648d69944f506f9a21dfa6 100644 (file)
@@ -21,6 +21,7 @@ end
 
 local xmlns_starttls = 'urn:ietf:params:xml:ns:xmpp-tls';
 local starttls_attr = { xmlns = xmlns_starttls };
+local starttls_initiate= st.stanza("starttls", starttls_attr);
 local starttls_proceed = st.stanza("proceed", starttls_attr);
 local starttls_failure = st.stanza("failure", starttls_attr);
 local c2s_feature = st.stanza("starttls", starttls_attr);
@@ -60,7 +61,7 @@ do
 end
 
 local function can_do_tls(session)
-       if not session.conn.starttls then
+       if session.ssl_ctx == false or not session.conn.starttls then
                return false;
        elseif session.ssl_ctx then
                return true;
@@ -116,7 +117,7 @@ module:hook_stanza("http://etherx.jabber.org/streams", "features", function (ses
        module:log("debug", "Received features element");
        if can_do_tls(session) and stanza:get_child("starttls", xmlns_starttls) then
                module:log("debug", "%s is offering TLS, taking up the offer...", session.to_host);
-               session.sends2s("<starttls xmlns='"..xmlns_starttls.."'/>");
+               session.sends2s(starttls_initiate);
                return true;
        end
 end, 500);
index ef436106948a2ae316172a3b610cce2db8e7c0e8..6c4b148a927105b37e8eb3a54a6fe61b2907c245 100755 (executable)
@@ -578,6 +578,8 @@ function commands.about(arg)
        print("");
        print("# Lua module versions");
        local module_versions, longest_name = {}, 8;
+       local luaevent =dependencies.softreq"luaevent";
+       local ssl = dependencies.softreq"ssl";
        for name, module in pairs(package.loaded) do
                if type(module) == "table" and rawget(module, "_VERSION")
                and name ~= "_G" and not name:match("%.") then
diff --git a/util/interpolation.lua b/util/interpolation.lua
new file mode 100644 (file)
index 0000000..b78bdfd
--- /dev/null
@@ -0,0 +1,77 @@
+-- Simple template language
+--
+-- The new() function takes a pattern and an escape function and returns 
+-- a render() function.  Both are required.
+--
+-- The function render() takes a string template and a table of values.
+-- Sequences like {name} in the template string are substituted
+-- with values from the table, optionally depending on a modifier
+-- symbol.
+--
+-- Variants are:
+-- {name} is substituted for values["name"] and is escaped using the 
+-- second argument to new_render().  To disable the escaping, use {name!}.
+-- {name.item} can be used to access table items.
+-- To renter lists of items: {name# item number {idx} is {item} }
+-- Or key-value pairs: {name% t[ {idx} ] = {item} }
+-- To show a defaults for missing values {name? sub-template } can be used, 
+-- which renders a sub-template if values["name"] is false-ish.
+-- {name& sub-template } does the opposite, the sub-template is rendered 
+-- if the selected value is anything but false or nil.
+
+local type, tostring = type, tostring;
+local pairs, ipairs = pairs, ipairs;
+local s_sub, s_gsub, s_match = string.sub, string.gsub, string.match;
+local t_concat = table.concat;
+
+local function new_render(pat, escape)
+       -- assert(type(pat) == "string", "bad argument #1 to 'new_render' (string expected)");
+       -- assert(type(escape) == "function", "bad argument #2 to 'new_render' (function expected)");
+       local function render(template, values)
+               -- assert(type(template) == "string", "bad argument #1 to 'render' (string expected)");
+               -- assert(type(values) == "table", "bad argument #2 to 'render' (table expected)");
+               return (s_gsub(template, pat, function (block)
+                       block = s_sub(block, 2, -2);
+                       local name, opt, e = s_match(block, "^([%a_][%w_.]*)(%p?)()");
+                       if not name then return end
+                       local value = values[name];
+                       if not value and name:find(".", 2, true) then
+                               value = values;
+                               for word in name:gmatch"[^.]+" do
+                                       value = value[word];
+                                       if not value then break; end
+                               end
+                       end
+                       if opt == '#' or opt == '%' then
+                               if type(value) ~= "table" then return ""; end
+                               local iter = opt == '#' and ipairs or pairs;
+                               local out, i, subtpl = {}, 1, s_sub(block, e);
+                               local subvalues = setmetatable({}, { __index = values });
+                               for idx, item in iter(value) do
+                                       subvalues.idx = idx;
+                                       subvalues.item = item;
+                                       out[i], i = render(subtpl, subvalues), i+1;
+                               end
+                               return t_concat(out);
+                       elseif opt == '&' then
+                               if not value then return ""; end
+                               return render(s_sub(block, e), values);
+                       elseif opt == '?' and not value then
+                               return render(s_sub(block, e), values);
+                       elseif value ~= nil then
+                               if type(value) ~= "string" then
+                                       value = tostring(value);
+                               end
+                               if opt ~= '!' then
+                                       return escape(value);
+                               end
+                               return value;
+                       end
+               end));
+       end
+       return render;
+end
+
+return {
+       new = new_render;
+};
index 5e1b49e501d3945fcce38593227b62649703d531..bf8d390661da35459434b16cee2b063b7849d8eb 100644 (file)
@@ -148,6 +148,9 @@ local function compare_srvname(host, service, asserted_names)
 end
 
 function verify_identity(host, service, cert)
+       if cert.setencode then
+               cert:setencode("utf8");
+       end
        local ext = cert:extensions()
        if ext[oid_subjectaltname] then
                local sans = ext[oid_subjectaltname];