From 83ab43ca8ff3d3e93042f76b91194f2ec3c6e36f Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 28 Nov 2018 20:36:53 +0100 Subject: util.format: Tweak how nil values are handled Because [] seems exsessive --- util/format.lua | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'util') diff --git a/util/format.lua b/util/format.lua index c5e513fa..16c57bc6 100644 --- a/util/format.lua +++ b/util/format.lua @@ -28,13 +28,12 @@ local function format(formatstring, ...) if spec ~= "%%" then i = i + 1; local arg = args[i]; - if arg == nil then -- special handling for nil - arg = "" - args[i] = ""; - end local option = spec:sub(-1); - if option == "q" or option == "s" then -- arg should be string + if arg == nil then + args[i] = "nil"; + spec = "<%s>"; + elseif option == "q" or option == "s" then -- arg should be string args[i] = tostring(arg); elseif type(arg) ~= "number" then -- arg isn't number as expected? args[i] = tostring(arg); -- cgit v1.2.3 From 755f1d8050c373af7abf578ac30067f1d141c93f Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 8 Dec 2018 16:35:39 +0100 Subject: util.format: Use pack from util.table --- util/format.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/format.lua b/util/format.lua index 16c57bc6..6c46384a 100644 --- a/util/format.lua +++ b/util/format.lua @@ -3,12 +3,13 @@ -- local tostring = tostring; -local select = select; local unpack = table.unpack or unpack; -- luacheck: ignore 113/unpack +local pack = require "util.table".pack; -- TODO table.pack in 5.2+ local type = type; local function format(formatstring, ...) - local args, args_length = { ... }, select('#', ...); + local args = pack(...); + local args_length = args.n; -- format specifier spec: -- 1. Start: '%%' -- cgit v1.2.3 From 08cb0002476600be61921a3a2a15bc9626e052e5 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 8 Dec 2018 16:36:05 +0100 Subject: util.iterators: Use pack from table.pack --- util/iterators.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'util') diff --git a/util/iterators.lua b/util/iterators.lua index 302cca36..c03c2fd6 100644 --- a/util/iterators.lua +++ b/util/iterators.lua @@ -11,9 +11,9 @@ local it = {}; local t_insert = table.insert; -local select, next = select, next; -local unpack = table.unpack or unpack; --luacheck: ignore 113 143 -local pack = table.pack or function (...) return { n = select("#", ...), ... }; end -- luacheck: ignore 143 +local next = next; +local unpack = table.unpack or unpack; --luacheck: ignore 113 +local pack = table.pack or require "util.table".pack; local type = type; local table, setmetatable = table, setmetatable; -- cgit v1.2.3 From a8b096516b2ef1791bc348e18baa7e7f5399de8f Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 8 Dec 2018 17:10:51 +0100 Subject: lint: Remove use of the 143 error code Does not appear to be invoked by anything --- util/import.lua | 2 +- util/multitable.lua | 2 +- util/serialization.lua | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) (limited to 'util') diff --git a/util/import.lua b/util/import.lua index 8ecfe43c..1007bc0a 100644 --- a/util/import.lua +++ b/util/import.lua @@ -8,7 +8,7 @@ -local unpack = table.unpack or unpack; --luacheck: ignore 113 143 +local unpack = table.unpack or unpack; --luacheck: ignore 113 local t_insert = table.insert; function _G.import(module, ...) local m = package.loaded[module] or require(module); diff --git a/util/multitable.lua b/util/multitable.lua index 8d32ed8a..4f2cd972 100644 --- a/util/multitable.lua +++ b/util/multitable.lua @@ -9,7 +9,7 @@ local select = select; local t_insert = table.insert; local pairs, next, type = pairs, next, type; -local unpack = table.unpack or unpack; --luacheck: ignore 113 143 +local unpack = table.unpack or unpack; --luacheck: ignore 113 local _ENV = nil; -- luacheck: std none diff --git a/util/serialization.lua b/util/serialization.lua index dd6a2a2b..7ae77a3a 100644 --- a/util/serialization.lua +++ b/util/serialization.lua @@ -20,7 +20,6 @@ local pcall = pcall; local envload = require"util.envload".envload; local pos_inf, neg_inf = math.huge, -math.huge; --- luacheck: ignore 143/math local m_type = math.type or function (n) return n % 1 == 0 and n <= 9007199254740992 and n >= -9007199254740992 and "integer" or "float"; end; -- cgit v1.2.3 From c0b8a6ef63746675fbe1441bceed47f6fb2bee88 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 12 Oct 2018 01:29:34 +0200 Subject: util.format: Serialize values for the %q format Improves eg debug logs --- util/format.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/format.lua b/util/format.lua index 6c46384a..c31f599f 100644 --- a/util/format.lua +++ b/util/format.lua @@ -6,6 +6,7 @@ local tostring = tostring; local unpack = table.unpack or unpack; -- luacheck: ignore 113/unpack local pack = require "util.table".pack; -- TODO table.pack in 5.2+ local type = type; +local dump = require "util.serialization".new("debug"); local function format(formatstring, ...) local args = pack(...); @@ -34,7 +35,10 @@ local function format(formatstring, ...) if arg == nil then args[i] = "nil"; spec = "<%s>"; - elseif option == "q" or option == "s" then -- arg should be string + elseif option == "q" then + args[i] = dump(arg); + spec = "%s"; + elseif option == "s" then args[i] = tostring(arg); elseif type(arg) ~= "number" then -- arg isn't number as expected? args[i] = tostring(arg); -- cgit v1.2.3 From b250ff0d70b614a5ee0025fc68f175428bfca4b5 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 28 Dec 2018 20:49:01 +0100 Subject: util.stanza: Require a type attribute for iq stanzas --- util/stanza.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/stanza.lua b/util/stanza.lua index a90d56b3..e9847ca6 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -423,9 +423,15 @@ local function message(attr, body) end end local function iq(attr) - if not (attr and attr.id) then + if not attr then + error("iq stanzas require id and type attributes"); + end + if not attr.id then error("iq stanzas require an id attribute"); end + if not attr.type then + error("iq stanzas require a type attribute"); + end return new_stanza("iq", attr); end -- cgit v1.2.3 From 93c5ebb743337c82103ea94166065106a9f5b641 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 30 Dec 2018 03:24:54 +0100 Subject: util.promise: Remove references to callbacks after settling promise This is to help the garbage collector. --- util/promise.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util') diff --git a/util/promise.lua b/util/promise.lua index 07c9c4dc..0b182b54 100644 --- a/util/promise.lua +++ b/util/promise.lua @@ -49,6 +49,9 @@ local function promise_settle(promise, new_state, new_next, cbs, value) for _, cb in ipairs(cbs) do cb(value); end + -- No need to keep references to callbacks + promise._pending_on_fulfilled = nil; + promise._pending_on_rejected = nil; return true; end -- cgit v1.2.3 From 38462e35420ca9e93af018210be940353b32d508 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 30 Dec 2018 12:55:58 +0000 Subject: util.error: Add new util library for structured errors --- util/error.lua | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 util/error.lua (limited to 'util') diff --git a/util/error.lua b/util/error.lua new file mode 100644 index 00000000..ed61793f --- /dev/null +++ b/util/error.lua @@ -0,0 +1,40 @@ +local error_mt = { __name = "error" }; + +function error_mt:__tostring() + return ("error<%s:%s:%s>"):format(self.type, self.condition, self.text); +end + +local function is_err(e) + return getmetatable(e) == error_mt; +end + +local function new(e, context, registry) + local template = (registry and registry[e]) or e or {}; + return setmetatable({ + type = template.type or "cancel"; + condition = template.condition or "undefined-condition"; + text = template.text; + + context = context or template.context or { _error_id = e }; + }, error_mt); +end + +local function coerce(ok, err, ...) + if ok or is_err(err) then + return ok, err, ...; + end + + local new_err = setmetatable({ + native = err; + + type = "cancel"; + condition = "undefined-condition"; + }, error_mt); + return ok, new_err, ...; +end + +return { + new = new; + coerce = coerce; + is_err = is_err; +} -- cgit v1.2.3 From ac2d2297b62ba2973c49d7d5bee3927cef64ea97 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 30 Dec 2018 20:30:59 +0100 Subject: util.error: Add a function for creating an error object from an error stanza --- util/error.lua | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'util') diff --git a/util/error.lua b/util/error.lua index ed61793f..344dd274 100644 --- a/util/error.lua +++ b/util/error.lua @@ -33,8 +33,20 @@ local function coerce(ok, err, ...) return ok, new_err, ...; end +local function from_stanza(stanza, context) + local error_type, condition, text = stanza:get_error(); + return setmetatable({ + type = error_type or "cancel"; + condition = condition or "undefined-condition"; + text = text; + + context = context or { stanza = stanza }; + }, error_mt); +end + return { new = new; coerce = coerce; is_err = is_err; + from_stanza = from_stanza; } -- cgit v1.2.3 From 60213520c2a27bf0472a0ef5a2cf16640134a1a8 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 6 Jan 2019 10:39:33 +0100 Subject: util.http: Pre-generate urlencoding mappings (optimization) Function calls are more expensive than table lookups --- util/http.lua | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'util') diff --git a/util/http.lua b/util/http.lua index cfb89193..1730d4d4 100644 --- a/util/http.lua +++ b/util/http.lua @@ -6,24 +6,25 @@ -- local format, char = string.format, string.char; -local pairs, ipairs, tonumber = pairs, ipairs, tonumber; +local pairs, ipairs = pairs, ipairs; local t_insert, t_concat = table.insert, table.concat; +local url_codes = {}; +for i = 0, 255 do + local c = char(i); + local u = format("%%%02x", i); + url_codes[c] = u; + url_codes[u] = c; +end local function urlencode(s) - return s and (s:gsub("[^a-zA-Z0-9.~_-]", function (c) return format("%%%02x", c:byte()); end)); + return s and (s:gsub("[^a-zA-Z0-9.~_-]", url_codes)); end local function urldecode(s) - return s and (s:gsub("%%(%x%x)", function (c) return char(tonumber(c,16)); end)); + return s and (s:gsub("%%%x%x", url_codes)); end local function _formencodepart(s) - return s and (s:gsub("%W", function (c) - if c ~= " " then - return format("%%%02x", c:byte()); - else - return "+"; - end - end)); + return s and (urlencode(s):gsub("%%20", "+")); end local function formencode(form) -- cgit v1.2.3 From 1a3edc0ab921f131c7304e92d7402ef88ff465ef Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 10 Jan 2019 14:57:26 +0100 Subject: util.prosodyctl: Allow passing path to Lua runtime to the start() function By default the shebang is used. Being able to override it is useful in cases where the shebang does not match the configured runtime. --- util/prosodyctl.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'util') diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua index 5f0c4d12..9b627bde 100644 --- a/util/prosodyctl.lua +++ b/util/prosodyctl.lua @@ -229,7 +229,8 @@ local function isrunning() return true, signal.kill(pid, 0) == 0; end -local function start(source_dir) +local function start(source_dir, lua) + lua = lua and lua .. " " or ""; local ok, ret = isrunning(); if not ok then return ok, ret; @@ -238,9 +239,9 @@ local function start(source_dir) return false, "already-running"; end if not source_dir then - os.execute("./prosody"); + os.execute(lua .. "./prosody"); else - os.execute(source_dir.."/../../bin/prosody"); + os.execute(lua .. source_dir.."/../../bin/prosody"); end return true; end -- cgit v1.2.3 From 7ae9fe492d2e616d94b77c280a2f5e0f2c744075 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 16 Jan 2019 13:53:04 +0100 Subject: util.http: Fix decoding of uppercase URL encoded chars Broken in 1af5106a2c34 --- util/http.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'util') diff --git a/util/http.lua b/util/http.lua index 1730d4d4..3852f91c 100644 --- a/util/http.lua +++ b/util/http.lua @@ -15,6 +15,7 @@ for i = 0, 255 do local u = format("%%%02x", i); url_codes[c] = u; url_codes[u] = c; + url_codes[u:upper()] = c; end local function urlencode(s) return s and (s:gsub("[^a-zA-Z0-9.~_-]", url_codes)); -- cgit v1.2.3 From 8dbde146973dbdf875e1a5fc5a455218426f454c Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 17 Mar 2019 20:40:01 +0100 Subject: util.serialization: Optimize handling of last table separator Fewer next() calls and a step towards allowing use of a different iterator. --- util/serialization.lua | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'util') diff --git a/util/serialization.lua b/util/serialization.lua index 7ae77a3a..c64bfec1 100644 --- a/util/serialization.lua +++ b/util/serialization.lua @@ -163,7 +163,9 @@ local function new(opt) local indent = s_rep(indentwith, d); local numkey = 1; local ktyp, vtyp; + local had_items = false; for k,v in next,t do + had_items = true; o[l], l = itemstart, l + 1; o[l], l = indent, l + 1; ktyp, vtyp = type(k), type(v); @@ -194,14 +196,10 @@ local function new(opt) else o[l], l = ser(v), l + 1; end - -- last item? - if next(t, k) ~= nil then - o[l], l = itemsep, l + 1; - else - o[l], l = itemlast, l + 1; - end + o[l], l = itemsep, l + 1; end - if next(t) ~= nil then + if had_items then + o[l - 1] = itemlast; o[l], l = s_rep(indentwith, d-1), l + 1; end o[l], l = tend, l +1; -- cgit v1.2.3 From a335b6de27f7da7df46ec7a4091b6d1ea8ba2fba Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 17 Mar 2019 21:16:27 +0100 Subject: util.serialization: Allow overriding table iterator Could be useful to eg swap it out with sorted_pairs to get a stable serialization. Default to next() wrapper to avoid metatable tricks from pairs(). --- util/serialization.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/serialization.lua b/util/serialization.lua index c64bfec1..2ead8c12 100644 --- a/util/serialization.lua +++ b/util/serialization.lua @@ -33,6 +33,10 @@ local function to_hex(s) return (s_gsub(s, ".", char_to_hex)); end +local function rawpairs(t) + return next, t, nil; +end + local function fatal_error(obj, why) error("Can't serialize "..type(obj) .. (why and ": ".. why or "")); end @@ -122,6 +126,7 @@ local function new(opt) local freeze = opt.freeze; local maxdepth = opt.maxdepth or 127; local multirefs = opt.multiref; + local table_pairs = opt.table_iterator or rawpairs; -- serialize one table, recursively -- t - table being serialized @@ -164,7 +169,7 @@ local function new(opt) local numkey = 1; local ktyp, vtyp; local had_items = false; - for k,v in next,t do + for k,v in table_pairs(t) do had_items = true; o[l], l = itemstart, l + 1; o[l], l = indent, l + 1; -- cgit v1.2.3 From 8999b2a97c8d5e0d9dcf10d7692f928287d6bc5e Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 17 Mar 2019 21:25:33 +0100 Subject: util.serialization: Use util.hex --- util/serialization.lua | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'util') diff --git a/util/serialization.lua b/util/serialization.lua index 2ead8c12..60e341cf 100644 --- a/util/serialization.lua +++ b/util/serialization.lua @@ -16,6 +16,8 @@ local s_char = string.char; local s_match = string.match; local t_concat = table.concat; +local to_hex = require "util.hex".to; + local pcall = pcall; local envload = require"util.envload".envload; @@ -24,15 +26,6 @@ local m_type = math.type or function (n) return n % 1 == 0 and n <= 9007199254740992 and n >= -9007199254740992 and "integer" or "float"; end; -local char_to_hex = {}; -for i = 0,255 do - char_to_hex[s_char(i)] = s_format("%02x", i); -end - -local function to_hex(s) - return (s_gsub(s, ".", char_to_hex)); -end - local function rawpairs(t) return next, t, nil; end -- cgit v1.2.3 From 67b08fccf7d370c1df426890057d2775edd49797 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 20 Mar 2019 12:18:34 +0000 Subject: util.startup: Give function a more generic name so it can apply to all warnings --- util/startup.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'util') diff --git a/util/startup.lua b/util/startup.lua index c101c290..4d3c6e4e 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -96,7 +96,7 @@ function startup.init_logging() end); end -function startup.log_dependency_warnings() +function startup.log_startup_warnings() dependencies.log_warnings(); end @@ -518,7 +518,7 @@ function startup.prosodyctl() startup.read_version(); startup.switch_user(); startup.check_dependencies(); - startup.log_dependency_warnings(); + startup.log_startup_warnings(); startup.check_unwriteable(); startup.load_libraries(); startup.init_http_client(); @@ -543,7 +543,7 @@ function startup.prosody() startup.add_global_prosody_functions(); startup.read_version(); startup.log_greeting(); - startup.log_dependency_warnings(); + startup.log_startup_warnings(); startup.load_secondary_libraries(); startup.init_http_client(); startup.init_data_store(); -- cgit v1.2.3 From 798be44f563b8764dad50e893f4b25268b42e5a8 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 20 Mar 2019 12:45:58 +0000 Subject: util.startup: Log configuration warnings at startup --- util/startup.lua | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'util') diff --git a/util/startup.lua b/util/startup.lua index 4d3c6e4e..966f2934 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -7,6 +7,7 @@ local logger = require "util.logger"; local log = logger.init("startup"); local config = require "core.configmanager"; +local config_warnings; local dependencies = require "util.dependencies"; @@ -64,6 +65,8 @@ function startup.read_config() print("**************************"); print(""); os.exit(1); + elseif err and #err > 0 then + config_warnings = err; end prosody.config_loaded = true; end @@ -98,6 +101,9 @@ end function startup.log_startup_warnings() dependencies.log_warnings(); + for _, warning in ipairs(config_warnings) do + log("warn", "Configuration warning: %s", warning); + end end function startup.sanity_check() -- cgit v1.2.3 From c77d416cfffd6604cc4d9a633f9f7941f6fd5f10 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 20 Mar 2019 13:44:29 +0000 Subject: util.startup: Don't die if there are no config warnings to log (thanks buildbot) --- util/startup.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/startup.lua b/util/startup.lua index 966f2934..7a1a95aa 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -101,8 +101,10 @@ end function startup.log_startup_warnings() dependencies.log_warnings(); - for _, warning in ipairs(config_warnings) do - log("warn", "Configuration warning: %s", warning); + if config_warnings then + for _, warning in ipairs(config_warnings) do + log("warn", "Configuration warning: %s", warning); + end end end -- cgit v1.2.3 From 2080433daf5a956d0456c6ce24ce1ef9183a023e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 23 Mar 2019 08:47:55 +0000 Subject: util.queue: Add 'consume()' convenience iterator --- util/queue.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util') diff --git a/util/queue.lua b/util/queue.lua index 728e905f..e63b3f1c 100644 --- a/util/queue.lua +++ b/util/queue.lua @@ -64,6 +64,9 @@ local function new(size, allow_wrapping) return pos+1, t._items[read_pos]; end, self, 0; end; + consume = function (self) + return self.pop, self; + end; }; end -- cgit v1.2.3 From bd52e2269b0fff09fc8aea6c6807a9f08727bb31 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 23 Mar 2019 08:52:57 +0000 Subject: util.queue: Update :items() to consistently use private data directly It will perform better this way, and we were accessing private variables already within the iterator. --- util/queue.lua | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'util') diff --git a/util/queue.lua b/util/queue.lua index e63b3f1c..66ed098b 100644 --- a/util/queue.lua +++ b/util/queue.lua @@ -52,16 +52,15 @@ local function new(size, allow_wrapping) return t[tail]; end; items = function (self) - --luacheck: ignore 431/t - return function (t, pos) - if pos >= t:count() then + return function (_, pos) + if pos >= items then return nil; end local read_pos = tail + pos; - if read_pos > t.size then + if read_pos > self.size then read_pos = (read_pos%size); end - return pos+1, t._items[read_pos]; + return pos+1, t[read_pos]; end, self, 0; end; consume = function (self) -- cgit v1.2.3 From ff3904d881514abc83443b02e10cd621873bed64 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 4 Jan 2019 10:20:51 +0100 Subject: util.x509: Add function that extracts usable names from a certificate --- util/x509.lua | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'util') diff --git a/util/x509.lua b/util/x509.lua index 15cc4d3c..1cdf07dc 100644 --- a/util/x509.lua +++ b/util/x509.lua @@ -20,6 +20,7 @@ local nameprep = require "util.encodings".stringprep.nameprep; local idna_to_ascii = require "util.encodings".idna.to_ascii; +local idna_to_unicode = require "util.encodings".idna.to_unicode; local base64 = require "util.encodings".base64; local log = require "util.logger".init("x509"); local s_format = string.format; @@ -216,6 +217,32 @@ local function verify_identity(host, service, cert) return false end +-- TODO Support other SANs +local function get_identities(cert) --> set of names + if cert.setencode then + cert:setencode("utf8"); + end + + local names = {}; + + local ext = cert:extensions(); + local sans = ext[oid_subjectaltname]; + if sans and sans["dNSName"] then + for i = 1, #sans["dNSName"] do + names[ idna_to_unicode(sans["dNSName"][i]) ] = true; + end + end + + local subject = cert:subject(); + for i = 1, #subject do + local dn = subject[i]; + if dn.oid == oid_commonname and nameprep(dn.value) then + names[dn.value] = true; + end + end + return names; +end + local pat = "%-%-%-%-%-BEGIN ([A-Z ]+)%-%-%-%-%-\r?\n".. "([0-9A-Za-z+/=\r\n]*)\r?\n%-%-%-%-%-END %1%-%-%-%-%-"; @@ -237,6 +264,7 @@ end return { verify_identity = verify_identity; + get_identities = get_identities; pem2der = pem2der; der2pem = der2pem; }; -- cgit v1.2.3 From e9eca8cd5521f2bfc8f9285b606eb84de5a467e4 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 25 Mar 2019 14:37:43 +0000 Subject: util.stanza: Fix :top_tag() handling of namespaced attributes --- util/stanza.lua | 62 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 30 insertions(+), 32 deletions(-) (limited to 'util') diff --git a/util/stanza.lua b/util/stanza.lua index e9847ca6..7fe5c7ae 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -270,6 +270,34 @@ function stanza_mt:find(path) until not self end +local function _clone(stanza, only_top) + local attr, tags = {}, {}; + for k,v in pairs(stanza.attr) do attr[k] = v; end + local old_namespaces, namespaces = stanza.namespaces; + if old_namespaces then + namespaces = {}; + for k,v in pairs(old_namespaces) do namespaces[k] = v; end + end + local new = { name = stanza.name, attr = attr, namespaces = namespaces, tags = tags }; + if not only_top then + for i=1,#stanza do + local child = stanza[i]; + if child.name then + child = _clone(child); + t_insert(tags, child); + end + t_insert(new, child); + end + end + return setmetatable(new, stanza_mt); +end + +local function clone(stanza, only_top) + if not is_stanza(stanza) then + error("bad argument to clone: expected stanza, got "..type(stanza)); + end + return _clone(stanza, only_top); +end local escape_table = { ["'"] = "'", ["\""] = """, ["<"] = "<", [">"] = ">", ["&"] = "&" }; local function xml_escape(str) return (s_gsub(str, "['&<>\"]", escape_table)); end @@ -310,11 +338,8 @@ function stanza_mt.__tostring(t) end function stanza_mt.top_tag(t) - local attr_string = ""; - if t.attr then - for k, v in pairs(t.attr) do if type(k) == "string" then attr_string = attr_string .. s_format(" %s='%s'", k, xml_escape(tostring(v))); end end - end - return s_format("<%s%s>", t.name, attr_string); + local top_tag_clone = clone(t, true); + return tostring(top_tag_clone):sub(1,-3)..">"; end function stanza_mt.get_text(t) @@ -388,33 +413,6 @@ local function deserialize(serialized) end end -local function _clone(stanza) - local attr, tags = {}, {}; - for k,v in pairs(stanza.attr) do attr[k] = v; end - local old_namespaces, namespaces = stanza.namespaces; - if old_namespaces then - namespaces = {}; - for k,v in pairs(old_namespaces) do namespaces[k] = v; end - end - local new = { name = stanza.name, attr = attr, namespaces = namespaces, tags = tags }; - for i=1,#stanza do - local child = stanza[i]; - if child.name then - child = _clone(child); - t_insert(tags, child); - end - t_insert(new, child); - end - return setmetatable(new, stanza_mt); -end - -local function clone(stanza) - if not is_stanza(stanza) then - error("bad argument to clone: expected stanza, got "..type(stanza)); - end - return _clone(stanza); -end - local function message(attr, body) if not body then return new_stanza("message", attr); -- cgit v1.2.3 From 08ec4600470e32bbc4bb0afdfce9012f6a8cb5e9 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 26 Mar 2019 13:51:06 +0000 Subject: Backed out changeset 3eea63a68e0f Commit included intended changes to loggingmanager --- util/queue.lua | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'util') diff --git a/util/queue.lua b/util/queue.lua index 66ed098b..e63b3f1c 100644 --- a/util/queue.lua +++ b/util/queue.lua @@ -52,15 +52,16 @@ local function new(size, allow_wrapping) return t[tail]; end; items = function (self) - return function (_, pos) - if pos >= items then + --luacheck: ignore 431/t + return function (t, pos) + if pos >= t:count() then return nil; end local read_pos = tail + pos; - if read_pos > self.size then + if read_pos > t.size then read_pos = (read_pos%size); end - return pos+1, t[read_pos]; + return pos+1, t._items[read_pos]; end, self, 0; end; consume = function (self) -- cgit v1.2.3 From 8e20f176e04a261129c9d49705b72f9460999145 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 26 Mar 2019 13:54:14 +0000 Subject: util.queue: Update :items() to consistently use private data directly It will perform better this way, and we were accessing private variables already within the iterator. Replaces 3eea63a68e0f --- util/queue.lua | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'util') diff --git a/util/queue.lua b/util/queue.lua index e63b3f1c..66ed098b 100644 --- a/util/queue.lua +++ b/util/queue.lua @@ -52,16 +52,15 @@ local function new(size, allow_wrapping) return t[tail]; end; items = function (self) - --luacheck: ignore 431/t - return function (t, pos) - if pos >= t:count() then + return function (_, pos) + if pos >= items then return nil; end local read_pos = tail + pos; - if read_pos > t.size then + if read_pos > self.size then read_pos = (read_pos%size); end - return pos+1, t._items[read_pos]; + return pos+1, t[read_pos]; end, self, 0; end; consume = function (self) -- cgit v1.2.3 From c0e30a99fc9b49847577598541328ebe1d7eaaba Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 3 Apr 2019 17:20:57 +0200 Subject: util.session: Fix session id not include unauthed forever --- util/session.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/session.lua b/util/session.lua index b2a726ce..b9c6bec7 100644 --- a/util/session.lua +++ b/util/session.lua @@ -4,12 +4,13 @@ local logger = require "util.logger"; local function new_session(typ) local session = { type = typ .. "_unauthed"; + base_type = typ; }; return session; end local function set_id(session) - local id = session.type .. tostring(session):match("%x+$"):lower(); + local id = session.base_type .. tostring(session):match("%x+$"):lower(); session.id = id; return session; end -- cgit v1.2.3 From 8e2e187ff819b98ebae24f2a5ebd9994a2d41c2f Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 19 Apr 2019 12:46:24 +0200 Subject: util.hmac: Reflow code --- util/hmac.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'util') diff --git a/util/hmac.lua b/util/hmac.lua index 2c4cc6ef..f3caae33 100644 --- a/util/hmac.lua +++ b/util/hmac.lua @@ -10,6 +10,8 @@ local hashes = require "util.hashes" -return { md5 = hashes.hmac_md5, - sha1 = hashes.hmac_sha1, - sha256 = hashes.hmac_sha256 }; +return { + md5 = hashes.hmac_md5, + sha1 = hashes.hmac_sha1, + sha256 = hashes.hmac_sha256, +}; -- cgit v1.2.3 From 1d11df9a67d5f650d1ff4404ba7d6668df99875e Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 19 Apr 2019 12:47:49 +0200 Subject: util.hmac: Expose hmac-sha-512 too All these are provided by util.hashes so why not? --- util/hmac.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'util') diff --git a/util/hmac.lua b/util/hmac.lua index f3caae33..4cad17cc 100644 --- a/util/hmac.lua +++ b/util/hmac.lua @@ -14,4 +14,5 @@ return { md5 = hashes.hmac_md5, sha1 = hashes.hmac_sha1, sha256 = hashes.hmac_sha256, + sha512 = hashes.hmac_sha512, }; -- cgit v1.2.3 From dae2e97303535d5f3c42e763a1a809ef340c2eb0 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 10 Jul 2019 17:04:36 +0200 Subject: util.error: Fix traceback due to missing text field --- util/error.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/error.lua b/util/error.lua index 344dd274..23551fe2 100644 --- a/util/error.lua +++ b/util/error.lua @@ -1,7 +1,7 @@ local error_mt = { __name = "error" }; function error_mt:__tostring() - return ("error<%s:%s:%s>"):format(self.type, self.condition, self.text); + return ("error<%s:%s:%s>"):format(self.type, self.condition, self.text or ""); end local function is_err(e) -- cgit v1.2.3 From 0ecfe1d6f19acc2f17e93b42cf86f9c6ed019229 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 28 Jul 2019 01:17:44 +0200 Subject: util.xmppstream: Inherit xml:lang from stream to stanzas (fixes #1401) --- util/xmppstream.lua | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'util') diff --git a/util/xmppstream.lua b/util/xmppstream.lua index 58cbd18e..6aa1def3 100644 --- a/util/xmppstream.lua +++ b/util/xmppstream.lua @@ -64,6 +64,8 @@ local function new_sax_handlers(session, stream_callbacks, cb_handleprogress) local stream_default_ns = stream_callbacks.default_ns; + local stream_lang = "en"; + local stack = {}; local chardata, stanza = {}; local stanza_size = 0; @@ -101,6 +103,7 @@ local function new_sax_handlers(session, stream_callbacks, cb_handleprogress) if session.notopen then if tagname == stream_tag then non_streamns_depth = 0; + stream_lang = attr["xml:lang"] or stream_lang; if cb_streamopened then if lxp_supports_bytecount then cb_handleprogress(stanza_size); @@ -178,6 +181,9 @@ local function new_sax_handlers(session, stream_callbacks, cb_handleprogress) cb_handleprogress(stanza_size); end stanza_size = 0; + if stanza.attr["xml:lang"] == nil then + stanza.attr["xml:lang"] = stream_lang; + end if tagname ~= stream_error_tag then cb_handlestanza(session, stanza); else -- cgit v1.2.3 From 8dba7528d5d580e965a4a44bd590cf5259564446 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 30 Jul 2019 02:23:06 +0200 Subject: util.startup: Remove tostring call from logging Taken care of by loggingmanager now --- util/startup.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/startup.lua b/util/startup.lua index 7a1a95aa..ab595526 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -251,9 +251,9 @@ function startup.add_global_prosody_functions() local ok, level, err = config.load(prosody.config_file); if not ok then if level == "parser" then - log("error", "There was an error parsing the configuration file: %s", tostring(err)); + log("error", "There was an error parsing the configuration file: %s", err); elseif level == "file" then - log("error", "Couldn't read the config file when trying to reload: %s", tostring(err)); + log("error", "Couldn't read the config file when trying to reload: %s", err); end else prosody.events.fire_event("config-reloaded", { -- cgit v1.2.3 From e57ef38b90335796e0d6f571c55fccbe33a3fe17 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 30 Jul 2019 02:23:44 +0200 Subject: util.sql: Remove tostring call from logging Taken care of by loggingmanager now --- util/sql.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'util') diff --git a/util/sql.lua b/util/sql.lua index 00c7b57f..4406d7ff 100644 --- a/util/sql.lua +++ b/util/sql.lua @@ -201,18 +201,18 @@ function engine:_transaction(func, ...) if not ok then return ok, err; end end --assert(not self.__transaction, "Recursive transactions not allowed"); - log("debug", "SQL transaction begin [%s]", tostring(func)); + log("debug", "SQL transaction begin [%s]", func); self.__transaction = true; local success, a, b, c = xpcall(func, handleerr, ...); self.__transaction = nil; if success then - log("debug", "SQL transaction success [%s]", tostring(func)); + log("debug", "SQL transaction success [%s]", func); local ok, err = self.conn:commit(); -- LuaDBI doesn't actually return an error message here, just a boolean if not ok then return ok, err or "commit failed"; end return success, a, b, c; else - log("debug", "SQL transaction failure [%s]: %s", tostring(func), a.err); + log("debug", "SQL transaction failure [%s]: %s", func, a.err); if self.conn then self.conn:rollback(); end return success, a.err; end @@ -224,7 +224,7 @@ function engine:transaction(...) if not conn or not conn:ping() then log("debug", "Database connection was closed. Will reconnect and retry."); self.conn = nil; - log("debug", "Retrying SQL transaction [%s]", tostring((...))); + log("debug", "Retrying SQL transaction [%s]", (...)); ok, ret = self:_transaction(...); log("debug", "SQL transaction retry %s", ok and "succeeded" or "failed"); else -- cgit v1.2.3 From 73fab2dc0d581b9744b611d97650cbe09ac6aaca Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 30 Jul 2019 02:24:06 +0200 Subject: util.session: Remove tostring call from logging Taken care of by loggingmanager now --- util/session.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/session.lua b/util/session.lua index b9c6bec7..25b22faf 100644 --- a/util/session.lua +++ b/util/session.lua @@ -31,7 +31,7 @@ local function set_send(session) local conn = session.conn; if not conn then function session.send(data) - session.log("debug", "Discarding data sent to unconnected session: %s", tostring(data)); + session.log("debug", "Discarding data sent to unconnected session: %s", data); return false; end return session; @@ -47,7 +47,7 @@ local function set_send(session) if t then local ret, err = w(conn, t); if not ret then - session.log("debug", "Error writing to connection: %s", tostring(err)); + session.log("debug", "Error writing to connection: %s", err); return false, err; end end -- cgit v1.2.3 From 2134cff478362f60eeb43c0f30d0297ea9100ef9 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 2 Aug 2019 08:56:29 +0200 Subject: util.stanza: Use :text_tag internally everywhere May allow future changes in a single place. --- util/stanza.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'util') diff --git a/util/stanza.lua b/util/stanza.lua index 7fe5c7ae..55c38c73 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -98,7 +98,7 @@ function stanza_mt:query(xmlns) end function stanza_mt:body(text, attr) - return self:tag("body", attr):text(text); + return self:text_tag("body", text, attr); end function stanza_mt:text_tag(name, text, attr, namespaces) @@ -417,7 +417,7 @@ local function message(attr, body) if not body then return new_stanza("message", attr); else - return new_stanza("message", attr):tag("body"):text(body):up(); + return new_stanza("message", attr):text_tag("body", body); end end local function iq(attr) @@ -449,7 +449,7 @@ local function error_reply(orig, error_type, condition, error_message) t.attr.type = "error"; t:tag("error", {type = error_type}) --COMPAT: Some day xmlns:stanzas goes here :tag(condition, xmpp_stanzas_attr):up(); - if error_message then t:tag("text", xmpp_stanzas_attr):text(error_message):up(); end + if error_message then t:text_tag("text", error_message, xmpp_stanzas_attr); end return t; -- stanza ready for adding app-specific errors end -- cgit v1.2.3 From 690c6a4eb240ede63cf7a31007c25df29fa123f2 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 3 May 2019 20:54:24 +0200 Subject: Fix various spelling mistakes [codespell] --- util/datamanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/datamanager.lua b/util/datamanager.lua index cf96887b..b52c77fa 100644 --- a/util/datamanager.lua +++ b/util/datamanager.lua @@ -24,7 +24,7 @@ local t_concat = table.concat; local envloadfile = require"util.envload".envloadfile; local serialize = require "util.serialization".serialize; local lfs = require "lfs"; --- Extract directory seperator from package.config (an undocumented string that comes with lua) +-- Extract directory separator from package.config (an undocumented string that comes with lua) local path_separator = assert ( package.config:match ( "^([^\n]+)" ) , "package.config not in standard form" ) local prosody = prosody; -- cgit v1.2.3 From b61b308c569dda7ca5e214466503e9cc7ea4a7e7 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 13 May 2019 10:03:46 +0100 Subject: util.hashring: Implementation of hashring data structure --- util/hashring.lua | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 util/hashring.lua (limited to 'util') diff --git a/util/hashring.lua b/util/hashring.lua new file mode 100644 index 00000000..322bc005 --- /dev/null +++ b/util/hashring.lua @@ -0,0 +1,88 @@ +local function generate_ring(nodes, num_replicas, hash) + local new_ring = {}; + for _, node_name in ipairs(nodes) do + for replica = 1, num_replicas do + local replica_hash = hash(node_name..":"..replica); + new_ring[replica_hash] = node_name; + table.insert(new_ring, replica_hash); + end + end + table.sort(new_ring); + return new_ring; +end + +local hashring_methods = {}; +local hashring_mt = { + __index = function (self, k) + -- Automatically build self.ring if it's missing + if k == "ring" then + local ring = generate_ring(self.nodes, self.num_replicas, self.hash); + rawset(self, "ring", ring); + return ring; + end + return rawget(hashring_methods, k); + end +}; + +local function new(num_replicas, hash_function) + return setmetatable({ nodes = {}, num_replicas = num_replicas, hash = hash_function }, hashring_mt); +end; + +function hashring_methods:add_node(name) + self.ring = nil; + self.nodes[name] = true; + table.insert(self.nodes, name); + return true; +end + +function hashring_methods:add_nodes(nodes) + self.ring = nil; + for _, node_name in ipairs(nodes) do + if not self.nodes[node_name] then + self.nodes[node_name] = true; + table.insert(self.nodes, node_name); + end + end + return true; +end + +function hashring_methods:remove_node(node_name) + self.ring = nil; + if self.nodes[node_name] then + for i, stored_node_name in ipairs(self.nodes) do + if node_name == stored_node_name then + self.nodes[node_name] = nil; + table.remove(self.nodes, i); + return true; + end + end + end + return false; +end + +function hashring_methods:remove_nodes(nodes) + self.ring = nil; + for _, node_name in ipairs(nodes) do + self:remove_node(node_name); + end +end + +function hashring_methods:clone() + local clone_hashring = new(self.num_replicas, self.hash); + clone_hashring:add_nodes(self.nodes); + return clone_hashring; +end + +function hashring_methods:get_node(key) + local key_hash = self.hash(key); + for _, replica_hash in ipairs(self.ring) do + if key_hash < replica_hash then + return self.ring[replica_hash]; + end + end + return self.ring[self.ring[1]]; +end + +return { + new = new; +} -- cgit v1.2.3 From be249b1a12e22c3276598555bedf7beb8386f550 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 30 May 2019 13:41:05 +0200 Subject: util.format: Handle formats expecting an integer in Lua 5.3+ (fixes #1371) --- util/format.lua | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'util') diff --git a/util/format.lua b/util/format.lua index c31f599f..857bb694 100644 --- a/util/format.lua +++ b/util/format.lua @@ -7,6 +7,9 @@ local unpack = table.unpack or unpack; -- luacheck: ignore 113/unpack local pack = require "util.table".pack; -- TODO table.pack in 5.2+ local type = type; local dump = require "util.serialization".new("debug"); +local num_type = math.type; + +local expects_integer = num_type and { c = true, d = true, i = true, o = true, u = true, X = true, x = true, } or {}; local function format(formatstring, ...) local args = pack(...); @@ -43,6 +46,9 @@ local function format(formatstring, ...) elseif type(arg) ~= "number" then -- arg isn't number as expected? args[i] = tostring(arg); spec = "[%s]"; + elseif expects_integer[option] and num_type(arg) ~= "integer" then + args[i] = tostring(arg); + spec = "[%s]"; end end return spec; -- cgit v1.2.3 From 9991f8892518b2d48cf27b3667a2fe33d25dcd67 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 30 May 2019 13:54:11 +0200 Subject: util.format: Handle integer formats the same way on Lua versions without integer support --- util/format.lua | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/format.lua b/util/format.lua index 857bb694..1ce670f3 100644 --- a/util/format.lua +++ b/util/format.lua @@ -7,9 +7,12 @@ local unpack = table.unpack or unpack; -- luacheck: ignore 113/unpack local pack = require "util.table".pack; -- TODO table.pack in 5.2+ local type = type; local dump = require "util.serialization".new("debug"); -local num_type = math.type; +local num_type = math.type or function (n) + return n % 1 == 0 and n <= 9007199254740992 and n >= -9007199254740992 and "integer" or "float"; +end -local expects_integer = num_type and { c = true, d = true, i = true, o = true, u = true, X = true, x = true, } or {}; +-- In Lua 5.3+ these formats throw an error if given a float +local expects_integer = { c = true, d = true, i = true, o = true, u = true, X = true, x = true, }; local function format(formatstring, ...) local args = pack(...); -- cgit v1.2.3 From 4e7d17bfff9ff33d5c0b58cae1941aac45090b70 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 19 Jun 2019 19:16:09 +0200 Subject: util.dependencies: Increase Lua version to warn about to 5.4 No significant problems have been encountered with Lua 5.3 itself, so apart from some odd problems in LuaExpat it seems about time to declare it ready. --- util/dependencies.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/dependencies.lua b/util/dependencies.lua index 7c7b938e..84e2dd5c 100644 --- a/util/dependencies.lua +++ b/util/dependencies.lua @@ -140,7 +140,7 @@ local function check_dependencies() end local function log_warnings() - if _VERSION > "Lua 5.2" then + if _VERSION > "Lua 5.3" then prosody.log("warn", "Support for %s is experimental, please report any issues", _VERSION); end local ssl = softreq"ssl"; -- cgit v1.2.3 From 4af3c3997c55fc0e1afc6cac160a7a0919b7e954 Mon Sep 17 00:00:00 2001 From: Jo?o Duarte Date: Tue, 25 Jun 2019 13:20:54 +0100 Subject: util.prosodyctl: Moved the 'admin_operation' function from prosodyctl to here --- util/prosodyctl.lua | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'util') diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua index 9b627bde..e2bc4369 100644 --- a/util/prosodyctl.lua +++ b/util/prosodyctl.lua @@ -278,6 +278,22 @@ local function reload() return true; end +local function admin_operation(operation, arg) + local modules, tree, server, refresh = "", "", "", "" + for i, _ in ipairs(arg) do + if arg[i]:sub(1, #"--tree=") == "--tree=" then + tree = arg[i].." " + elseif arg[i]:sub(1, #"--server=") == "--server=" then + server = arg[i].." " + elseif arg[i]:sub(1, #"--no-refresh") == "--no-refresh" then + refresh = arg[i].." " + else + modules=modules..arg[i].." " + end + end + os.execute("luarocks-admin "..tree..operation..server..refresh..modules) +end + return { show_message = show_message; show_warning = show_message; @@ -297,4 +313,5 @@ return { start = start; stop = stop; reload = reload; + admin_operation = admin_operation; }; -- cgit v1.2.3 From e5971f6e72429dfb5f0df4f52cd23b60a286da4d Mon Sep 17 00:00:00 2001 From: Jo?o Duarte Date: Thu, 27 Jun 2019 18:01:36 +0100 Subject: util.prosodyctl: Added help support to 'admin_operation' --- util/prosodyctl.lua | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'util') diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua index e2bc4369..c2235e85 100644 --- a/util/prosodyctl.lua +++ b/util/prosodyctl.lua @@ -279,6 +279,11 @@ local function reload() end local function admin_operation(operation, arg) + if arg[1] == "--help" then + print(" admin-"..operation) + print(" "..operation.."plugins from a server (repository)") + return 1; + end local modules, tree, server, refresh = "", "", "", "" for i, _ in ipairs(arg) do if arg[i]:sub(1, #"--tree=") == "--tree=" then -- cgit v1.2.3 From f55bf2aace5ee3389366f0c5ef800d0ae197a8e3 Mon Sep 17 00:00:00 2001 From: Jo?o Duarte Date: Sat, 20 Jul 2019 12:41:31 -0700 Subject: util.prosodyctl: Corrected indentation on my code --- util/prosodyctl.lua | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'util') diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua index c2235e85..6f7bf065 100644 --- a/util/prosodyctl.lua +++ b/util/prosodyctl.lua @@ -279,24 +279,24 @@ local function reload() end local function admin_operation(operation, arg) - if arg[1] == "--help" then - print(" admin-"..operation) - print(" "..operation.."plugins from a server (repository)") - return 1; - end - local modules, tree, server, refresh = "", "", "", "" - for i, _ in ipairs(arg) do - if arg[i]:sub(1, #"--tree=") == "--tree=" then - tree = arg[i].." " - elseif arg[i]:sub(1, #"--server=") == "--server=" then - server = arg[i].." " - elseif arg[i]:sub(1, #"--no-refresh") == "--no-refresh" then - refresh = arg[i].." " - else - modules=modules..arg[i].." " - end - end - os.execute("luarocks-admin "..tree..operation..server..refresh..modules) + if arg[1] == "--help" then + print(" admin-"..operation) + print(" "..operation.."plugins from a server (repository)") + return 1; + end + local modules, tree, server, refresh = "", "", "", "" + for i, _ in ipairs(arg) do + if arg[i]:sub(1, #"--tree=") == "--tree=" then + tree = arg[i].." " + elseif arg[i]:sub(1, #"--server=") == "--server=" then + server = arg[i].." " + elseif arg[i]:sub(1, #"--no-refresh") == "--no-refresh" then + refresh = arg[i].." " + else + modules=modules..arg[i].." " + end + end + os.execute("luarocks-admin "..tree..operation..server..refresh..modules) end return { -- cgit v1.2.3 From 86016bed9b3d17207b4e29b6b3fe3c700cf16abb Mon Sep 17 00:00:00 2001 From: Jo?o Duarte Date: Tue, 23 Jul 2019 10:24:55 -0700 Subject: util.prosodyctl: Added the show_module_configuration_help function --- util/prosodyctl.lua | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'util') diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua index 6f7bf065..84f65665 100644 --- a/util/prosodyctl.lua +++ b/util/prosodyctl.lua @@ -39,6 +39,16 @@ local function show_usage(usage, desc) end end +local function show_module_configuration_help(mod_name) + print("Done.") + print("If you installed a prosody plugin, don't forget to add its name under the 'modules_enabled' section inside your configuration file.") + print("Depending on the module, there might be further configuration steps required.") + print("") + print("More info about: ") + print(" modules_enabled: https://prosody.im/doc/modules_enabled") + print(" "..mod_name..": https://modules.prosody.im/"..mod_name..".html") +end + local function getchar(n) local stty_ret = os.execute("stty raw -echo 2>/dev/null"); local ok, char; @@ -303,6 +313,7 @@ return { show_message = show_message; show_warning = show_message; show_usage = show_usage; + show_module_configuration_help = show_module_configuration_help; getchar = getchar; getline = getline; getpass = getpass; -- cgit v1.2.3 From f89d5e01b0ddb4cde58073440da999d61f46a4d7 Mon Sep 17 00:00:00 2001 From: Jo?o Duarte Date: Wed, 24 Jul 2019 04:44:44 -0700 Subject: util.prosodyctl: Removed the admin_operation command --- util/prosodyctl.lua | 22 ---------------------- 1 file changed, 22 deletions(-) (limited to 'util') diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua index 84f65665..5e19937e 100644 --- a/util/prosodyctl.lua +++ b/util/prosodyctl.lua @@ -288,27 +288,6 @@ local function reload() return true; end -local function admin_operation(operation, arg) - if arg[1] == "--help" then - print(" admin-"..operation) - print(" "..operation.."plugins from a server (repository)") - return 1; - end - local modules, tree, server, refresh = "", "", "", "" - for i, _ in ipairs(arg) do - if arg[i]:sub(1, #"--tree=") == "--tree=" then - tree = arg[i].." " - elseif arg[i]:sub(1, #"--server=") == "--server=" then - server = arg[i].." " - elseif arg[i]:sub(1, #"--no-refresh") == "--no-refresh" then - refresh = arg[i].." " - else - modules=modules..arg[i].." " - end - end - os.execute("luarocks-admin "..tree..operation..server..refresh..modules) -end - return { show_message = show_message; show_warning = show_message; @@ -329,5 +308,4 @@ return { start = start; stop = stop; reload = reload; - admin_operation = admin_operation; }; -- cgit v1.2.3 From 8a516777e10421846c59c3fce74d7899fcbdeed2 Mon Sep 17 00:00:00 2001 From: Jo?o Duarte Date: Wed, 24 Jul 2019 10:37:01 -0700 Subject: util.startup: Now it also loads default or configured paths to custom plugin directories and creates them --- util/startup.lua | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'util') diff --git a/util/startup.lua b/util/startup.lua index 7a1a95aa..65a131fe 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -227,6 +227,7 @@ end function startup.setup_plugindir() local custom_plugin_paths = config.get("*", "plugin_paths"); + local installer_plugin_paths = config.get("*", "installer_plugin_paths") or {"custom_plugins"}; if custom_plugin_paths then local path_sep = package.config:sub(3,3); -- path1;path2;path3;defaultpath... @@ -234,6 +235,17 @@ function startup.setup_plugindir() CFG_PLUGINDIR = table.concat(custom_plugin_paths, path_sep)..path_sep..(CFG_PLUGINDIR or "plugins"); prosody.paths.plugins = CFG_PLUGINDIR; end + if installer_plugin_paths then + for path, _ in ipairs(installer_plugin_paths) do + if os.execute('[ -d "'..installer_plugin_paths[path]..'" ]') ~= 0 then + os.execute("mkdir "..installer_plugin_paths[path]) + end + end + local path_sep = package.config:sub(3,3); + -- luacheck: ignore 111 + CFG_PLUGINDIR = table.concat(installer_plugin_paths, path_sep)..path_sep..(CFG_PLUGINDIR or "plugins"); + prosody.paths.plugins = CFG_PLUGINDIR; + end end function startup.chdir() -- cgit v1.2.3 From 5fa62c1c2a1d7ac5d17526cfdf1d1d6faa8288b2 Mon Sep 17 00:00:00 2001 From: Jo?o Duarte Date: Wed, 24 Jul 2019 11:20:35 -0700 Subject: util.prosodyctl: Created the helper function get_path_custom_plugins --- util/prosodyctl.lua | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'util') diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua index 5e19937e..fbeb9540 100644 --- a/util/prosodyctl.lua +++ b/util/prosodyctl.lua @@ -288,6 +288,14 @@ local function reload() return true; end +local function get_path_custom_plugins(plugin_paths) + -- I'm considering that we are using just one path to custom plugins, and it is the first in prosody.paths.plugins, for now + -- luacheck: ignore 512 + for path in plugin_paths:gmatch("[^;]+") do + return path + end +end + return { show_message = show_message; show_warning = show_message; @@ -308,4 +316,5 @@ return { start = start; stop = stop; reload = reload; + get_path_custom_plugins = get_path_custom_plugins; }; -- cgit v1.2.3 From a0aa32c752425c9abcd0e565722818ea73ddaf90 Mon Sep 17 00:00:00 2001 From: Jo?o Duarte Date: Thu, 25 Jul 2019 06:46:04 -0700 Subject: util.startup: Removed unnecessary if clause at startup.set_plugindir --- util/startup.lua | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'util') diff --git a/util/startup.lua b/util/startup.lua index 65a131fe..54b968f8 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -235,17 +235,15 @@ function startup.setup_plugindir() CFG_PLUGINDIR = table.concat(custom_plugin_paths, path_sep)..path_sep..(CFG_PLUGINDIR or "plugins"); prosody.paths.plugins = CFG_PLUGINDIR; end - if installer_plugin_paths then - for path, _ in ipairs(installer_plugin_paths) do - if os.execute('[ -d "'..installer_plugin_paths[path]..'" ]') ~= 0 then - os.execute("mkdir "..installer_plugin_paths[path]) - end + for path, _ in ipairs(installer_plugin_paths) do + if os.execute('[ -d "'..installer_plugin_paths[path]..'" ]') ~= 0 then + os.execute("mkdir "..installer_plugin_paths[path]) end - local path_sep = package.config:sub(3,3); - -- luacheck: ignore 111 - CFG_PLUGINDIR = table.concat(installer_plugin_paths, path_sep)..path_sep..(CFG_PLUGINDIR or "plugins"); - prosody.paths.plugins = CFG_PLUGINDIR; end + local path_sep = package.config:sub(3,3); + -- luacheck: ignore 111 + CFG_PLUGINDIR = table.concat(installer_plugin_paths, path_sep)..path_sep..(CFG_PLUGINDIR or "plugins"); + prosody.paths.plugins = CFG_PLUGINDIR; end function startup.chdir() -- cgit v1.2.3 From b29dab1c1c6185c310c6346910aa7e0c8ed72596 Mon Sep 17 00:00:00 2001 From: Jo?o Duarte Date: Fri, 26 Jul 2019 08:39:27 -0700 Subject: util.startup: The .setup_plugindir function now correctly sets a default/specified path for custom plugins --- util/startup.lua | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) (limited to 'util') diff --git a/util/startup.lua b/util/startup.lua index 54b968f8..363d8465 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -226,23 +226,29 @@ function startup.setup_datadir() end function startup.setup_plugindir() + --require "lfs".currentdir() + --local current_directory = lfs.currentdir() local custom_plugin_paths = config.get("*", "plugin_paths"); - local installer_plugin_paths = config.get("*", "installer_plugin_paths") or {"custom_plugins"}; + local installer_plugin_path = config.get("*", "installer_plugin_path") or "custom_plugins"; + local path_sep = package.config:sub(3,3); if custom_plugin_paths then - local path_sep = package.config:sub(3,3); -- path1;path2;path3;defaultpath... -- luacheck: ignore 111 CFG_PLUGINDIR = table.concat(custom_plugin_paths, path_sep)..path_sep..(CFG_PLUGINDIR or "plugins"); prosody.paths.plugins = CFG_PLUGINDIR; end - for path, _ in ipairs(installer_plugin_paths) do - if os.execute('[ -d "'..installer_plugin_paths[path]..'" ]') ~= 0 then - os.execute("mkdir "..installer_plugin_paths[path]) - end - end - local path_sep = package.config:sub(3,3); - -- luacheck: ignore 111 - CFG_PLUGINDIR = table.concat(installer_plugin_paths, path_sep)..path_sep..(CFG_PLUGINDIR or "plugins"); + -- Checking if the folder exists. If it doesn't, we create it + --[[if os.execute('[ -d "'..installer_plugin_path..'" ]') ~= 0 then + os.execute("mkdir "..installer_plugin_path) + end]] + --[[if not string.find(package.path, current_directory..installer_plugin_path[path]) then + --os.execute("ls -la "..current_directory..path_sep..installer_plugin_paths[path]) + package.path = package.path..path_sep..current_directory..installer_plugin_path.."/?.lua"..path_sep..path_sep + package.path = package.path..current_directory..installer_plugin_path.."/?/init.lua"..path_sep..path_sep + package.cpath = package.cpath..path_sep..current_directory..installer_plugin_path.."/?.lua" + package.cpath = package.cpath..path_sep..current_directory..installer_plugin_path.."/?/init.lua" + end]] + CFG_PLUGINDIR = installer_plugin_path..path_sep..(CFG_PLUGINDIR or "plugins"); prosody.paths.plugins = CFG_PLUGINDIR; end -- cgit v1.2.3 From aa6a7d9238f2288a2fbca2e19f3ab855d0d43d95 Mon Sep 17 00:00:00 2001 From: Jo?o Duarte Date: Fri, 26 Jul 2019 08:58:56 -0700 Subject: util.startup: .setup_plugindir now checks if the specified directory for custom plugins exists, and creates it if it doesn't --- util/startup.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'util') diff --git a/util/startup.lua b/util/startup.lua index 363d8465..9ddb10b5 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -226,8 +226,8 @@ function startup.setup_datadir() end function startup.setup_plugindir() - --require "lfs".currentdir() - --local current_directory = lfs.currentdir() + --local lfs_currentdir = require "lfs".currentdir() + --local current_directory = lfs_currentdir local custom_plugin_paths = config.get("*", "plugin_paths"); local installer_plugin_path = config.get("*", "installer_plugin_path") or "custom_plugins"; local path_sep = package.config:sub(3,3); @@ -238,9 +238,9 @@ function startup.setup_plugindir() prosody.paths.plugins = CFG_PLUGINDIR; end -- Checking if the folder exists. If it doesn't, we create it - --[[if os.execute('[ -d "'..installer_plugin_path..'" ]') ~= 0 then + if os.execute('[ -d "'..installer_plugin_path..'" ]') ~= 0 then os.execute("mkdir "..installer_plugin_path) - end]] + end --[[if not string.find(package.path, current_directory..installer_plugin_path[path]) then --os.execute("ls -la "..current_directory..path_sep..installer_plugin_paths[path]) package.path = package.path..path_sep..current_directory..installer_plugin_path.."/?.lua"..path_sep..path_sep -- cgit v1.2.3 From 899ab776e55395a68e94462376f3bd7b852b8d66 Mon Sep 17 00:00:00 2001 From: Jo?o Duarte Date: Fri, 26 Jul 2019 17:54:37 -0700 Subject: util.startup: Improved how .set_plugindir updates prosody.paths.plugins, package.path and package.cpath --- util/startup.lua | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) (limited to 'util') diff --git a/util/startup.lua b/util/startup.lua index 9ddb10b5..4601bd85 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -226,28 +226,44 @@ function startup.setup_datadir() end function startup.setup_plugindir() - --local lfs_currentdir = require "lfs".currentdir() - --local current_directory = lfs_currentdir local custom_plugin_paths = config.get("*", "plugin_paths"); local installer_plugin_path = config.get("*", "installer_plugin_path") or "custom_plugins"; + -- This variable separates different paths, like this "," here -> /usr;/home local path_sep = package.config:sub(3,3); + -- This variable is the separator between directories, in a path, like the "/" here -> /home/path/to/somewhere + local dir_sep = package.config:sub(1,1); if custom_plugin_paths then -- path1;path2;path3;defaultpath... -- luacheck: ignore 111 CFG_PLUGINDIR = table.concat(custom_plugin_paths, path_sep)..path_sep..(CFG_PLUGINDIR or "plugins"); prosody.paths.plugins = CFG_PLUGINDIR; end - -- Checking if the folder exists. If it doesn't, we create it + -- Checks if installer_plugin_path is a relative paths and makes it an absolute path + if installer_plugin_path:sub(1,1) ~= "/" then + -- Works fine when executing prosody from source (configure and make only) + -- Probably wont be the best install directory, when using a package installation + local lfs_currentdir = require "lfs".currentdir(); + local current_directory = lfs_currentdir; + -- Some normalization + installer_plugin_path = installer_plugin_path:gsub("^%.%"..dir_sep.."+", ""); + installer_plugin_path = current_directory..dir_sep..installer_plugin_path; + end + -- Checking if the folder exists. If it doesn't, we create it, but we need permissions to do so if os.execute('[ -d "'..installer_plugin_path..'" ]') ~= 0 then - os.execute("mkdir "..installer_plugin_path) + os.execute("mkdir "..installer_plugin_path); + end + -- Developers may have add these custom paths to their LUA_PATH/LUA_CPATH variables, before running prosody + -- Therefore, I'll just check if the paths we are about to add aren't already at package.(path/cpath) + if not string.match(package.path, installer_plugin_path) then + local lua_version = _VERSION:match(" (.+)$") + -- I'm assuming there's good reason not to hard code any separator + -- This next line is unnecessary, but I think it makes the code more readable and neat + local sub_path = dir_sep.."lua"..dir_sep..lua_version..dir_sep + package.path = package.path..path_sep..installer_plugin_path..dir_sep.."share"..sub_path.."?.lua"; + package.path = package.path..path_sep..installer_plugin_path..dir_sep.."share"..sub_path.."?"..dir_sep.."init.lua"; + package.cpath = package.cpath..path_sep..installer_plugin_path..dir_sep.."lib"..sub_path.."?.lua"; end - --[[if not string.find(package.path, current_directory..installer_plugin_path[path]) then - --os.execute("ls -la "..current_directory..path_sep..installer_plugin_paths[path]) - package.path = package.path..path_sep..current_directory..installer_plugin_path.."/?.lua"..path_sep..path_sep - package.path = package.path..current_directory..installer_plugin_path.."/?/init.lua"..path_sep..path_sep - package.cpath = package.cpath..path_sep..current_directory..installer_plugin_path.."/?.lua" - package.cpath = package.cpath..path_sep..current_directory..installer_plugin_path.."/?/init.lua" - end]] + -- The commands using luarocks need the path to the directory that has the /share and /lib folders. CFG_PLUGINDIR = installer_plugin_path..path_sep..(CFG_PLUGINDIR or "plugins"); prosody.paths.plugins = CFG_PLUGINDIR; end -- cgit v1.2.3 From ebb62190fb2ec32435be0074f84532d248f463f3 Mon Sep 17 00:00:00 2001 From: Jo?o Duarte Date: Wed, 31 Jul 2019 06:42:13 -0700 Subject: util.prosodyctl: Added the check_flags function --- util/prosodyctl.lua | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua index fbeb9540..affa2f6b 100644 --- a/util/prosodyctl.lua +++ b/util/prosodyctl.lua @@ -292,10 +292,20 @@ local function get_path_custom_plugins(plugin_paths) -- I'm considering that we are using just one path to custom plugins, and it is the first in prosody.paths.plugins, for now -- luacheck: ignore 512 for path in plugin_paths:gmatch("[^;]+") do - return path + return path; end end +local function check_flags(arg) + local flag = "--tree="; + -- There might not be any argument when the list command is calling this function + if arg[1] and arg[1]:sub(1, #flag) == flag then + local dir = arg[1]:match("=(.+)$") + return true, arg[2], dir; + end + return false, arg[1] +end + return { show_message = show_message; show_warning = show_message; @@ -317,4 +327,5 @@ return { stop = stop; reload = reload; get_path_custom_plugins = get_path_custom_plugins; + check_flags = check_flags; }; -- cgit v1.2.3 From 62e70b82a16a91f7964a52b2fe15b8c8ffbafa4c Mon Sep 17 00:00:00 2001 From: Jo?o Duarte Date: Wed, 31 Jul 2019 07:24:43 -0700 Subject: util.prosodyctl: Added the call_luarocks function --- util/prosodyctl.lua | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'util') diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua index affa2f6b..22571061 100644 --- a/util/prosodyctl.lua +++ b/util/prosodyctl.lua @@ -306,6 +306,12 @@ local function check_flags(arg) return false, arg[1] end +local function call_luarocks(operation, mod, dir) + show_message("Installing %s at %s", mod, dir); + os.execute("luarocks --tree='"..dir.."' --server='http://localhost/' "..operation.." "..mod); + show_module_configuration_help(mod); +end + return { show_message = show_message; show_warning = show_message; @@ -328,4 +334,5 @@ return { reload = reload; get_path_custom_plugins = get_path_custom_plugins; check_flags = check_flags; + call_luarocks = call_luarocks; }; -- cgit v1.2.3 From 03d787c0aca4c2267a1535cfc7bbb25372a5d349 Mon Sep 17 00:00:00 2001 From: Jo?o Duarte Date: Wed, 31 Jul 2019 10:02:42 -0700 Subject: util.prosodyctl: Function now differentiates its output, depending if it is being called by install or remove --- util/prosodyctl.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua index 22571061..a98a89d7 100644 --- a/util/prosodyctl.lua +++ b/util/prosodyctl.lua @@ -307,9 +307,15 @@ local function check_flags(arg) end local function call_luarocks(operation, mod, dir) + if operation == "install" then show_message("Installing %s at %s", mod, dir); - os.execute("luarocks --tree='"..dir.."' --server='http://localhost/' "..operation.." "..mod); + elseif operation == "remove" then + show_message("Removing %s from %s", mod, dir); + end + os.execute("luarocks --tree='"..dir.."' --server='http://localhost/' "..operation.." "..mod); + if operation == "install" then show_module_configuration_help(mod); + end end return { -- cgit v1.2.3 From 46f373b49bdec7e65ed48e54dc92dabc11d9ec17 Mon Sep 17 00:00:00 2001 From: Jo?o Duarte Date: Wed, 31 Jul 2019 10:22:11 -0700 Subject: util.prosodyctl: call_luarocks function is now compatible with the list command --- util/prosodyctl.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua index a98a89d7..7fe87dab 100644 --- a/util/prosodyctl.lua +++ b/util/prosodyctl.lua @@ -312,7 +312,11 @@ local function call_luarocks(operation, mod, dir) elseif operation == "remove" then show_message("Removing %s from %s", mod, dir); end - os.execute("luarocks --tree='"..dir.."' --server='http://localhost/' "..operation.." "..mod); + if operation == "list" then + os.execute("luarocks list --tree='"..dir.."'") + else + os.execute("luarocks --tree='"..dir.."' --server='http://localhost/' "..operation.." "..mod); + end if operation == "install" then show_module_configuration_help(mod); end -- cgit v1.2.3 From e956f52972ff4ae03afe866037d8559a77817032 Mon Sep 17 00:00:00 2001 From: Jo?o Duarte Date: Wed, 31 Jul 2019 11:00:20 -0700 Subject: util.prosodyctl: Added the execute_command function --- util/prosodyctl.lua | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'util') diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua index 7fe87dab..274d611c 100644 --- a/util/prosodyctl.lua +++ b/util/prosodyctl.lua @@ -322,6 +322,19 @@ local function call_luarocks(operation, mod, dir) end end +local function execute_command(arg) + local operation = arg[#arg] + local tree, mod, dir = check_flags(arg); + if tree then + call_luarocks(operation, mod, dir); + return 0; + else + dir = get_path_custom_plugins(prosody.paths.plugins); + call_luarocks(operation, mod, dir); + return 0; + end +end + return { show_message = show_message; show_warning = show_message; @@ -345,4 +358,5 @@ return { get_path_custom_plugins = get_path_custom_plugins; check_flags = check_flags; call_luarocks = call_luarocks; + execute_command = execute_command; }; -- cgit v1.2.3 From ee77705d60cd0a61d6fac8d1c03e6814da42b928 Mon Sep 17 00:00:00 2001 From: Jo?o Duarte Date: Wed, 31 Jul 2019 11:08:43 -0700 Subject: util.prosodyctl: The check_flags function now considers that a module, if given, is specified at the penultimate argument it receives --- util/prosodyctl.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua index 274d611c..bf65e396 100644 --- a/util/prosodyctl.lua +++ b/util/prosodyctl.lua @@ -301,9 +301,9 @@ local function check_flags(arg) -- There might not be any argument when the list command is calling this function if arg[1] and arg[1]:sub(1, #flag) == flag then local dir = arg[1]:match("=(.+)$") - return true, arg[2], dir; + return true, arg[#arg-1], dir; end - return false, arg[1] + return false, arg[#arg-1]; end local function call_luarocks(operation, mod, dir) -- cgit v1.2.3 From f93f3a10cdb1ed0db62dac40096df10cd0a0e0dc Mon Sep 17 00:00:00 2001 From: Jo?o Duarte Date: Thu, 1 Aug 2019 04:16:00 -0700 Subject: util.prosodyctl: Changed a comment --- util/prosodyctl.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua index bf65e396..c6abc02f 100644 --- a/util/prosodyctl.lua +++ b/util/prosodyctl.lua @@ -289,7 +289,7 @@ local function reload() end local function get_path_custom_plugins(plugin_paths) - -- I'm considering that we are using just one path to custom plugins, and it is the first in prosody.paths.plugins, for now + -- I'm considering that the custom plugins' path is the first one at prosody.paths.plugins -- luacheck: ignore 512 for path in plugin_paths:gmatch("[^;]+") do return path; -- cgit v1.2.3 From 9cf24d5024f39cc54a39d220879d4c62d4a4b34c Mon Sep 17 00:00:00 2001 From: Jo?o Duarte Date: Thu, 1 Aug 2019 04:33:05 -0700 Subject: util.startupt: I'm now using the resolve_relative_path function from util/paths at the setup_plugindir function --- util/startup.lua | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) (limited to 'util') diff --git a/util/startup.lua b/util/startup.lua index 4601bd85..4b3842bb 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -238,16 +238,8 @@ function startup.setup_plugindir() CFG_PLUGINDIR = table.concat(custom_plugin_paths, path_sep)..path_sep..(CFG_PLUGINDIR or "plugins"); prosody.paths.plugins = CFG_PLUGINDIR; end - -- Checks if installer_plugin_path is a relative paths and makes it an absolute path - if installer_plugin_path:sub(1,1) ~= "/" then - -- Works fine when executing prosody from source (configure and make only) - -- Probably wont be the best install directory, when using a package installation - local lfs_currentdir = require "lfs".currentdir(); - local current_directory = lfs_currentdir; - -- Some normalization - installer_plugin_path = installer_plugin_path:gsub("^%.%"..dir_sep.."+", ""); - installer_plugin_path = current_directory..dir_sep..installer_plugin_path; - end + local current_directory = require "lfs".currentdir(); + installer_plugin_path = config.resolve_relative_path(current_directory, installer_plugin_path); -- Checking if the folder exists. If it doesn't, we create it, but we need permissions to do so if os.execute('[ -d "'..installer_plugin_path..'" ]') ~= 0 then os.execute("mkdir "..installer_plugin_path); -- cgit v1.2.3 From 8cfebaa8318c0b6233941be5dafa5940fafa9360 Mon Sep 17 00:00:00 2001 From: Jo?o Duarte Date: Thu, 1 Aug 2019 07:48:11 -0700 Subject: util.startupt: setup_plugindir now uses lfs.mkdir to check/create directories --- util/startup.lua | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'util') diff --git a/util/startup.lua b/util/startup.lua index 4b3842bb..8c6c8b99 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -240,10 +240,7 @@ function startup.setup_plugindir() end local current_directory = require "lfs".currentdir(); installer_plugin_path = config.resolve_relative_path(current_directory, installer_plugin_path); - -- Checking if the folder exists. If it doesn't, we create it, but we need permissions to do so - if os.execute('[ -d "'..installer_plugin_path..'" ]') ~= 0 then - os.execute("mkdir "..installer_plugin_path); - end + require "lfs".mkdir(installer_plugin_path) -- Developers may have add these custom paths to their LUA_PATH/LUA_CPATH variables, before running prosody -- Therefore, I'll just check if the paths we are about to add aren't already at package.(path/cpath) if not string.match(package.path, installer_plugin_path) then -- cgit v1.2.3 From dcbfef2fcf7a397f230b2e13985d2f410fddf177 Mon Sep 17 00:00:00 2001 From: Jo?o Duarte Date: Thu, 1 Aug 2019 08:02:26 -0700 Subject: util.startup: Removed/rewrote comments at setup_plugindir --- util/startup.lua | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'util') diff --git a/util/startup.lua b/util/startup.lua index 8c6c8b99..e7107a9c 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -228,9 +228,7 @@ end function startup.setup_plugindir() local custom_plugin_paths = config.get("*", "plugin_paths"); local installer_plugin_path = config.get("*", "installer_plugin_path") or "custom_plugins"; - -- This variable separates different paths, like this "," here -> /usr;/home local path_sep = package.config:sub(3,3); - -- This variable is the separator between directories, in a path, like the "/" here -> /home/path/to/somewhere local dir_sep = package.config:sub(1,1); if custom_plugin_paths then -- path1;path2;path3;defaultpath... @@ -241,18 +239,15 @@ function startup.setup_plugindir() local current_directory = require "lfs".currentdir(); installer_plugin_path = config.resolve_relative_path(current_directory, installer_plugin_path); require "lfs".mkdir(installer_plugin_path) - -- Developers may have add these custom paths to their LUA_PATH/LUA_CPATH variables, before running prosody - -- Therefore, I'll just check if the paths we are about to add aren't already at package.(path/cpath) + -- Checking for duplicates + -- The commands using luarocks need the path to the directory that has the /share and /lib folders. if not string.match(package.path, installer_plugin_path) then local lua_version = _VERSION:match(" (.+)$") - -- I'm assuming there's good reason not to hard code any separator - -- This next line is unnecessary, but I think it makes the code more readable and neat local sub_path = dir_sep.."lua"..dir_sep..lua_version..dir_sep package.path = package.path..path_sep..installer_plugin_path..dir_sep.."share"..sub_path.."?.lua"; package.path = package.path..path_sep..installer_plugin_path..dir_sep.."share"..sub_path.."?"..dir_sep.."init.lua"; package.cpath = package.cpath..path_sep..installer_plugin_path..dir_sep.."lib"..sub_path.."?.lua"; end - -- The commands using luarocks need the path to the directory that has the /share and /lib folders. CFG_PLUGINDIR = installer_plugin_path..path_sep..(CFG_PLUGINDIR or "plugins"); prosody.paths.plugins = CFG_PLUGINDIR; end -- cgit v1.2.3 From d317295e55436b65519f05f48d76abc989354283 Mon Sep 17 00:00:00 2001 From: Jo?o Duarte Date: Thu, 1 Aug 2019 08:08:06 -0700 Subject: util.startup: Directly calling lfs.currentdir instead of storing it in a local variable --- util/startup.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'util') diff --git a/util/startup.lua b/util/startup.lua index e7107a9c..abf985f8 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -236,8 +236,7 @@ function startup.setup_plugindir() CFG_PLUGINDIR = table.concat(custom_plugin_paths, path_sep)..path_sep..(CFG_PLUGINDIR or "plugins"); prosody.paths.plugins = CFG_PLUGINDIR; end - local current_directory = require "lfs".currentdir(); - installer_plugin_path = config.resolve_relative_path(current_directory, installer_plugin_path); + installer_plugin_path = config.resolve_relative_path(require "lfs".currentdir(), installer_plugin_path); require "lfs".mkdir(installer_plugin_path) -- Checking for duplicates -- The commands using luarocks need the path to the directory that has the /share and /lib folders. -- cgit v1.2.3 From fdbe6f4afe9d2f6942451dc2d03df132a0988d75 Mon Sep 17 00:00:00 2001 From: Jo?o Duarte Date: Thu, 1 Aug 2019 08:28:24 -0700 Subject: util.startup: setup_plugindir now also checks package.cpath for duplicates --- util/startup.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/startup.lua b/util/startup.lua index abf985f8..6ba81819 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -240,11 +240,13 @@ function startup.setup_plugindir() require "lfs".mkdir(installer_plugin_path) -- Checking for duplicates -- The commands using luarocks need the path to the directory that has the /share and /lib folders. + local lua_version = _VERSION:match(" (.+)$") + local sub_path = dir_sep.."lua"..dir_sep..lua_version..dir_sep if not string.match(package.path, installer_plugin_path) then - local lua_version = _VERSION:match(" (.+)$") - local sub_path = dir_sep.."lua"..dir_sep..lua_version..dir_sep package.path = package.path..path_sep..installer_plugin_path..dir_sep.."share"..sub_path.."?.lua"; package.path = package.path..path_sep..installer_plugin_path..dir_sep.."share"..sub_path.."?"..dir_sep.."init.lua"; + end + if not string.match(package.path, installer_plugin_path) then package.cpath = package.cpath..path_sep..installer_plugin_path..dir_sep.."lib"..sub_path.."?.lua"; end CFG_PLUGINDIR = installer_plugin_path..path_sep..(CFG_PLUGINDIR or "plugins"); -- cgit v1.2.3 From 33ae171c3d547dfbd2e03d2383c39902fc911aed Mon Sep 17 00:00:00 2001 From: Jo?o Duarte Date: Thu, 1 Aug 2019 09:12:28 -0700 Subject: util.paths: Added the function 'complement_lua_path' --- util/paths.lua | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'util') diff --git a/util/paths.lua b/util/paths.lua index 89f4cad9..a8d80a4c 100644 --- a/util/paths.lua +++ b/util/paths.lua @@ -41,4 +41,20 @@ function path_util.join(...) return t_concat({...}, path_sep); end +function path_util.complement_lua_path(installer_plugin_path) + -- Checking for duplicates + -- The commands using luarocks need the path to the directory that has the /share and /lib folders. + local lua_version = _VERSION:match(" (.+)$"); + local path_sep = package.config:sub(3,3); + local dir_sep = package.config:sub(1,1); + local sub_path = dir_sep.."lua"..dir_sep..lua_version..dir_sep; + if not string.match(package.path, installer_plugin_path) then + package.path = package.path..path_sep..installer_plugin_path..dir_sep.."share"..sub_path.."?.lua"; + package.path = package.path..path_sep..installer_plugin_path..dir_sep.."share"..sub_path.."?"..dir_sep.."init.lua"; + end + if not string.match(package.path, installer_plugin_path) then + package.cpath = package.cpath..path_sep..installer_plugin_path..dir_sep.."lib"..sub_path.."?.lua"; + end +end + return path_util; -- cgit v1.2.3 From 84da5cd3d7c5c1fb2c74cff6044569f0bc03fec4 Mon Sep 17 00:00:00 2001 From: Jo?o Duarte Date: Thu, 1 Aug 2019 09:22:50 -0700 Subject: util.startup: Now calls a function to complement lua's path/cpath --- util/startup.lua | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) (limited to 'util') diff --git a/util/startup.lua b/util/startup.lua index 6ba81819..e46f98c9 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -229,7 +229,6 @@ function startup.setup_plugindir() local custom_plugin_paths = config.get("*", "plugin_paths"); local installer_plugin_path = config.get("*", "installer_plugin_path") or "custom_plugins"; local path_sep = package.config:sub(3,3); - local dir_sep = package.config:sub(1,1); if custom_plugin_paths then -- path1;path2;path3;defaultpath... -- luacheck: ignore 111 @@ -237,18 +236,8 @@ function startup.setup_plugindir() prosody.paths.plugins = CFG_PLUGINDIR; end installer_plugin_path = config.resolve_relative_path(require "lfs".currentdir(), installer_plugin_path); - require "lfs".mkdir(installer_plugin_path) - -- Checking for duplicates - -- The commands using luarocks need the path to the directory that has the /share and /lib folders. - local lua_version = _VERSION:match(" (.+)$") - local sub_path = dir_sep.."lua"..dir_sep..lua_version..dir_sep - if not string.match(package.path, installer_plugin_path) then - package.path = package.path..path_sep..installer_plugin_path..dir_sep.."share"..sub_path.."?.lua"; - package.path = package.path..path_sep..installer_plugin_path..dir_sep.."share"..sub_path.."?"..dir_sep.."init.lua"; - end - if not string.match(package.path, installer_plugin_path) then - package.cpath = package.cpath..path_sep..installer_plugin_path..dir_sep.."lib"..sub_path.."?.lua"; - end + require "lfs".mkdir(installer_plugin_path); + config.complement_lua_path(installer_plugin_path); CFG_PLUGINDIR = installer_plugin_path..path_sep..(CFG_PLUGINDIR or "plugins"); prosody.paths.plugins = CFG_PLUGINDIR; end -- cgit v1.2.3 From 5df8222affc9ee205232020be4b00856dc51d0b0 Mon Sep 17 00:00:00 2001 From: Jo?o Duarte Date: Thu, 1 Aug 2019 09:29:18 -0700 Subject: util.paths: Refactored a variable, to avoid shadowing --- util/paths.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'util') diff --git a/util/paths.lua b/util/paths.lua index a8d80a4c..de63111e 100644 --- a/util/paths.lua +++ b/util/paths.lua @@ -45,15 +45,15 @@ function path_util.complement_lua_path(installer_plugin_path) -- Checking for duplicates -- The commands using luarocks need the path to the directory that has the /share and /lib folders. local lua_version = _VERSION:match(" (.+)$"); - local path_sep = package.config:sub(3,3); + local lua_path_sep = package.config:sub(3,3); local dir_sep = package.config:sub(1,1); local sub_path = dir_sep.."lua"..dir_sep..lua_version..dir_sep; if not string.match(package.path, installer_plugin_path) then - package.path = package.path..path_sep..installer_plugin_path..dir_sep.."share"..sub_path.."?.lua"; - package.path = package.path..path_sep..installer_plugin_path..dir_sep.."share"..sub_path.."?"..dir_sep.."init.lua"; + package.path = package.path..lua_path_sep..installer_plugin_path..dir_sep.."share"..sub_path.."?.lua"; + package.path = package.path..lua_path_sep..installer_plugin_path..dir_sep.."share"..sub_path.."?"..dir_sep.."init.lua"; end if not string.match(package.path, installer_plugin_path) then - package.cpath = package.cpath..path_sep..installer_plugin_path..dir_sep.."lib"..sub_path.."?.lua"; + package.cpath = package.cpath..lua_path_sep..installer_plugin_path..dir_sep.."lib"..sub_path.."?.lua"; end end -- cgit v1.2.3 From 6a485386c060dcce9565ca0f2431ce6af15c88c7 Mon Sep 17 00:00:00 2001 From: Jo?o Duarte Date: Thu, 1 Aug 2019 09:29:40 -0700 Subject: util.startup: Reorganized code at setup_plugindir --- util/startup.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'util') diff --git a/util/startup.lua b/util/startup.lua index e46f98c9..a77237d5 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -229,15 +229,15 @@ function startup.setup_plugindir() local custom_plugin_paths = config.get("*", "plugin_paths"); local installer_plugin_path = config.get("*", "installer_plugin_path") or "custom_plugins"; local path_sep = package.config:sub(3,3); + installer_plugin_path = config.resolve_relative_path(require "lfs".currentdir(), installer_plugin_path); + require "lfs".mkdir(installer_plugin_path); + config.complement_lua_path(installer_plugin_path); if custom_plugin_paths then -- path1;path2;path3;defaultpath... -- luacheck: ignore 111 CFG_PLUGINDIR = table.concat(custom_plugin_paths, path_sep)..path_sep..(CFG_PLUGINDIR or "plugins"); prosody.paths.plugins = CFG_PLUGINDIR; end - installer_plugin_path = config.resolve_relative_path(require "lfs".currentdir(), installer_plugin_path); - require "lfs".mkdir(installer_plugin_path); - config.complement_lua_path(installer_plugin_path); CFG_PLUGINDIR = installer_plugin_path..path_sep..(CFG_PLUGINDIR or "plugins"); prosody.paths.plugins = CFG_PLUGINDIR; end -- cgit v1.2.3 From 7a596299b4c3fc79424130f2beb2cffe1a8caf02 Mon Sep 17 00:00:00 2001 From: Jo?o Duarte Date: Fri, 16 Aug 2019 09:26:36 -0700 Subject: util.startup: Changed the way util.paths.complement_lua_path was being accessed --- util/startup.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/startup.lua b/util/startup.lua index a77237d5..02e5f012 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -231,7 +231,7 @@ function startup.setup_plugindir() local path_sep = package.config:sub(3,3); installer_plugin_path = config.resolve_relative_path(require "lfs".currentdir(), installer_plugin_path); require "lfs".mkdir(installer_plugin_path); - config.complement_lua_path(installer_plugin_path); + require"util.paths".complement_lua_path(installer_plugin_path); if custom_plugin_paths then -- path1;path2;path3;defaultpath... -- luacheck: ignore 111 -- cgit v1.2.3 From 930c50094c3d5f7d8ef01e9c3bcd93509f3c6693 Mon Sep 17 00:00:00 2001 From: Jo?o Duarte Date: Fri, 16 Aug 2019 10:44:10 -0700 Subject: util.paths: Fixed file termination for package.cpath's extra path --- util/paths.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/paths.lua b/util/paths.lua index de63111e..c225108a 100644 --- a/util/paths.lua +++ b/util/paths.lua @@ -53,7 +53,7 @@ function path_util.complement_lua_path(installer_plugin_path) package.path = package.path..lua_path_sep..installer_plugin_path..dir_sep.."share"..sub_path.."?"..dir_sep.."init.lua"; end if not string.match(package.path, installer_plugin_path) then - package.cpath = package.cpath..lua_path_sep..installer_plugin_path..dir_sep.."lib"..sub_path.."?.lua"; + package.cpath = package.cpath..lua_path_sep..installer_plugin_path..dir_sep.."lib"..sub_path.."?.so"; end end -- cgit v1.2.3 From c96869a9b30536712703439a5a5ece36a26ef8df Mon Sep 17 00:00:00 2001 From: Jo?o Duarte Date: Fri, 16 Aug 2019 13:54:40 -0700 Subject: util.pluginloader: Added a new path to the variable local_names --- util/pluginloader.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/pluginloader.lua b/util/pluginloader.lua index 9ab8f245..af0428c4 100644 --- a/util/pluginloader.lua +++ b/util/pluginloader.lua @@ -36,12 +36,13 @@ end local function load_resource(plugin, resource) resource = resource or "mod_"..plugin..".lua"; - + local lua_version = _VERSION:match(" (.+)$"); local names = { "mod_"..plugin..dir_sep..plugin..dir_sep..resource; -- mod_hello/hello/mod_hello.lua "mod_"..plugin..dir_sep..resource; -- mod_hello/mod_hello.lua plugin..dir_sep..resource; -- hello/mod_hello.lua resource; -- mod_hello.lua + "share"..dir_sep.."lua"..dir_sep..lua_version..dir_sep.."mod_"..plugin..dir_sep..resource; }; return load_file(names); -- cgit v1.2.3 From 2e5522674ec344d9f240f656ed907ad7801185ee Mon Sep 17 00:00:00 2001 From: Jo?o Duarte Date: Fri, 16 Aug 2019 15:01:57 -0700 Subject: util/prosodyctl: Removed the check_flags and execute_command function --- util/prosodyctl.lua | 25 ------------------------- 1 file changed, 25 deletions(-) (limited to 'util') diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua index c6abc02f..316831a9 100644 --- a/util/prosodyctl.lua +++ b/util/prosodyctl.lua @@ -296,16 +296,6 @@ local function get_path_custom_plugins(plugin_paths) end end -local function check_flags(arg) - local flag = "--tree="; - -- There might not be any argument when the list command is calling this function - if arg[1] and arg[1]:sub(1, #flag) == flag then - local dir = arg[1]:match("=(.+)$") - return true, arg[#arg-1], dir; - end - return false, arg[#arg-1]; -end - local function call_luarocks(operation, mod, dir) if operation == "install" then show_message("Installing %s at %s", mod, dir); @@ -322,19 +312,6 @@ local function call_luarocks(operation, mod, dir) end end -local function execute_command(arg) - local operation = arg[#arg] - local tree, mod, dir = check_flags(arg); - if tree then - call_luarocks(operation, mod, dir); - return 0; - else - dir = get_path_custom_plugins(prosody.paths.plugins); - call_luarocks(operation, mod, dir); - return 0; - end -end - return { show_message = show_message; show_warning = show_message; @@ -356,7 +333,5 @@ return { stop = stop; reload = reload; get_path_custom_plugins = get_path_custom_plugins; - check_flags = check_flags; call_luarocks = call_luarocks; - execute_command = execute_command; }; -- cgit v1.2.3 From 8a6819c5643736a0fc7d4c45f496ad958796d36e Mon Sep 17 00:00:00 2001 From: Jo?o Duarte Date: Fri, 16 Aug 2019 15:03:50 -0700 Subject: util/prosodyctl: call_luarocks now sets a directory variable itself --- util/prosodyctl.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua index 316831a9..163658f3 100644 --- a/util/prosodyctl.lua +++ b/util/prosodyctl.lua @@ -296,7 +296,8 @@ local function get_path_custom_plugins(plugin_paths) end end -local function call_luarocks(operation, mod, dir) +local function call_luarocks(mod, operation) + local dir = get_path_custom_plugins(prosody.paths.plugins); if operation == "install" then show_message("Installing %s at %s", mod, dir); elseif operation == "remove" then -- cgit v1.2.3 From 59cbfb4e8c8ea4fcab7d443a7db9a897c04a2362 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 13 Jan 2019 14:01:31 +0100 Subject: util.sasl.scram: Factor out SHA-1 specific getAuthenticationDatabaseSHA1 This makes the code more generic, allowing SHA-1 to be replaced --- util/sasl/scram.lua | 45 +++++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 20 deletions(-) (limited to 'util') diff --git a/util/sasl/scram.lua b/util/sasl/scram.lua index 043f328b..0ca20a52 100644 --- a/util/sasl/scram.lua +++ b/util/sasl/scram.lua @@ -14,9 +14,7 @@ local s_match = string.match; local type = type local base64 = require "util.encodings".base64; -local hmac_sha1 = require "util.hashes".hmac_sha1; -local sha1 = require "util.hashes".sha1; -local Hi = require "util.hashes".scram_Hi_sha1; +local hashes = require "util.hashes"; local generate_uuid = require "util.uuid".generate; local saslprep = require "util.encodings".stringprep.saslprep; local nodeprep = require "util.encodings".stringprep.nodeprep; @@ -99,20 +97,22 @@ local function hashprep(hashname) return hashname:lower():gsub("-", "_"); end -local function getAuthenticationDatabaseSHA1(password, salt, iteration_count) - if type(password) ~= "string" or type(salt) ~= "string" or type(iteration_count) ~= "number" then - return false, "inappropriate argument types" - end - if iteration_count < 4096 then - log("warn", "Iteration count < 4096 which is the suggested minimum according to RFC 5802.") +local function get_scram_hasher(H, HMAC, Hi) + return function (password, salt, iteration_count) + if type(password) ~= "string" or type(salt) ~= "string" or type(iteration_count) ~= "number" then + return false, "inappropriate argument types" + end + if iteration_count < 4096 then + log("warn", "Iteration count < 4096 which is the suggested minimum according to RFC 5802.") + end + local salted_password = Hi(password, salt, iteration_count); + local stored_key = H(HMAC(salted_password, "Client Key")) + local server_key = HMAC(salted_password, "Server Key"); + return true, stored_key, server_key end - local salted_password = Hi(password, salt, iteration_count); - local stored_key = sha1(hmac_sha1(salted_password, "Client Key")) - local server_key = hmac_sha1(salted_password, "Server Key"); - return true, stored_key, server_key end -local function scram_gen(hash_name, H_f, HMAC_f) +local function scram_gen(hash_name, H_f, HMAC_f, get_auth_db) local profile_name = "scram_" .. hashprep(hash_name); local function scram_hash(self, message) local support_channel_binding = false; @@ -177,7 +177,7 @@ local function scram_gen(hash_name, H_f, HMAC_f) iteration_count = default_i; local succ; - succ, stored_key, server_key = getAuthenticationDatabaseSHA1(password, salt, iteration_count); + succ, stored_key, server_key = get_auth_db(password, salt, iteration_count); if not succ then log("error", "Generating authentication database failed. Reason: %s", stored_key); return "failure", "temporary-auth-failure"; @@ -247,22 +247,27 @@ local function scram_gen(hash_name, H_f, HMAC_f) return scram_hash; end +local auth_db_getters = {} local function init(registerMechanism) - local function registerSCRAMMechanism(hash_name, hash, hmac_hash) + local function registerSCRAMMechanism(hash_name, hash, hmac_hash, pbkdf2) + local get_auth_db = get_scram_hasher(hash, hmac_hash, pbkdf2); + auth_db_getters[hash_name] = get_auth_db; registerMechanism("SCRAM-"..hash_name, {"plain", "scram_"..(hashprep(hash_name))}, - scram_gen(hash_name:lower(), hash, hmac_hash)); + scram_gen(hash_name:lower(), hash, hmac_hash, get_auth_db)); -- register channel binding equivalent registerMechanism("SCRAM-"..hash_name.."-PLUS", {"plain", "scram_"..(hashprep(hash_name))}, - scram_gen(hash_name:lower(), hash, hmac_hash), {"tls-unique"}); + scram_gen(hash_name:lower(), hash, hmac_hash, get_auth_db), {"tls-unique"}); end - registerSCRAMMechanism("SHA-1", sha1, hmac_sha1); + registerSCRAMMechanism("SHA-1", hashes.sha1, hashes.hmac_sha1, hashes.pbkdf2_hmac_sha1); end return { - getAuthenticationDatabaseSHA1 = getAuthenticationDatabaseSHA1; + get_hash = get_scram_hasher; + hashers = auth_db_getters; + getAuthenticationDatabaseSHA1 = get_scram_hasher(hashes.sha1, hashes.sha256, hashes.pbkdf2_hmac_sha1); init = init; } -- cgit v1.2.3 From 77c357d325f1df0b755affca72daa4e1583554fc Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 13 Jan 2019 14:02:29 +0100 Subject: util.sasl.scram: Add support for SCRAM-SHA-256 --- util/sasl/scram.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'util') diff --git a/util/sasl/scram.lua b/util/sasl/scram.lua index 0ca20a52..793382b8 100644 --- a/util/sasl/scram.lua +++ b/util/sasl/scram.lua @@ -263,6 +263,7 @@ local function init(registerMechanism) end registerSCRAMMechanism("SHA-1", hashes.sha1, hashes.hmac_sha1, hashes.pbkdf2_hmac_sha1); + registerSCRAMMechanism("SHA-256", hashes.sha256, hashes.hmac_sha256, hashes.pbkdf2_hmac_sha256); end return { -- cgit v1.2.3 From aedca363c093007542941749578caf1badf1674e Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 22 Jul 2019 01:58:57 +0200 Subject: util.bitops: Library to find appropriate bitwise library (closes #1395) --- util/bit53.lua | 7 +++++++ util/bitcompat.lua | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 util/bit53.lua create mode 100644 util/bitcompat.lua (limited to 'util') diff --git a/util/bit53.lua b/util/bit53.lua new file mode 100644 index 00000000..4b5c2e9c --- /dev/null +++ b/util/bit53.lua @@ -0,0 +1,7 @@ +-- Only the operators needed by net.websocket.frames are provided at this point +return { + band = function (a, b) return a & b end; + bor = function (a, b) return a | b end; + bxor = function (a, b) return a ~ b end; +}; + diff --git a/util/bitcompat.lua b/util/bitcompat.lua new file mode 100644 index 00000000..454181af --- /dev/null +++ b/util/bitcompat.lua @@ -0,0 +1,32 @@ +-- Compatibility layer for bitwise operations + +-- First try the bit32 lib +-- Lua 5.3 has it with compat enabled +-- Lua 5.2 has it by default +if _G.bit32 then + return _G.bit32; +else + -- Lua 5.1 may have it as a standalone module that can be installed + local ok, bitop = pcall(require, "bit32") + if ok then + return bitop; + end +end + +do + -- Lua 5.3 and 5.4 would be able to use native infix operators + local ok, bitop = pcall(require, "util.bit53") + if ok then + return bitop; + end +end + +do + -- Lastly, try the LuaJIT bitop library + local ok, bitop = pcall(require, "bit") + if ok then + return bitop; + end +end + +error "No bit module found. See https://prosody.im/doc/depends#bitop"; -- cgit v1.2.3 From 9ea6b422a3568aefec377a30c321096fb5604be9 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 10 Sep 2019 18:16:11 +0200 Subject: util.x509: Nameprep commonName once --- util/x509.lua | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/x509.lua b/util/x509.lua index 1cdf07dc..c4001ecb 100644 --- a/util/x509.lua +++ b/util/x509.lua @@ -236,8 +236,11 @@ local function get_identities(cert) --> set of names local subject = cert:subject(); for i = 1, #subject do local dn = subject[i]; - if dn.oid == oid_commonname and nameprep(dn.value) then - names[dn.value] = true; + if dn.oid == oid_commonname then + local name = nameprep(dn.value); + if name then + names[name] = true; + end end end return names; -- cgit v1.2.3 From 1219da203e46b757ca1fb824b264d3ab726df056 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 10 Sep 2019 18:17:13 +0200 Subject: util.x509: Only collect commonNames that pass idna Weeds out "Example Certificate" and the like, which are uninteresting for this function. --- util/x509.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/x509.lua b/util/x509.lua index c4001ecb..82f8285d 100644 --- a/util/x509.lua +++ b/util/x509.lua @@ -238,7 +238,7 @@ local function get_identities(cert) --> set of names local dn = subject[i]; if dn.oid == oid_commonname then local name = nameprep(dn.value); - if name then + if name and idna_to_ascii(name) then names[name] = true; end end -- cgit v1.2.3 From e575b5de199840dfe17247e851ba2ee511565f68 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 10 Sep 2019 18:41:36 +0200 Subject: util.x509: Return sets of services per identity --- util/x509.lua | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) (limited to 'util') diff --git a/util/x509.lua b/util/x509.lua index 82f8285d..fe6e4b79 100644 --- a/util/x509.lua +++ b/util/x509.lua @@ -23,7 +23,9 @@ local idna_to_ascii = require "util.encodings".idna.to_ascii; local idna_to_unicode = require "util.encodings".idna.to_unicode; local base64 = require "util.encodings".base64; local log = require "util.logger".init("x509"); +local mt = require "util.multitable"; local s_format = string.format; +local ipairs = ipairs; local _ENV = nil; -- luacheck: std none @@ -218,18 +220,43 @@ local function verify_identity(host, service, cert) end -- TODO Support other SANs -local function get_identities(cert) --> set of names +local function get_identities(cert) --> map of names to sets of services if cert.setencode then cert:setencode("utf8"); end - local names = {}; + local names = mt.new(); local ext = cert:extensions(); local sans = ext[oid_subjectaltname]; - if sans and sans["dNSName"] then - for i = 1, #sans["dNSName"] do - names[ idna_to_unicode(sans["dNSName"][i]) ] = true; + if sans then + if sans["dNSName"] then -- Valid for any service + for _, name in ipairs(sans["dNSName"]) do + name = idna_to_unicode(nameprep(name)); + if name then + names:set(name, "*", true); + end + end + end + if sans[oid_xmppaddr] then + for _, name in ipairs(sans[oid_xmppaddr]) do + name = nameprep(name); + if name then + names:set(name, "xmpp-client", true); + names:set(name, "xmpp-server", true); + end + end + end + if sans[oid_dnssrv] then + for _, srvname in ipairs(sans[oid_dnssrv]) do + local srv, name = srvname:match("^_([^.]+)%.(.*)"); + if srv then + name = nameprep(name); + if name then + names:set(name, srv, true); + end + end + end end end @@ -239,11 +266,11 @@ local function get_identities(cert) --> set of names if dn.oid == oid_commonname then local name = nameprep(dn.value); if name and idna_to_ascii(name) then - names[name] = true; + names:set("*", name, true); end end end - return names; + return names.data; end local pat = "%-%-%-%-%-BEGIN ([A-Z ]+)%-%-%-%-%-\r?\n".. -- cgit v1.2.3 From 5bc702719e48c8b44d31f0ee65197357397d6f35 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 28 Sep 2019 18:24:28 +0200 Subject: util.sql: Preserve 3rd and 4th return values from transaction (fixes #1434) (thanks mrdoctorwho) --- util/sql.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'util') diff --git a/util/sql.lua b/util/sql.lua index 4406d7ff..86740b1c 100644 --- a/util/sql.lua +++ b/util/sql.lua @@ -218,14 +218,14 @@ function engine:_transaction(func, ...) end end function engine:transaction(...) - local ok, ret = self:_transaction(...); + local ok, ret, b, c = self:_transaction(...); if not ok then local conn = self.conn; if not conn or not conn:ping() then log("debug", "Database connection was closed. Will reconnect and retry."); self.conn = nil; log("debug", "Retrying SQL transaction [%s]", (...)); - ok, ret = self:_transaction(...); + ok, ret, b, c = self:_transaction(...); log("debug", "SQL transaction retry %s", ok and "succeeded" or "failed"); else log("debug", "SQL connection is up, so not retrying"); @@ -234,7 +234,7 @@ function engine:transaction(...) log("error", "Error in SQL transaction: %s", ret); end end - return ok, ret; + return ok, ret, b, c; end function engine:_create_index(index) local sql = "CREATE INDEX \""..index.name.."\" ON \""..index.table.."\" ("; -- cgit v1.2.3 From 24b178651d59bb841090eb3d83f907172c98e0f4 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 29 Sep 2019 18:42:35 +0200 Subject: util.async: Add function for waiting on promises and unpacking the results --- util/async.lua | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'util') diff --git a/util/async.lua b/util/async.lua index 20397785..d338071f 100644 --- a/util/async.lua +++ b/util/async.lua @@ -246,9 +246,25 @@ local function ready() return pcall(checkthread); end +local function wait(promise) + local async_wait, async_done = waiter(); + local ret, err = nil, nil; + promise:next( + function (r) ret = r; end, + function (e) err = e; end) + :finally(async_done); + async_wait(); + if ret then + return ret; + else + return nil, err; + end +end + return { ready = ready; waiter = waiter; guarder = guarder; runner = runner; + wait = wait; }; -- cgit v1.2.3 From cbbcbf6fda93217a647d58ecd6b8b08968338e59 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 29 Sep 2019 21:12:46 +0200 Subject: util.sasl.scram: Fix old API This function is not directly used by anything in Prosody anymore and should be considered deprecated. --- util/sasl/scram.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/sasl/scram.lua b/util/sasl/scram.lua index 793382b8..9bf1737b 100644 --- a/util/sasl/scram.lua +++ b/util/sasl/scram.lua @@ -269,6 +269,6 @@ end return { get_hash = get_scram_hasher; hashers = auth_db_getters; - getAuthenticationDatabaseSHA1 = get_scram_hasher(hashes.sha1, hashes.sha256, hashes.pbkdf2_hmac_sha1); + getAuthenticationDatabaseSHA1 = get_scram_hasher(hashes.sha1, hashes.hmac_sha1, hashes.pbkdf2_hmac_sha1); -- COMPAT init = init; } -- cgit v1.2.3 From af596f7e291caa5db92c484ddfa256ec2efa75c1 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 6 Oct 2019 16:07:26 +0200 Subject: util.sasl.scram: Avoid implicit coersion of number to string Lua can be compiled without coercion, which would cause an error here. --- util/sasl/scram.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/sasl/scram.lua b/util/sasl/scram.lua index 9bf1737b..1d1590e8 100644 --- a/util/sasl/scram.lua +++ b/util/sasl/scram.lua @@ -190,7 +190,7 @@ local function scram_gen(hash_name, H_f, HMAC_f, get_auth_db) end local nonce = clientnonce .. generate_uuid(); - local server_first_message = "r="..nonce..",s="..base64.encode(salt)..",i="..iteration_count; + local server_first_message = ("r=%s,s=%s,i=%d"):format(nonce, base64.encode(salt), iteration_count); self.state = { gs2_header = gs2_header; gs2_cbind_name = gs2_cbind_name; -- cgit v1.2.3 From fe0ae623c79dea7d567e7328b287396deed7d429 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Thu, 10 Oct 2019 16:58:02 +0200 Subject: util.statistics: Add a total count for rate counters, counting from server start. --- util/statistics.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/statistics.lua b/util/statistics.lua index 39954652..0ec88e21 100644 --- a/util/statistics.lua +++ b/util/statistics.lua @@ -57,12 +57,14 @@ local function new_registry(config) end; end; rate = function (name) - local since, n = time(), 0; + local since, n, total = time(), 0, 0; registry[name..":rate"] = function () + total = total + n; local t = time(); local stats = { rate = n/(t-since); count = n; + total = total; }; since, n = t, 0; return "rate", stats.rate, stats; -- cgit v1.2.3 From 3496abfdacdb40cd401952b7512c1221e771280e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 27 Oct 2019 14:45:57 +0000 Subject: util.pubsub, pubsub.lib and tests: Add text to precondition-not-met error (fixes #1455) --- util/pubsub.lua | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'util') diff --git a/util/pubsub.lua b/util/pubsub.lua index e5e0cb7c..8a07c669 100644 --- a/util/pubsub.lua +++ b/util/pubsub.lua @@ -1,5 +1,6 @@ local events = require "util.events"; local cache = require "util.cache"; +local errors = require "util.error"; local service_mt = {}; @@ -510,7 +511,7 @@ local function check_preconditions(node_config, required_config) end for config_field, value in pairs(required_config) do if node_config[config_field] ~= value then - return false; + return false, config_field; end end return true; @@ -546,8 +547,13 @@ function service:publish(node, actor, id, item, requested_config) --> ok, err node_obj = self.nodes[node]; elseif requested_config and not requested_config._defaults_only then -- Check that node has the requested config before we publish - if not check_preconditions(node_obj.config, requested_config) then - return false, "precondition-not-met"; + local ok, field = check_preconditions(node_obj.config, requested_config); + if not ok then + local err = errors.new({ + type = "cancel", condition = "conflict", text = "Field does not match: "..field; + }); + err.pubsub_condition = "precondition-not-met"; + return false, err; end end if not self.config.itemcheck(item) then -- cgit v1.2.3 From 571afbf292e67ca92636a6b491ea88fdc1be653d Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 9 Sep 2019 22:15:04 +0200 Subject: util.jid: Add a 'strict' flag for jidprep calls --- util/jid.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'util') diff --git a/util/jid.lua b/util/jid.lua index ec31f180..1ddf33d4 100644 --- a/util/jid.lua +++ b/util/jid.lua @@ -45,20 +45,20 @@ local function bare(jid) return host; end -local function prepped_split(jid) +local function prepped_split(jid, strict) local node, host, resource = split(jid); if host and host ~= "." then if sub(host, -1, -1) == "." then -- Strip empty root label host = sub(host, 1, -2); end - host = nameprep(host); + host = nameprep(host, strict); if not host then return; end if node then - node = nodeprep(node); + node = nodeprep(node, strict); if not node then return; end end if resource then - resource = resourceprep(resource); + resource = resourceprep(resource, strict); if not resource then return; end end return node, host, resource; @@ -77,8 +77,8 @@ local function join(node, host, resource) return host; end -local function prep(jid) - local node, host, resource = prepped_split(jid); +local function prep(jid, strict) + local node, host, resource = prepped_split(jid, strict); return join(node, host, resource); end -- cgit v1.2.3 From 3ba24f4366692a0bb2c8df19a034ac37c731428e Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 1 Nov 2019 18:31:12 +0100 Subject: util.error: Add well-known field 'code' in error templates Intended to be for HTTP-ish numeric status codes --- util/error.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'util') diff --git a/util/error.lua b/util/error.lua index 23551fe2..9ebfa6ab 100644 --- a/util/error.lua +++ b/util/error.lua @@ -14,6 +14,7 @@ local function new(e, context, registry) type = template.type or "cancel"; condition = template.condition or "undefined-condition"; text = template.text; + code = template.code or 500; context = context or template.context or { _error_id = e }; }, error_mt); -- cgit v1.2.3 From f700edb4f0a23ca0327629936da4a25ac034a550 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 1 Nov 2019 22:53:14 +0100 Subject: util.prosodyctl: Enforce strict JID validation on user creation This is where 64ddcbc9a328 should have started. By preventing creation of users with invalid JIDs, it will slowly become safer to enforce strict validation on everything. --- util/prosodyctl.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua index 163658f3..586802d3 100644 --- a/util/prosodyctl.lua +++ b/util/prosodyctl.lua @@ -134,7 +134,7 @@ end -- Server control local function adduser(params) - local user, host, password = nodeprep(params.user), nameprep(params.host), params.password; + local user, host, password = nodeprep(params.user, true), nameprep(params.host), params.password; if not user then return false, "invalid-username"; elseif not host then -- cgit v1.2.3 From bcaf97314929c5e06eb526917a6047002be481d8 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 8 Nov 2019 19:25:57 +0100 Subject: util.paths: Don't treat path as pattern, fix traceback (thanks Menel87) --- util/paths.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/paths.lua b/util/paths.lua index c225108a..036f315a 100644 --- a/util/paths.lua +++ b/util/paths.lua @@ -48,11 +48,11 @@ function path_util.complement_lua_path(installer_plugin_path) local lua_path_sep = package.config:sub(3,3); local dir_sep = package.config:sub(1,1); local sub_path = dir_sep.."lua"..dir_sep..lua_version..dir_sep; - if not string.match(package.path, installer_plugin_path) then + if not string.find(package.path, installer_plugin_path, 1, true) then package.path = package.path..lua_path_sep..installer_plugin_path..dir_sep.."share"..sub_path.."?.lua"; package.path = package.path..lua_path_sep..installer_plugin_path..dir_sep.."share"..sub_path.."?"..dir_sep.."init.lua"; end - if not string.match(package.path, installer_plugin_path) then + if not string.find(package.path, installer_plugin_path, 1, true) then package.cpath = package.cpath..lua_path_sep..installer_plugin_path..dir_sep.."lib"..sub_path.."?.so"; end end -- cgit v1.2.3 From f8cd8190d57f3a04420c3ecfb0c85e6a1ee02620 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 9 Nov 2019 00:23:08 +0100 Subject: util.startup: Split plugin installer path setup into a separate function --- util/startup.lua | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'util') diff --git a/util/startup.lua b/util/startup.lua index 62fd5579..c9d06109 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -228,17 +228,22 @@ end function startup.setup_plugindir() local custom_plugin_paths = config.get("*", "plugin_paths"); - local installer_plugin_path = config.get("*", "installer_plugin_path") or "custom_plugins"; local path_sep = package.config:sub(3,3); - installer_plugin_path = config.resolve_relative_path(require "lfs".currentdir(), installer_plugin_path); - require "lfs".mkdir(installer_plugin_path); - require"util.paths".complement_lua_path(installer_plugin_path); if custom_plugin_paths then -- path1;path2;path3;defaultpath... -- luacheck: ignore 111 CFG_PLUGINDIR = table.concat(custom_plugin_paths, path_sep)..path_sep..(CFG_PLUGINDIR or "plugins"); prosody.paths.plugins = CFG_PLUGINDIR; end +end + +function startup.setup_plugin_install_path() + local installer_plugin_path = config.get("*", "installer_plugin_path") or "custom_plugins"; + local path_sep = package.config:sub(3,3); + installer_plugin_path = config.resolve_relative_path(require "lfs".currentdir(), installer_plugin_path); + require "lfs".mkdir(installer_plugin_path); + require"util.paths".complement_lua_path(installer_plugin_path); + -- luacheck: ignore 111 CFG_PLUGINDIR = installer_plugin_path..path_sep..(CFG_PLUGINDIR or "plugins"); prosody.paths.plugins = CFG_PLUGINDIR; end @@ -534,6 +539,7 @@ function startup.prosodyctl() startup.force_console_logging(); startup.init_logging(); startup.setup_plugindir(); + startup.setup_plugin_install_path(); startup.setup_datadir(); startup.chdir(); startup.read_version(); @@ -559,6 +565,7 @@ function startup.prosody() startup.init_logging(); startup.load_libraries(); startup.setup_plugindir(); + startup.setup_plugin_install_path(); startup.setup_datadir(); startup.chdir(); startup.add_global_prosody_functions(); -- cgit v1.2.3 From f6c25cbfee77020698f43865a9e33e74ca71a048 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 9 Nov 2019 00:26:56 +0100 Subject: util.startup: Disable plugin installer path creation for now (see comments) --- util/startup.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/startup.lua b/util/startup.lua index c9d06109..8e6d89e6 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -240,7 +240,9 @@ end function startup.setup_plugin_install_path() local installer_plugin_path = config.get("*", "installer_plugin_path") or "custom_plugins"; local path_sep = package.config:sub(3,3); + -- TODO Figure out what this should be relative to, because CWD could be anywhere installer_plugin_path = config.resolve_relative_path(require "lfs".currentdir(), installer_plugin_path); + -- TODO Can probably move directory creation to the install command require "lfs".mkdir(installer_plugin_path); require"util.paths".complement_lua_path(installer_plugin_path); -- luacheck: ignore 111 @@ -539,7 +541,7 @@ function startup.prosodyctl() startup.force_console_logging(); startup.init_logging(); startup.setup_plugindir(); - startup.setup_plugin_install_path(); + -- startup.setup_plugin_install_path(); startup.setup_datadir(); startup.chdir(); startup.read_version(); @@ -565,7 +567,7 @@ function startup.prosody() startup.init_logging(); startup.load_libraries(); startup.setup_plugindir(); - startup.setup_plugin_install_path(); + -- startup.setup_plugin_install_path(); startup.setup_datadir(); startup.chdir(); startup.add_global_prosody_functions(); -- cgit v1.2.3 From e6f328b33cfd381832e89c2ef2b316aefbc03f83 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 9 Nov 2019 13:58:25 +0100 Subject: util.dependencies: Avoid missing bitop false positive on Lua 5.4 --- util/dependencies.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/dependencies.lua b/util/dependencies.lua index 84e2dd5c..22b66d7c 100644 --- a/util/dependencies.lua +++ b/util/dependencies.lua @@ -90,7 +90,7 @@ local function check_dependencies() }, "SSL/TLS support will not be available"); end - local bit = _G.bit32 or softreq"bit"; + local bit = softreq"util.bitcompat"; if not bit then missingdep("lua-bitops", { -- cgit v1.2.3 From 2e216150e8938ad5df6e5df751db3a48bfb96ced Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 20 Nov 2019 19:22:55 +0100 Subject: util.termcolours: Use explicit number formatting instead of coercion on concatenation --- util/termcolours.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/termcolours.lua b/util/termcolours.lua index 829d84af..2c13d0ff 100644 --- a/util/termcolours.lua +++ b/util/termcolours.lua @@ -83,7 +83,7 @@ end setmetatable(stylemap, { __index = function(_, style) if type(style) == "string" and style:find("%x%x%x%x%x%x") == 1 then local g = style:sub(7) == " background" and "48;5;" or "38;5;"; - return g .. color(hex2rgb(style)); + return format("%s%d", g, color(hex2rgb(style))); end end } ); -- cgit v1.2.3 From b9875e5501fe7ab32465e4677a9f35b064be5099 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 25 Nov 2019 20:44:05 +0100 Subject: util.stanza: Check that argument to reply is a stanza --- util/stanza.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util') diff --git a/util/stanza.lua b/util/stanza.lua index 55c38c73..f46d5b30 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -434,6 +434,9 @@ local function iq(attr) end local function reply(orig) + if not is_stanza(orig) then + error("bad argument to reply: expected stanza, got "..type(orig)); + end return new_stanza(orig.name, orig.attr and { to = orig.attr.from, -- cgit v1.2.3 From 7fdb06225efa628956ee38d944bd45d811bac358 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 25 Nov 2019 20:46:55 +0100 Subject: util.stanza: Remove redundant check for attrs A stanza can't not have attrs if created the correct way --- util/stanza.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/stanza.lua b/util/stanza.lua index f46d5b30..919d983c 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -438,7 +438,7 @@ local function reply(orig) error("bad argument to reply: expected stanza, got "..type(orig)); end return new_stanza(orig.name, - orig.attr and { + { to = orig.attr.from, from = orig.attr.to, id = orig.attr.id, -- cgit v1.2.3 From 7954f16d5913ce2383e220c040e36b153ac522e3 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 25 Nov 2019 20:52:01 +0100 Subject: util.stanza: Check that argument to error_reply is a stanza --- util/stanza.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util') diff --git a/util/stanza.lua b/util/stanza.lua index 919d983c..24fd4fea 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -448,6 +448,9 @@ end local xmpp_stanzas_attr = { xmlns = xmlns_stanzas }; local function error_reply(orig, error_type, condition, error_message) + if not is_stanza(orig) then + error("bad argument to error_reply: expected stanza, got "..type(orig)); + end local t = reply(orig); t.attr.type = "error"; t:tag("error", {type = error_type}) --COMPAT: Some day xmlns:stanzas goes here -- cgit v1.2.3 From cb1ec1605974c11ab431aa2537804956f1ecd579 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 25 Nov 2019 20:52:03 +0100 Subject: util.stanza: Check that argument to error_reply is NOT a stanza of type error Replying to an error is Very Bad --- util/stanza.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util') diff --git a/util/stanza.lua b/util/stanza.lua index 24fd4fea..8ff1aae3 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -450,6 +450,8 @@ local xmpp_stanzas_attr = { xmlns = xmlns_stanzas }; local function error_reply(orig, error_type, condition, error_message) if not is_stanza(orig) then error("bad argument to error_reply: expected stanza, got "..type(orig)); + elseif orig.attr.type == "error" then + error("bad argument to error_reply: got stanza of type error which must not be replied to"); end local t = reply(orig); t.attr.type = "error"; -- cgit v1.2.3 From 1396c094b678d3e8f91ef6ccf2ccb7cf330541c5 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 25 Nov 2019 20:59:36 +0100 Subject: util.stanza: Support the 'by' attribute on errors This is to be used when the entity generating the error is not the same as the one the stanza was directed to, e.g. an intermediate server. --- util/stanza.lua | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/stanza.lua b/util/stanza.lua index 8ff1aae3..8e46f90f 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -447,7 +447,7 @@ local function reply(orig) end local xmpp_stanzas_attr = { xmlns = xmlns_stanzas }; -local function error_reply(orig, error_type, condition, error_message) +local function error_reply(orig, error_type, condition, error_message, error_by) if not is_stanza(orig) then error("bad argument to error_reply: expected stanza, got "..type(orig)); elseif orig.attr.type == "error" then @@ -455,7 +455,10 @@ local function error_reply(orig, error_type, condition, error_message) end local t = reply(orig); t.attr.type = "error"; - t:tag("error", {type = error_type}) --COMPAT: Some day xmlns:stanzas goes here + if t.attr.from == error_by then + error_by = nil; + end + t:tag("error", {type = error_type, by = error_by}) --COMPAT: Some day xmlns:stanzas goes here :tag(condition, xmpp_stanzas_attr):up(); if error_message then t:text_tag("text", error_message, xmpp_stanzas_attr); end return t; -- stanza ready for adding app-specific errors -- cgit v1.2.3 From 007f277b1716c5052f3bbf2fba120973440a9f70 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 8 Dec 2019 17:00:45 +0100 Subject: util.error: Write down some thoughts in comments --- util/error.lua | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'util') diff --git a/util/error.lua b/util/error.lua index 9ebfa6ab..461bf385 100644 --- a/util/error.lua +++ b/util/error.lua @@ -8,6 +8,13 @@ local function is_err(e) return getmetatable(e) == error_mt; end +-- Do we want any more well-known fields? +-- Or could we just copy all fields from `e`? +-- Sometimes you want variable details in the `text`, how to handle that? +-- Translations? +-- Should the `type` be restricted to the stanza error types or free-form? +-- What to set `type` to for stream errors or SASL errors? Those don't have a 'type' attr. + local function new(e, context, registry) local template = (registry and registry[e]) or e or {}; return setmetatable({ -- cgit v1.2.3 From 104eff8b7c85497a8352d9ce3b234a149fa74486 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 8 Dec 2019 17:48:37 +0100 Subject: util.x509: Fix recording of CommonNames in get_identities Don't worry, this function is not used by anything yet, this isn't a security issue. It'll be used by Prosody to pick the correct certificate for itself in the future. The `names` multitable is a collection of (name, service) pairs but it put them in the wrong order here. --- util/x509.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/x509.lua b/util/x509.lua index fe6e4b79..342dafde 100644 --- a/util/x509.lua +++ b/util/x509.lua @@ -266,7 +266,7 @@ local function get_identities(cert) --> map of names to sets of services if dn.oid == oid_commonname then local name = nameprep(dn.value); if name and idna_to_ascii(name) then - names:set("*", name, true); + names:set(name, "*", true); end end end -- cgit v1.2.3 From 8401285913baab3730236e06f48ecb079a5fa16d Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 9 Dec 2019 16:39:48 +0100 Subject: util.sasl.scram: Ignore unused authzid variable (strict lint) It would be nice if authzid was passed down into the stack and could be used by plugins for things. --- util/sasl/scram.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'util') diff --git a/util/sasl/scram.lua b/util/sasl/scram.lua index 1d1590e8..e2ce00f5 100644 --- a/util/sasl/scram.lua +++ b/util/sasl/scram.lua @@ -125,6 +125,7 @@ local function scram_gen(hash_name, H_f, HMAC_f, get_auth_db) local client_first_message = message; -- TODO: fail if authzid is provided, since we don't support them yet + -- luacheck: ignore 211/authzid local gs2_header, gs2_cbind_flag, gs2_cbind_name, authzid, client_first_message_bare, username, clientnonce = s_match(client_first_message, "^(([pny])=?([^,]*),([^,]*),)(m?=?[^,]*,?n=([^,]*),r=([^,]*),?.*)$"); -- cgit v1.2.3 From 1ac8dabcf5cd07b82745e00f91b161e03bf120a4 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 14 Dec 2019 20:28:44 +0100 Subject: util.error: Move default for numeric error code to net.http.server Stanza errors can also have numbers but these are a legacy thing and rarely used, except in MUC. HTTP errors on the other hand always have a number. --- util/error.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/error.lua b/util/error.lua index 461bf385..ca960dd9 100644 --- a/util/error.lua +++ b/util/error.lua @@ -21,7 +21,7 @@ local function new(e, context, registry) type = template.type or "cancel"; condition = template.condition or "undefined-condition"; text = template.text; - code = template.code or 500; + code = template.code; context = context or template.context or { _error_id = e }; }, error_mt); -- cgit v1.2.3 From ee3a103480d2af2dabf83174cd64e6917df5972d Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 14 Dec 2019 22:47:41 +0100 Subject: util.stanza: Accept util.error object to error_reply If we're moving towards util.error as the standard error container then this makes sense. This may allow for future extensibility without needing a lot of optional arguments. --- util/stanza.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util') diff --git a/util/stanza.lua b/util/stanza.lua index 8e46f90f..f5cd5668 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -458,6 +458,9 @@ local function error_reply(orig, error_type, condition, error_message, error_by) if t.attr.from == error_by then error_by = nil; end + if type(error_type) == "table" then -- an util.error or similar object + error_type, condition, error_message = error_type.type, error_type.condition, error_type.text; + end t:tag("error", {type = error_type, by = error_by}) --COMPAT: Some day xmlns:stanzas goes here :tag(condition, xmpp_stanzas_attr):up(); if error_message then t:text_tag("text", error_message, xmpp_stanzas_attr); end -- cgit v1.2.3 From 392f6fec79929be9bd303543649872baaafda756 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 23 Dec 2019 21:01:55 +0100 Subject: util.datamanager: Ignore unused 'errno' variable [luacheck] --- util/datamanager.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/datamanager.lua b/util/datamanager.lua index b52c77fa..1c578fee 100644 --- a/util/datamanager.lua +++ b/util/datamanager.lua @@ -157,7 +157,8 @@ end local function atomic_store(filename, data) local scratch = filename.."~"; - local f, ok, msg, errno; + local f, ok, msg, errno; -- luacheck: ignore errno + -- TODO return util.error with code=errno? f, msg, errno = io_open(scratch, "w"); if not f then -- cgit v1.2.3 From d36536327142ecfe77b00fa929f383bf932270ae Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 23 Dec 2019 21:02:19 +0100 Subject: util.startup: Ignore unused errno variable [luacheck] --- util/startup.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/startup.lua b/util/startup.lua index 8e6d89e6..dd757dd1 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -448,7 +448,7 @@ function startup.switch_user() print("Warning: Couldn't switch to Prosody user/group '"..tostring(desired_user).."'/'"..tostring(desired_group).."': "..tostring(err)); else -- Make sure the Prosody user can read the config - local conf, err, errno = io.open(prosody.config_file); + local conf, err, errno = io.open(prosody.config_file); --luacheck: ignore 211/errno if conf then conf:close(); else -- cgit v1.2.3 From 7da0e322ed6969444703a976ae01fabdadfb2794 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 23 Dec 2019 21:09:38 +0100 Subject: util.mercurial: Ignore an unused error variable [luacheck] --- util/mercurial.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/mercurial.lua b/util/mercurial.lua index 3f75c4c1..0f2b1d04 100644 --- a/util/mercurial.lua +++ b/util/mercurial.lua @@ -19,7 +19,7 @@ function hg.check_id(path) hg_changelog:close(); end else - local hg_archival,e = io.open(path.."/.hg_archival.txt"); + local hg_archival,e = io.open(path.."/.hg_archival.txt"); -- luacheck: ignore 211/e if hg_archival then local repo = hg_archival:read("*l"); local node = hg_archival:read("*l"); -- cgit v1.2.3 From be507b93838619f24a0b5cc89463513bc2238932 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 23 Dec 2019 21:13:27 +0100 Subject: util.sql: Handle failure to detect connection encoding Silences a luacheck warning about an unused variable --- util/sql.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util') diff --git a/util/sql.lua b/util/sql.lua index 86740b1c..9d1c86ca 100644 --- a/util/sql.lua +++ b/util/sql.lua @@ -335,6 +335,9 @@ function engine:set_encoding() -- to UTF-8 local ok, actual_charset = self:transaction(function () return self:select"SHOW SESSION VARIABLES LIKE 'character_set_client'"; end); + if not ok then + return false, "Failed to detect connection encoding"; + end local charset_ok = true; for row in actual_charset do if row[2] ~= charset then -- cgit v1.2.3 From e93c2bc0ece20334952feaa90124895959dc61cc Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 23 Dec 2019 21:15:01 +0100 Subject: util.dependencies: Pass require error to error formatting function For future use there. Silences luacheck warnings about unused 'err' --- util/dependencies.lua | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'util') diff --git a/util/dependencies.lua b/util/dependencies.lua index 22b66d7c..ede8c6ac 100644 --- a/util/dependencies.lua +++ b/util/dependencies.lua @@ -13,7 +13,8 @@ if not softreq "luarocks.loader" then -- LuaRocks 2.x softreq "luarocks.require"; -- LuaRocks <1.x end -local function missingdep(name, sources, msg) +local function missingdep(name, sources, msg, err) -- luacheck: ignore err + -- TODO print something about the underlying error, useful for debugging print(""); print("**************************"); print("Prosody was unable to find "..tostring(name)); @@ -44,25 +45,25 @@ local function check_dependencies() local fatal; - local lxp = softreq "lxp" + local lxp, err = softreq "lxp" if not lxp then missingdep("luaexpat", { ["Debian/Ubuntu"] = "sudo apt-get install lua-expat"; ["luarocks"] = "luarocks install luaexpat"; ["Source"] = "http://matthewwild.co.uk/projects/luaexpat/"; - }); + }, nil, err); fatal = true; end - local socket = softreq "socket" + local socket, err = softreq "socket" if not socket then missingdep("luasocket", { ["Debian/Ubuntu"] = "sudo apt-get install lua-socket"; ["luarocks"] = "luarocks install luasocket"; ["Source"] = "http://www.tecgraf.puc-rio.br/~diego/professional/luasocket/"; - }); + }, nil, err); fatal = true; elseif not socket.tcp4 then -- COMPAT LuaSocket before being IP-version agnostic @@ -76,28 +77,28 @@ local function check_dependencies() ["luarocks"] = "luarocks install luafilesystem"; ["Debian/Ubuntu"] = "sudo apt-get install lua-filesystem"; ["Source"] = "http://www.keplerproject.org/luafilesystem/"; - }); + }, nil, err); fatal = true; end - local ssl = softreq "ssl" + local ssl, err = softreq "ssl" if not ssl then missingdep("LuaSec", { ["Debian/Ubuntu"] = "sudo apt-get install lua-sec"; ["luarocks"] = "luarocks install luasec"; ["Source"] = "https://github.com/brunoos/luasec"; - }, "SSL/TLS support will not be available"); + }, "SSL/TLS support will not be available", err); end - local bit = softreq"util.bitcompat"; + local bit, err = softreq"util.bitcompat"; if not bit then missingdep("lua-bitops", { ["Debian/Ubuntu"] = "sudo apt-get install lua-bitop"; ["luarocks"] = "luarocks install luabitop"; ["Source"] = "http://bitop.luajit.org/"; - }, "WebSocket support will not be available"); + }, "WebSocket support will not be available", err); end local encodings, err = softreq "util.encodings" -- cgit v1.2.3 From 5c0fcb510634d09bbd1b583c92d3414c8c0b3026 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 23 Dec 2019 21:16:00 +0100 Subject: util.prosodyctl: Silence luacheck warnings --- util/prosodyctl.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua index 586802d3..0787b8c3 100644 --- a/util/prosodyctl.lua +++ b/util/prosodyctl.lua @@ -210,7 +210,7 @@ local function getpid() return false, "pidfile-read-failed", err; end - local locked, err = lfs.lock(file, "w"); + local locked, err = lfs.lock(file, "w"); -- luacheck: ignore 211/err if locked then file:close(); return false, "pidfile-not-locked"; @@ -227,7 +227,7 @@ local function getpid() end local function isrunning() - local ok, pid, err = getpid(); + local ok, pid, err = getpid(); -- luacheck: ignore 211/err if not ok then if pid == "pidfile-read-failed" or pid == "pidfile-not-locked" then -- Report as not running, since we can't open the pidfile -- cgit v1.2.3 From 79df4b5cc14a57ff8d9b50b5eb101c2c994aa692 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 23 Dec 2019 21:16:10 +0100 Subject: util.pubsub: Silence luacheck warnings, leaving notes on future proper fix --- util/pubsub.lua | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'util') diff --git a/util/pubsub.lua b/util/pubsub.lua index 2e9df0e7..cfac7a68 100644 --- a/util/pubsub.lua +++ b/util/pubsub.lua @@ -281,7 +281,8 @@ function service:set_affiliation(node, actor, jid, affiliation) --> ok, err node_obj.affiliations[jid] = affiliation; if self.config.nodestore then - local ok, err = save_node_to_store(self, node_obj); + -- TODO pass the error from storage to caller eg wrapped in an util.error + local ok, err = save_node_to_store(self, node_obj); -- luacheck: ignore 211/err if not ok then node_obj.affiliations[jid] = old_affiliation; return ok, "internal-server-error"; @@ -345,7 +346,8 @@ function service:add_subscription(node, actor, jid, options) --> ok, err end if self.config.nodestore then - local ok, err = save_node_to_store(self, node_obj); + -- TODO pass the error from storage to caller eg wrapped in an util.error + local ok, err = save_node_to_store(self, node_obj); -- luacheck: ignore 211/err if not ok then node_obj.subscribers[jid] = old_subscription; self.subscriptions[normal_jid][jid][node] = old_subscription and true or nil; @@ -397,7 +399,8 @@ function service:remove_subscription(node, actor, jid) --> ok, err end if self.config.nodestore then - local ok, err = save_node_to_store(self, node_obj); + -- TODO pass the error from storage to caller eg wrapped in an util.error + local ok, err = save_node_to_store(self, node_obj); -- luacheck: ignore 211/err if not ok then node_obj.subscribers[jid] = old_subscription; self.subscriptions[normal_jid][jid][node] = old_subscription and true or nil; @@ -455,7 +458,8 @@ function service:create(node, actor, options) --> ok, err }; if self.config.nodestore then - local ok, err = save_node_to_store(self, self.nodes[node]); + -- TODO pass the error from storage to caller eg wrapped in an util.error + local ok, err = save_node_to_store(self, self.nodes[node]); -- luacheck: ignore 211/err if not ok then self.nodes[node] = nil; return ok, "internal-server-error"; @@ -774,7 +778,8 @@ function service:set_node_config(node, actor, new_config) --> ok, err node_obj.config = new_config; if self.config.nodestore then - local ok, err = save_node_to_store(self, node_obj); + -- TODO pass the error from storage to caller eg wrapped in an util.error + local ok, err = save_node_to_store(self, node_obj); -- luacheck: ignore 211/err if not ok then node_obj.config = old_config; return ok, "internal-server-error"; -- cgit v1.2.3 From 6aca72acd8752ba17d1f5e1b3740c8fed22e81da Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 15 Jan 2020 21:08:01 +0100 Subject: util.array: Fix equality metamethod in Lua 5.3 Lua 5.2 only used the __eq metamethod if both operands have the same __eq, but Lua 5.3 will pick one from either operands that has one as long as both are tables. This results in array() == {} and all sorts of odd behavior, including array() == util.json.null. I think [array() == {}] should have the same semantics as {} == {} --- util/array.lua | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'util') diff --git a/util/array.lua b/util/array.lua index 0b60a4fd..32d2d6a5 100644 --- a/util/array.lua +++ b/util/array.lua @@ -10,6 +10,7 @@ local t_insert, t_sort, t_remove, t_concat = table.insert, table.sort, table.remove, table.concat; local setmetatable = setmetatable; +local getmetatable = getmetatable; local math_random = math.random; local math_floor = math.floor; local pairs, ipairs = pairs, ipairs; @@ -40,6 +41,10 @@ function array_mt.__add(a1, a2) end function array_mt.__eq(a, b) + if getmetatable(a) ~= array_mt or getmetatable(b) ~= array_mt then + -- Lua 5.3+ calls this if both operands are tables, even if metatables differ + return false; + end if #a == #b then for i = 1, #a do if a[i] ~= b[i] then -- cgit v1.2.3 From 4d0edf5ebdfe6f8dfd815526145e23b212449505 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 15 Jan 2020 21:16:08 +0100 Subject: util.ip: Fix equality metamethod for Lua 5.3 --- util/ip.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/ip.lua b/util/ip.lua index d1808225..d039e475 100644 --- a/util/ip.lua +++ b/util/ip.lua @@ -19,8 +19,14 @@ local ip_mt = { return ret; end, __tostring = function (ip) return ip.addr; end, - __eq = function (ipA, ipB) return ipA.packed == ipB.packed; end }; +ip_mt.__eq = function (ipA, ipB) + if getmetatable(ipA) ~= ip_mt or getmetatable(ipB) ~= ip_mt then + -- Lua 5.3+ calls this if both operands are tables, even if metatables differ + return false; + end + return ipA.packed == ipB.packed; +end local hex2bits = { ["0"] = "0000", ["1"] = "0001", ["2"] = "0010", ["3"] = "0011", -- cgit v1.2.3 From dc65ddc4a43daabb97ee1f2af7dedac7c5406688 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 15 Jan 2020 21:18:30 +0100 Subject: util.set: Fix equality metamethod in Lua 5.3 --- util/set.lua | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'util') diff --git a/util/set.lua b/util/set.lua index 02fabc6a..827a9158 100644 --- a/util/set.lua +++ b/util/set.lua @@ -8,6 +8,7 @@ local ipairs, pairs, setmetatable, next, tostring = ipairs, pairs, setmetatable, next, tostring; +local getmetatable = getmetatable; local t_concat = table.concat; local _ENV = nil; @@ -146,6 +147,11 @@ function set_mt.__div(set, func) return new_set; end function set_mt.__eq(set1, set2) + if getmetatable(set1) ~= set_mt or getmetatable(set2) ~= set_mt then + -- Lua 5.3+ calls this if both operands are tables, even if metatables differ + return false; + end + set1, set2 = set1._items, set2._items; for item in pairs(set1) do if not set2[item] then -- cgit v1.2.3 From c139f46ec0357c09c8fb7cd458ec16c53d8c751c Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 26 Jan 2020 16:40:21 +0100 Subject: util.prosodyctl: Pass command line flag to force daemonization on start Part of the deprecation of the 'daemonize' config option. Further, it is a bit weird to run `prosodyctl start` and get Prosody running in the foreground. --- util/prosodyctl.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua index 0787b8c3..ea697ffc 100644 --- a/util/prosodyctl.lua +++ b/util/prosodyctl.lua @@ -249,9 +249,9 @@ local function start(source_dir, lua) return false, "already-running"; end if not source_dir then - os.execute(lua .. "./prosody"); + os.execute(lua .. "./prosody -D"); else - os.execute(lua .. source_dir.."/../../bin/prosody"); + os.execute(lua .. source_dir.."/../../bin/prosody -D"); end return true; end -- cgit v1.2.3 From 43a5cfe0bc34a0dee2cfe255a0f0f909204b45b7 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 28 Jan 2020 12:46:59 +0000 Subject: util.startup: expose current process type (prosody/prosodyctl) in the global prosody object --- util/startup.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util') diff --git a/util/startup.lua b/util/startup.lua index 9b8d50f8..a799177c 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -589,6 +589,7 @@ end -- prosodyctl only function startup.prosodyctl() + prosody.process_type = "prosodyctl"; startup.parse_args(); startup.init_global_state(); startup.read_config(); @@ -611,6 +612,7 @@ end function startup.prosody() -- These actions are in a strict order, as many depend on -- previous steps to have already been performed + prosody.process_type = "prosody"; startup.parse_args(); startup.init_global_state(); startup.read_config(); -- cgit v1.2.3 From 9a4fe983315802ff15adea3d481718a0e4180be0 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 19 Feb 2020 21:38:00 +0100 Subject: util.startup: Break out command line argument parsing into util.argparse This will allow using it from other places such as prosodyctl sub-commands and plugins --- util/argparse.lua | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ util/startup.lua | 54 +++++---------------------------------------------- 2 files changed, 63 insertions(+), 49 deletions(-) create mode 100644 util/argparse.lua (limited to 'util') diff --git a/util/argparse.lua b/util/argparse.lua new file mode 100644 index 00000000..928fc3eb --- /dev/null +++ b/util/argparse.lua @@ -0,0 +1,58 @@ +local function parse(arg, config) + local short_params = config and config.short_params or {}; + local value_params = config and config.value_params or {}; + + local parsed_opts = {}; + + if #arg == 0 then + return parsed_opts; + end + while true do + local raw_param = arg[1]; + if not raw_param then + break; + end + + local prefix = raw_param:match("^%-%-?"); + if not prefix then + break; + elseif prefix == "--" and raw_param == "--" then + table.remove(arg, 1); + break; + end + local param = table.remove(arg, 1):sub(#prefix+1); + if #param == 1 and short_params then + param = short_params[param]; + end + + if not param then + print("Unknown command-line option: "..tostring(param)); + print("Perhaps you meant to use prosodyctl instead?"); + os.exit(1); + end + + local param_k, param_v; + if value_params[param] then + param_k, param_v = param, table.remove(arg, 1); + if not param_v then + print("Expected a value to follow command-line option: "..raw_param); + os.exit(1); + end + else + param_k, param_v = param:match("^([^=]+)=(.+)$"); + if not param_k then + if param:match("^no%-") then + param_k, param_v = param:sub(4), false; + else + param_k, param_v = param, true; + end + end + end + parsed_opts[param_k] = param_v; + end + return parsed_opts; +end + +return { + parse = parse; +} diff --git a/util/startup.lua b/util/startup.lua index a799177c..d45855f2 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -5,6 +5,7 @@ local startup = {}; local prosody = { events = require "util.events".new() }; local logger = require "util.logger"; local log = logger.init("startup"); +local parse_args = require "util.argparse".parse; local config = require "core.configmanager"; local config_warnings; @@ -17,55 +18,10 @@ local short_params = { D = "daemonize", F = "no-daemonize" }; local value_params = { config = true }; function startup.parse_args() - local parsed_opts = {}; - prosody.opts = parsed_opts; - - if #arg == 0 then - return; - end - while true do - local raw_param = arg[1]; - if not raw_param then - break; - end - - local prefix = raw_param:match("^%-%-?"); - if not prefix then - break; - elseif prefix == "--" and raw_param == "--" then - table.remove(arg, 1); - break; - end - local param = table.remove(arg, 1):sub(#prefix+1); - if #param == 1 then - param = short_params[param]; - end - - if not param then - print("Unknown command-line option: "..tostring(param)); - print("Perhaps you meant to use prosodyctl instead?"); - os.exit(1); - end - - local param_k, param_v; - if value_params[param] then - param_k, param_v = param, table.remove(arg, 1); - if not param_v then - print("Expected a value to follow command-line option: "..raw_param); - os.exit(1); - end - else - param_k, param_v = param:match("^([^=]+)=(.+)$"); - if not param_k then - if param:match("^no%-") then - param_k, param_v = param:sub(4), false; - else - param_k, param_v = param, true; - end - end - end - parsed_opts[param_k] = param_v; - end + prosody.opts = parse_args(arg, { + short_params = short_params, + value_params = value_params, + }); end function startup.read_config() -- cgit v1.2.3 From ac7d71241f20e2a598dacf7f163e9757e59bbdc3 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 24 Feb 2020 01:24:25 +0100 Subject: util.jwt: Basic JSON Web Token library supporting HS256 tokens --- util/jwt.lua | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 util/jwt.lua (limited to 'util') diff --git a/util/jwt.lua b/util/jwt.lua new file mode 100644 index 00000000..2b172d38 --- /dev/null +++ b/util/jwt.lua @@ -0,0 +1,50 @@ +local s_gsub = string.gsub; +local json = require "util.json"; +local hashes = require "util.hashes"; +local base64_encode = require "util.encodings".base64.encode; +local base64_decode = require "util.encodings".base64.decode; + +local b64url_rep = { ["+"] = "-", ["/"] = "_", ["="] = "", ["-"] = "+", ["_"] = "/" }; +local function b64url(data) + return (s_gsub(base64_encode(data), "[+/=]", b64url_rep)); +end +local function unb64url(data) + return base64_decode(s_gsub(data, "[-_]", b64url_rep).."=="); +end + +local static_header = b64url('{"alg":"HS256","typ":"JWT"}') .. '.'; + +local function sign(key, payload) + local encoded_payload = json.encode(payload); + local signed = static_header .. b64url(encoded_payload); + local signature = hashes.hmac_sha256(key, signed); + return signed .. "." .. b64url(signature); +end + +local jwt_pattern = "^(([A-Za-z0-9-_]+)%.([A-Za-z0-9-_]+))%.([A-Za-z0-9-_]+)$" +local function verify(key, blob) + local signed, bheader, bpayload, signature = string.match(blob, jwt_pattern); + if not signed then + return nil, "invalid-encoding"; + end + local header = json.decode(unb64url(bheader)); + if not header or type(header) ~= "table" then + return nil, "invalid-header"; + elseif header.alg ~= "HS256" then + return nil, "unsupported-algorithm"; + end + if b64url(hashes.hmac_sha256(key, signed)) ~= signature then + return false, "signature-mismatch"; + end + local payload, err = json.decode(unb64url(bpayload)); + if err ~= nil then + return nil, "json-decode-error"; + end + return true, payload; +end + +return { + sign = sign; + verify = verify; +}; + -- cgit v1.2.3 From 7137e467a0a251a18dbade4d32b4222dae475231 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 26 Feb 2020 00:59:35 +0100 Subject: util.adhoc: Allow passing dataforms in initial command This might not be quite legal per XEP-0050, but makes it possible to call simpler commands without keeping state across another roundtrip. --- util/adhoc.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/adhoc.lua b/util/adhoc.lua index d81b8242..19c94c34 100644 --- a/util/adhoc.lua +++ b/util/adhoc.lua @@ -2,7 +2,7 @@ local function new_simple_form(form, result_handler) return function(self, data, state) - if state then + if state or data.form then if data.action == "cancel" then return { status = "canceled" }; end @@ -16,7 +16,7 @@ end local function new_initial_data_form(form, initial_data, result_handler) return function(self, data, state) - if state then + if state or data.form then if data.action == "cancel" then return { status = "canceled" }; end -- cgit v1.2.3 From b4bd0fc355b30083da874b2be0be5ee1e783b908 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 12 Apr 2020 17:03:05 +0200 Subject: util.stanza: Add method returning stanza with added indentation Adds indentation and line breaks to stanzas, to make stanzas easier to read for humans. --- util/stanza.lua | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'util') diff --git a/util/stanza.lua b/util/stanza.lua index f5cd5668..a8a417ab 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -511,6 +511,36 @@ else stanza_mt.pretty_top_tag = stanza_mt.top_tag; end +function stanza_mt.indent(t, level, indent) + if #t == 0 or (#t == 1 and type(t[1]) == "string") then + -- Empty nodes wouldn't have any indentation + -- Text-only nodes are preserved as to not alter the text content + -- Optimization: Skip clone of these since we don't alter them + return t; + end + + indent = indent or "\t"; + level = level or 1; + local tag = clone(t, true); + + for child in t:children() do + if type(child) == "string" then + -- Already indented text would look weird but let's ignore that for now. + if child:find("%S") then + tag:text("\n" .. indent:rep(level)); + tag:text(child); + end + elseif is_stanza(child) then + tag:text("\n" .. indent:rep(level)); + tag:add_direct_child(child:indent(level+1, indent)); + end + end + -- before the closing tag + tag:text("\n" .. indent:rep((level-1))); + + return tag; +end + return { stanza_mt = stanza_mt; stanza = new_stanza; -- cgit v1.2.3 From d341deca9e5855e0bf94ff02fd64515a80bd63dc Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 14 Apr 2020 16:51:24 +0200 Subject: util.sasl.digest-md5: Remove, obsolete since 2011 RFC 6331 lists several problems with this outdated authentication mechanism. The code here was also completely ignored by lint checks and has probably not been used for a long time, as it is incompatible with SCRAM-hashed password storage. --- util/sasl.lua | 1 - util/sasl/digest-md5.lua | 251 ----------------------------------------------- 2 files changed, 252 deletions(-) delete mode 100644 util/sasl/digest-md5.lua (limited to 'util') diff --git a/util/sasl.lua b/util/sasl.lua index 50851405..fc2abdf3 100644 --- a/util/sasl.lua +++ b/util/sasl.lua @@ -134,7 +134,6 @@ end -- load the mechanisms require "util.sasl.plain" .init(registerMechanism); -require "util.sasl.digest-md5".init(registerMechanism); require "util.sasl.anonymous" .init(registerMechanism); require "util.sasl.scram" .init(registerMechanism); require "util.sasl.external" .init(registerMechanism); diff --git a/util/sasl/digest-md5.lua b/util/sasl/digest-md5.lua deleted file mode 100644 index 7542a037..00000000 --- a/util/sasl/digest-md5.lua +++ /dev/null @@ -1,251 +0,0 @@ --- sasl.lua v0.4 --- Copyright (C) 2008-2010 Tobias Markmann --- --- All rights reserved. --- --- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: --- --- * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. --- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. --- * Neither the name of Tobias Markmann nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. --- --- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -local tostring = tostring; -local type = type; - -local s_gmatch = string.gmatch; -local s_match = string.match; -local t_concat = table.concat; -local t_insert = table.insert; -local to_byte, to_char = string.byte, string.char; - -local md5 = require "util.hashes".md5; -local log = require "util.logger".init("sasl"); -local generate_uuid = require "util.uuid".generate; -local nodeprep = require "util.encodings".stringprep.nodeprep; - -local _ENV = nil; --- luacheck: std none - ---========================= ---SASL DIGEST-MD5 according to RFC 2831 - ---[[ -Supported Authentication Backends - -digest_md5: - function(username, domain, realm, encoding) -- domain and realm are usually the same; for some broken - -- implementations it's not - return digesthash, state; - end - -digest_md5_test: - function(username, domain, realm, encoding, digesthash) - return true or false, state; - end -]] - -local function digest(self, message) - --TODO complete support for authzid - - local function serialize(message) - local data = "" - - -- testing all possible values - if message["realm"] then data = data..[[realm="]]..message.realm..[[",]] end - if message["nonce"] then data = data..[[nonce="]]..message.nonce..[[",]] end - if message["qop"] then data = data..[[qop="]]..message.qop..[[",]] end - if message["charset"] then data = data..[[charset=]]..message.charset.."," end - if message["algorithm"] then data = data..[[algorithm=]]..message.algorithm.."," end - if message["rspauth"] then data = data..[[rspauth=]]..message.rspauth.."," end - data = data:gsub(",$", "") - return data - end - - local function utf8tolatin1ifpossible(passwd) - local i = 1; - while i <= #passwd do - local passwd_i = to_byte(passwd:sub(i, i)); - if passwd_i > 0x7F then - if passwd_i < 0xC0 or passwd_i > 0xC3 then - return passwd; - end - i = i + 1; - passwd_i = to_byte(passwd:sub(i, i)); - if passwd_i < 0x80 or passwd_i > 0xBF then - return passwd; - end - end - i = i + 1; - end - - local p = {}; - local j = 0; - i = 1; - while (i <= #passwd) do - local passwd_i = to_byte(passwd:sub(i, i)); - if passwd_i > 0x7F then - i = i + 1; - local passwd_i_1 = to_byte(passwd:sub(i, i)); - t_insert(p, to_char(passwd_i%4*64 + passwd_i_1%64)); -- I'm so clever - else - t_insert(p, to_char(passwd_i)); - end - i = i + 1; - end - return t_concat(p); - end - local function latin1toutf8(str) - local p = {}; - for ch in s_gmatch(str, ".") do - ch = to_byte(ch); - if (ch < 0x80) then - t_insert(p, to_char(ch)); - elseif (ch < 0xC0) then - t_insert(p, to_char(0xC2, ch)); - else - t_insert(p, to_char(0xC3, ch - 64)); - end - end - return t_concat(p); - end - local function parse(data) - local message = {} - -- COMPAT: %z in the pattern to work around jwchat bug (sends "charset=utf-8\0") - for k, v in s_gmatch(data, [[([%w%-]+)="?([^",%z]*)"?,?]]) do -- FIXME The hacky regex makes me shudder - message[k] = v; - end - return message; - end - - if not self.nonce then - self.nonce = generate_uuid(); - self.step = 0; - self.nonce_count = {}; - end - - self.step = self.step + 1; - if (self.step == 1) then - local challenge = serialize({ nonce = self.nonce, - qop = "auth", - charset = "utf-8", - algorithm = "md5-sess", - realm = self.realm}); - return "challenge", challenge; - elseif (self.step == 2) then - local response = parse(message); - -- check for replay attack - if response["nc"] then - if self.nonce_count[response["nc"]] then return "failure", "not-authorized" end - end - - -- check for username, it's REQUIRED by RFC 2831 - local username = response["username"]; - local _nodeprep = self.profile.nodeprep; - if username and _nodeprep ~= false then - username = (_nodeprep or nodeprep)(username); -- FIXME charset - end - if not username or username == "" then - return "failure", "malformed-request"; - end - self.username = username; - - -- check for nonce, ... - if not response["nonce"] then - return "failure", "malformed-request"; - else - -- check if it's the right nonce - if response["nonce"] ~= tostring(self.nonce) then return "failure", "malformed-request" end - end - - if not response["cnonce"] then return "failure", "malformed-request", "Missing entry for cnonce in SASL message." end - if not response["qop"] then response["qop"] = "auth" end - - if response["realm"] == nil or response["realm"] == "" then - response["realm"] = ""; - elseif response["realm"] ~= self.realm then - return "failure", "not-authorized", "Incorrect realm value"; - end - - local decoder; - if response["charset"] == nil then - decoder = utf8tolatin1ifpossible; - elseif response["charset"] ~= "utf-8" then - return "failure", "incorrect-encoding", "The client's response uses "..response["charset"].." for encoding with isn't supported by sasl.lua. Supported encodings are latin or utf-8."; - end - - local domain = ""; - local protocol = ""; - if response["digest-uri"] then - protocol, domain = response["digest-uri"]:match("(%w+)/(.*)$"); - if protocol == nil or domain == nil then return "failure", "malformed-request" end - else - return "failure", "malformed-request", "Missing entry for digest-uri in SASL message." - end - - --TODO maybe realm support - local Y, state; - if self.profile.plain then - local password, state = self.profile.plain(self, response["username"], self.realm) - if state == nil then return "failure", "not-authorized" - elseif state == false then return "failure", "account-disabled" end - Y = md5(response["username"]..":"..response["realm"]..":"..password); - elseif self.profile["digest-md5"] then - Y, state = self.profile["digest-md5"](self, response["username"], self.realm, response["realm"], response["charset"]) - if state == nil then return "failure", "not-authorized" - elseif state == false then return "failure", "account-disabled" end - elseif self.profile["digest-md5-test"] then - -- TODO - end - --local password_encoding, Y = self.credentials_handler("DIGEST-MD5", response["username"], self.realm, response["realm"], decoder); - --if Y == nil then return "failure", "not-authorized" - --elseif Y == false then return "failure", "account-disabled" end - local A1 = ""; - if response.authzid then - if response.authzid == self.username or response.authzid == self.username.."@"..self.realm then - -- COMPAT - log("warn", "Client is violating RFC 3920 (section 6.1, point 7)."); - A1 = Y..":"..response["nonce"]..":"..response["cnonce"]..":"..response.authzid; - else - return "failure", "invalid-authzid"; - end - else - A1 = Y..":"..response["nonce"]..":"..response["cnonce"]; - end - local A2 = "AUTHENTICATE:"..protocol.."/"..domain; - - local HA1 = md5(A1, true); - local HA2 = md5(A2, true); - - local KD = HA1..":"..response["nonce"]..":"..response["nc"]..":"..response["cnonce"]..":"..response["qop"]..":"..HA2; - local response_value = md5(KD, true); - - if response_value == response["response"] then - -- calculate rspauth - A2 = ":"..protocol.."/"..domain; - - HA1 = md5(A1, true); - HA2 = md5(A2, true); - - KD = HA1..":"..response["nonce"]..":"..response["nc"]..":"..response["cnonce"]..":"..response["qop"]..":"..HA2 - local rspauth = md5(KD, true); - self.authenticated = true; - --TODO: considering sending the rspauth in a success node for saving one roundtrip; allowed according to http://tools.ietf.org/html/draft-saintandre-rfc3920bis-09#section-7.3.6 - return "challenge", serialize({rspauth = rspauth}); - else - return "failure", "not-authorized", "The response provided by the client doesn't match the one we calculated." - end - elseif self.step == 3 then - if self.authenticated ~= nil then return "success" - else return "failure", "malformed-request" end - end -end - -local function init(registerMechanism) - registerMechanism("DIGEST-MD5", {"plain"}, digest); -end - -return { - init = init; -} -- cgit v1.2.3 From d0b6d2010c6badde88a1493ea67fc19d94ccff4c Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 23 Apr 2020 18:16:36 +0200 Subject: util.rsm: Fix passing number as attribute --- util/rsm.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/rsm.lua b/util/rsm.lua index 40a78fb5..b5afd62a 100644 --- a/util/rsm.lua +++ b/util/rsm.lua @@ -11,9 +11,14 @@ local stanza = require"util.stanza".stanza; local tostring, tonumber = tostring, tonumber; +local s_format = string.format; local type = type; local pairs = pairs; +local function inttostr(n) + return s_format("%d", n); +end + local xmlns_rsm = 'http://jabber.org/protocol/rsm'; local element_parsers = {}; @@ -45,7 +50,7 @@ end local element_generators = setmetatable({ first = function(st, data) if type(data) == "table" then - st:tag("first", { index = data.index }):text(data[1]):up(); + st:tag("first", { index = inttostr(data.index) }):text(data[1]):up(); else st:tag("first"):text(tostring(data)):up(); end -- cgit v1.2.3 From e39789a5fff23500a7ff393ea67d7bf80175d9be Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 23 Apr 2020 18:40:57 +0200 Subject: util.rsm: Explicitly serialize numbers in correct format --- util/rsm.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/rsm.lua b/util/rsm.lua index b5afd62a..aade3c19 100644 --- a/util/rsm.lua +++ b/util/rsm.lua @@ -61,7 +61,13 @@ local element_generators = setmetatable({ else st:tag("before"):text(tostring(data)):up(); end - end + end; + max = function (st, data) + st:tag("max"):text(inttostr(data)):up(); + end; + count = function (st, data) + st:tag("count"):text(inttostr(data)):up(); + end; }, { __index = function(_, name) return function(st, data) -- cgit v1.2.3 From 40788f772440e610aa50936e330f5a067ae6fd58 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 23 Apr 2020 18:42:47 +0200 Subject: util.rsm: Don't convert values to strings that should already be strings Causes util.stanza to throw an error, which helps detect mistakes --- util/rsm.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'util') diff --git a/util/rsm.lua b/util/rsm.lua index aade3c19..ad725d76 100644 --- a/util/rsm.lua +++ b/util/rsm.lua @@ -10,7 +10,7 @@ -- local stanza = require"util.stanza".stanza; -local tostring, tonumber = tostring, tonumber; +local tonumber = tonumber; local s_format = string.format; local type = type; local pairs = pairs; @@ -52,14 +52,14 @@ local element_generators = setmetatable({ if type(data) == "table" then st:tag("first", { index = inttostr(data.index) }):text(data[1]):up(); else - st:tag("first"):text(tostring(data)):up(); + st:tag("first"):text(data):up(); end end; before = function(st, data) if data == true then st:tag("before"):up(); else - st:tag("before"):text(tostring(data)):up(); + st:tag("before"):text(data):up(); end end; max = function (st, data) @@ -71,7 +71,7 @@ local element_generators = setmetatable({ }, { __index = function(_, name) return function(st, data) - st:tag(name):text(tostring(data)):up(); + st:tag(name):text(data):up(); end end; }); -- cgit v1.2.3 From 3f78f7638d37ea987beea1f06789582a34ef71d5 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 26 Apr 2020 14:31:43 +0200 Subject: util.sasl.scram: Mention if clients try PLUS without channel binding This isn't normal, but is it invalid? Likely a client bug in any case. --- util/sasl/scram.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/sasl/scram.lua b/util/sasl/scram.lua index e2ce00f5..b3370d4b 100644 --- a/util/sasl/scram.lua +++ b/util/sasl/scram.lua @@ -112,7 +112,7 @@ local function get_scram_hasher(H, HMAC, Hi) end end -local function scram_gen(hash_name, H_f, HMAC_f, get_auth_db) +local function scram_gen(hash_name, H_f, HMAC_f, get_auth_db, expect_cb) local profile_name = "scram_" .. hashprep(hash_name); local function scram_hash(self, message) local support_channel_binding = false; @@ -141,6 +141,10 @@ local function scram_gen(hash_name, H_f, HMAC_f, get_auth_db) if gs2_cbind_flag == "n" then -- "n" -> client doesn't support channel binding. + if expect_cb then + log("debug", "Client unexpectedly doesn't support channel binding"); + -- XXX Is it sensible to abort if the client starts -PLUS but doesn't use channel binding? + end support_channel_binding = false; end @@ -260,7 +264,7 @@ local function init(registerMechanism) -- register channel binding equivalent registerMechanism("SCRAM-"..hash_name.."-PLUS", {"plain", "scram_"..(hashprep(hash_name))}, - scram_gen(hash_name:lower(), hash, hmac_hash, get_auth_db), {"tls-unique"}); + scram_gen(hash_name:lower(), hash, hmac_hash, get_auth_db, true), {"tls-unique"}); end registerSCRAMMechanism("SHA-1", hashes.sha1, hashes.hmac_sha1, hashes.pbkdf2_hmac_sha1); -- cgit v1.2.3 From 26d3ffdd4a4a8752609ffdb6f1670e0c603db42c Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 1 Jun 2020 15:42:19 +0100 Subject: mod_admin_socket, util.adminstream: New module to manage a local unix domain socket for admin functionality --- util/adminstream.lua | 285 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 285 insertions(+) create mode 100644 util/adminstream.lua (limited to 'util') diff --git a/util/adminstream.lua b/util/adminstream.lua new file mode 100644 index 00000000..186cb0e9 --- /dev/null +++ b/util/adminstream.lua @@ -0,0 +1,285 @@ +local st = require "util.stanza"; +local new_xmpp_stream = require "util.xmppstream".new; +local sessionlib = require "util.session"; +local gettime = require "util.time".now; +local runner = require "util.async".runner; +local add_task = require "util.timer".add_task; +local events = require "util.events"; + +local stream_close_timeout = 5; + +local log = require "util.logger".init("adminstream"); + +local xmlns_xmpp_streams = "urn:ietf:params:xml:ns:xmpp-streams"; + +local stream_callbacks = { default_ns = "xmpp:prosody.im/admin" }; + +function stream_callbacks.streamopened(session, attr) + -- run _streamopened in async context + session.thread:run({ stream = "opened", attr = attr }); +end + +function stream_callbacks._streamopened(session, attr) --luacheck: ignore 212/attr + if session.type ~= "client" then + session:open_stream(); + end + session.notopen = nil; +end + +function stream_callbacks.streamclosed(session, attr) + -- run _streamclosed in async context + session.thread:run({ stream = "closed", attr = attr }); +end + +function stream_callbacks._streamclosed(session) + session.log("debug", "Received "); + session:close(false); +end + +function stream_callbacks.error(session, error, data) + if error == "no-stream" then + session.log("debug", "Invalid opening stream header (%s)", (data:gsub("^([^\1]+)\1", "{%1}"))); + session:close("invalid-namespace"); + elseif error == "parse-error" then + session.log("debug", "Client XML parse error: %s", data); + session:close("not-well-formed"); + elseif error == "stream-error" then + local condition, text = "undefined-condition"; + for child in data:childtags(nil, xmlns_xmpp_streams) do + if child.name ~= "text" then + condition = child.name; + else + text = child:get_text(); + end + if condition ~= "undefined-condition" and text then + break; + end + end + text = condition .. (text and (" ("..text..")") or ""); + session.log("info", "Session closed by remote with error: %s", text); + session:close(nil, text); + end +end + +function stream_callbacks.handlestanza(session, stanza) + session.thread:run(stanza); +end + +local runner_callbacks = {}; + +function runner_callbacks:error(err) + self.data.log("error", "Traceback[c2s]: %s", err); +end + +local stream_xmlns_attr = {xmlns='urn:ietf:params:xml:ns:xmpp-streams'}; + +local function destroy_session(session, reason) + if session.destroyed then return; end + session.destroyed = true; + session.log("debug", "Destroying session: %s", reason or "unknown reason"); +end + +local function session_close(session, reason) + local log = session.log or log; + if session.conn then + if session.notopen then + session:open_stream(); + end + if reason then -- nil == no err, initiated by us, false == initiated by client + local stream_error = st.stanza("stream:error"); + if type(reason) == "string" then -- assume stream error + stream_error:tag(reason, {xmlns = 'urn:ietf:params:xml:ns:xmpp-streams' }); + elseif type(reason) == "table" then + if reason.condition then + stream_error:tag(reason.condition, stream_xmlns_attr):up(); + if reason.text then + stream_error:tag("text", stream_xmlns_attr):text(reason.text):up(); + end + if reason.extra then + stream_error:add_child(reason.extra); + end + elseif reason.name then -- a stanza + stream_error = reason; + end + end + stream_error = tostring(stream_error); + log("debug", "Disconnecting client, is: %s", stream_error); + session.send(stream_error); + end + + session.send(""); + function session.send() return false; end + + local reason_text = (reason and (reason.name or reason.text or reason.condition)) or reason; + session.log("debug", "c2s stream for %s closed: %s", session.full_jid or session.ip or "", reason_text or "session closed"); + + -- Authenticated incoming stream may still be sending us stanzas, so wait for from remote + local conn = session.conn; + if reason_text == nil and not session.notopen and session.type == "c2s" then + -- Grace time to process data from authenticated cleanly-closed stream + add_task(stream_close_timeout, function () + if not session.destroyed then + session.log("warn", "Failed to receive a stream close response, closing connection anyway..."); + destroy_session(session); + conn:close(); + end + end); + else + destroy_session(session, reason_text); + conn:close(); + end + else + local reason_text = (reason and (reason.name or reason.text or reason.condition)) or reason; + destroy_session(session, reason_text); + end +end + +--- Public methods + +local function new_server(sessions, stanza_handler) + local listeners = {}; + + function listeners.onconnect(conn) + log("debug", "New connection"); + local session = sessionlib.new("admin"); + sessionlib.set_id(session); + sessionlib.set_logger(session); + sessionlib.set_conn(session, conn); + + session.conntime = gettime(); + session.type = "admin"; + + local stream = new_xmpp_stream(session, stream_callbacks); + session.stream = stream; + session.notopen = true; + + session.thread = runner(function (stanza) + if st.is_stanza(stanza) then + stanza_handler(session, stanza); + elseif stanza.stream == "opened" then + stream_callbacks._streamopened(session, stanza.attr); + elseif stanza.stream == "closed" then + stream_callbacks._streamclosed(session, stanza.attr); + end + end, runner_callbacks, session); + + function session.data(data) + -- Parse the data, which will store stanzas in session.pending_stanzas + if data then + local ok, err = stream:feed(data); + if not ok then + session.log("debug", "Received invalid XML (%s) %d bytes: %q", err, #data, data:sub(1, 300)); + session:close("not-well-formed"); + end + end + end + + session.close = session_close; + + session.send = function (t) + session.log("debug", "Sending[%s]: %s", session.type, t.top_tag and t:top_tag() or t:match("^[^>]*>?")); + return session.rawsend(tostring(t)); + end + + function session.rawsend(t) + local ret, err = conn:write(t); + if not ret then + session.log("debug", "Error writing to connection: %s", err); + return false, err; + end + return true; + end + + sessions[conn] = session; + end + + function listeners.onincoming(conn, data) + local session = sessions[conn]; + if session then + session.data(data); + end + end + + function listeners.ondisconnect(conn, err) + local session = sessions[conn]; + if session then + session.log("info", "Admin client disconnected: %s", err or "connection closed"); + session.conn = nil; + sessions[conn] = nil; + end + end + return { + listeners = listeners; + }; +end + +local function new_client() + local client = { + type = "client"; + events = events.new(); + log = log; + }; + + local listeners = {}; + + function listeners.onconnect(conn) + log("debug", "Connected"); + client.conn = conn; + + local stream = new_xmpp_stream(client, stream_callbacks); + client.stream = stream; + client.notopen = true; + + client.thread = runner(function (stanza) + if st.is_stanza(stanza) then + client.events.fire_event("received", stanza); + elseif stanza.stream == "opened" then + stream_callbacks._streamopened(client, stanza.attr); + client.events.fire_event("connected"); + elseif stanza.stream == "closed" then + client.events.fire_event("disconnected"); + stream_callbacks._streamclosed(client, stanza.attr); + end + end, runner_callbacks, client); + + client.close = session_close; + + function client.send(t) + client.log("debug", "Sending: %s", t.top_tag and t:top_tag() or t:match("^[^>]*>?")); + return client.rawsend(tostring(t)); + end + + function client.rawsend(t) + local ret, err = conn:write(t); + if not ret then + client.log("debug", "Error writing to connection: %s", err); + return false, err; + end + return true; + end + client.log("debug", "Opening stream..."); + client:open_stream(); + end + + function listeners.onincoming(conn, data) --luacheck: ignore 212/conn + local ok, err = client.stream:feed(data); + if not ok then + client.log("debug", "Received invalid XML (%s) %d bytes: %q", err, #data, data:sub(1, 300)); + client:close("not-well-formed"); + end + end + + function listeners.ondisconnect(conn, err) --luacheck: ignore 212/conn + client.log("info", "Admin client disconnected: %s", err or "connection closed"); + client.conn = nil; + end + + client.listeners = listeners; + + return client; +end + +return { + server = new_server; + client = new_client; +}; -- cgit v1.2.3 From f9176ca0e930cd142df047178f9e585b56275ddf Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 1 Jun 2020 15:44:44 +0100 Subject: prosodyctl, util.prosodyctl.shell: `prosodyctl shell` - a client to access the prosodyctl admin shell --- util/prosodyctl/shell.lua | 125 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 util/prosodyctl/shell.lua (limited to 'util') diff --git a/util/prosodyctl/shell.lua b/util/prosodyctl/shell.lua new file mode 100644 index 00000000..0b84eaca --- /dev/null +++ b/util/prosodyctl/shell.lua @@ -0,0 +1,125 @@ +local have_unix, unix = pcall(require, "socket.unix"); + +if not have_unix or type(unix) ~= "table" then + print("** LuaSocket unix socket support not available or incompatible, ensure your"); + print("** version is up to date."); + os.exit(1); +end + +local server = require "net.server"; +local st = require "util.stanza"; + +local have_readline, readline = pcall(require, "readline"); + +local adminstream = require "util.adminstream"; + +if have_readline then + readline.set_readline_name("prosody"); +end + +local function read_line() + if have_readline then + return readline.readline("prosody> "); + else + io.write("prosody> "); + return io.read("*line"); + end +end + +local function send_line(client, line) + client.send(st.stanza("repl-line"):text(line)); +end + +local function repl(client) + local line = read_line(); + if not line or line == "quit" or line == "exit" or line == "bye" then + if not line then + print(""); + end + os.exit(); + end + send_line(client, line); +end + +local function connection(socket_path, listeners) + local conn, sock; + + return { + connect = function () + if sock or conn then + return nil, "already connected"; + end + sock = unix.stream(); + sock:settimeout(0); + local ok, err = sock:connect(socket_path); + if not ok then + return nil, err; + end + conn = server.wrapclient(sock, nil, nil, listeners, "*a"); + return true; + end; + disconnect = function () + if conn then + conn:close(); + conn = nil; + end + if sock then + sock:close(); + sock = nil; + end + return true; + end; + }; +end + +local function printbanner() + print([[ + ____ \ / _ + | _ \ _ __ ___ ___ _-_ __| |_ _ + | |_) | '__/ _ \/ __|/ _ \ / _` | | | | + | __/| | | (_) \__ \ |_| | (_| | |_| | + |_| |_| \___/|___/\___/ \__,_|\__, | + A study in simplicity |___/ + +]]); + print("Welcome to the Prosody administration console. For a list of commands, type: help"); + print("You may find more help on using this console in our online documentation at "); + print("https://prosody.im/doc/console\n"); +end + +local function start(arg) --luacheck: ignore 212/arg + local client = adminstream.client(); + + client.events.add_handler("connected", function () + if not arg.quiet then + printbanner(); + end + repl(client); + end); + + client.events.add_handler("disconnected", function () + print("--- session closed ---"); + os.exit(); + end); + + client.events.add_handler("received", function (stanza) + if stanza.name == "repl-result" then + local result_prefix = stanza.attr.type == "error" and "!" or "|"; + print(result_prefix.." "..stanza:get_text()); + repl(client); + end + end); + + local conn = connection("data/prosody.sock", client.listeners); + local ok, err = conn:connect(); + if not ok then + print("** Unable to connect to server - is it running? Is mod_admin_shell enabled?"); + print("** Connection error: "..err); + os.exit(1); + end + server.loop(); +end + +return { + start = start; +}; -- cgit v1.2.3 From cdeaa5882177f00e6eb3262c7428486874faab28 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 1 Jun 2020 16:14:06 +0100 Subject: mod_admin_shell, mod_admin_telnet, util.prosodyctl.shell: Separate output from final result Fixes the client pausing for input after output from commands. --- util/prosodyctl/shell.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/prosodyctl/shell.lua b/util/prosodyctl/shell.lua index 0b84eaca..3e70e8f1 100644 --- a/util/prosodyctl/shell.lua +++ b/util/prosodyctl/shell.lua @@ -27,7 +27,7 @@ local function read_line() end local function send_line(client, line) - client.send(st.stanza("repl-line"):text(line)); + client.send(st.stanza("repl-input"):text(line)); end local function repl(client) @@ -103,9 +103,11 @@ local function start(arg) --luacheck: ignore 212/arg end); client.events.add_handler("received", function (stanza) - if stanza.name == "repl-result" then + if stanza.name == "repl-output" or stanza.name == "repl-result" then local result_prefix = stanza.attr.type == "error" and "!" or "|"; print(result_prefix.." "..stanza:get_text()); + end + if stanza.name == "repl-result" then repl(client); end end); -- cgit v1.2.3 From fef43e354399548ed548e992d18f17515d7bd087 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 1 Jun 2020 23:26:30 +0200 Subject: util.prosodyctl.shell: Join socket path with current data directory Don't hardcode socket path as it happens to be in a source checkout. Hold on, it should use the same config option as the module! --- util/prosodyctl/shell.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/prosodyctl/shell.lua b/util/prosodyctl/shell.lua index 3e70e8f1..6d5ea116 100644 --- a/util/prosodyctl/shell.lua +++ b/util/prosodyctl/shell.lua @@ -8,6 +8,7 @@ end local server = require "net.server"; local st = require "util.stanza"; +local path = require "util.paths"; local have_readline, readline = pcall(require, "readline"); @@ -112,7 +113,8 @@ local function start(arg) --luacheck: ignore 212/arg end end); - local conn = connection("data/prosody.sock", client.listeners); + local socket_path = path.join(prosody.paths.data, "prosody.sock"); + local conn = connection(socket_path, client.listeners); local ok, err = conn:connect(); if not ok then print("** Unable to connect to server - is it running? Is mod_admin_shell enabled?"); -- cgit v1.2.3 From 360dac00c284c855dd251e709a419dac62d6a214 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 1 Jun 2020 23:53:03 +0200 Subject: util.prosodyctl.shell: Use same config option as module for socket path So now if you set it to a custom value, both the client and the server should use it. --- util/prosodyctl/shell.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/prosodyctl/shell.lua b/util/prosodyctl/shell.lua index 6d5ea116..0e019152 100644 --- a/util/prosodyctl/shell.lua +++ b/util/prosodyctl/shell.lua @@ -6,6 +6,7 @@ if not have_unix or type(unix) ~= "table" then os.exit(1); end +local config = require "core.configmanager"; local server = require "net.server"; local st = require "util.stanza"; local path = require "util.paths"; @@ -113,7 +114,7 @@ local function start(arg) --luacheck: ignore 212/arg end end); - local socket_path = path.join(prosody.paths.data, "prosody.sock"); + local socket_path = path.resolve_relative_path(prosody.paths.data, config.get("*", "admin_socket") or "prosody.sock"); local conn = connection(socket_path, client.listeners); local ok, err = conn:connect(); if not ok then -- cgit v1.2.3 From 431e3bcc54c79dc521786cbce5a16116d5b25eb8 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 1 Jun 2020 23:57:50 +0200 Subject: util.prosodyctl.shell: Allow passing path to socket on command line E.g. `prosodyctl shell --socket /path/to/prosody.scok` --- util/prosodyctl/shell.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/prosodyctl/shell.lua b/util/prosodyctl/shell.lua index 0e019152..cbcea927 100644 --- a/util/prosodyctl/shell.lua +++ b/util/prosodyctl/shell.lua @@ -114,7 +114,7 @@ local function start(arg) --luacheck: ignore 212/arg end end); - local socket_path = path.resolve_relative_path(prosody.paths.data, config.get("*", "admin_socket") or "prosody.sock"); + local socket_path = path.resolve_relative_path(prosody.paths.data, arg.socket or config.get("*", "admin_socket") or "prosody.sock"); local conn = connection(socket_path, client.listeners); local ok, err = conn:connect(); if not ok then -- cgit v1.2.3 From dc9ce16d648c4cb2c953f871aaad84474bf380fb Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 2 Jun 2020 08:00:37 +0100 Subject: util.human.io: New central place for UI helpers --- util/human/io.lua | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 util/human/io.lua (limited to 'util') diff --git a/util/human/io.lua b/util/human/io.lua new file mode 100644 index 00000000..bfd1c00d --- /dev/null +++ b/util/human/io.lua @@ -0,0 +1,96 @@ +local function getchar(n) + local stty_ret = os.execute("stty raw -echo 2>/dev/null"); + local ok, char; + if stty_ret == true or stty_ret == 0 then + ok, char = pcall(io.read, n or 1); + os.execute("stty sane"); + else + ok, char = pcall(io.read, "*l"); + if ok then + char = char:sub(1, n or 1); + end + end + if ok then + return char; + end +end + +local function getline() + local ok, line = pcall(io.read, "*l"); + if ok then + return line; + end +end + +local function getpass() + local stty_ret, _, status_code = os.execute("stty -echo 2>/dev/null"); + if status_code then -- COMPAT w/ Lua 5.1 + stty_ret = status_code; + end + if stty_ret ~= 0 then + io.write("\027[08m"); -- ANSI 'hidden' text attribute + end + local ok, pass = pcall(io.read, "*l"); + if stty_ret == 0 then + os.execute("stty sane"); + else + io.write("\027[00m"); + end + io.write("\n"); + if ok then + return pass; + end +end + +local function show_yesno(prompt) + io.write(prompt, " "); + local choice = getchar():lower(); + io.write("\n"); + if not choice:match("%a") then + choice = prompt:match("%[.-(%U).-%]$"); + if not choice then return nil; end + end + return (choice == "y"); +end + +local function read_password() + local password; + while true do + io.write("Enter new password: "); + password = getpass(); + if not password then + print("No password - cancelled"); + return; + end + io.write("Retype new password: "); + if getpass() ~= password then + if not show_yesno [=[Passwords did not match, try again? [Y/n]]=] then + return; + end + else + break; + end + end + return password; +end + +local function show_prompt(prompt) + io.write(prompt, " "); + local line = getline(); + line = line and line:gsub("\n$",""); + return (line and #line > 0) and line or nil; +end + +local function printf(fmt, ...) + print(msg:format(...)); +end + +return { + getchar = getchar; + getline = getline; + getpass = getpass; + show_yesno = show_yesno; + read_password = read_password; + show_prompt = show_prompt; + printf = printf; +}; -- cgit v1.2.3 From 225aaa81ca334414c7736050384897578796b9f3 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 2 Jun 2020 08:01:21 +0100 Subject: prosodyctl+util.prosodyctl.*: Start breaking up the ever-growing prosodyctl --- util/prosodyctl.lua | 109 ++-------- util/prosodyctl/cert.lua | 293 +++++++++++++++++++++++++ util/prosodyctl/check.lua | 530 ++++++++++++++++++++++++++++++++++++++++++++++ util/prosodyctl/shell.lua | 2 +- 4 files changed, 840 insertions(+), 94 deletions(-) create mode 100644 util/prosodyctl/cert.lua create mode 100644 util/prosodyctl/check.lua (limited to 'util') diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua index ea697ffc..cb86a5a1 100644 --- a/util/prosodyctl.lua +++ b/util/prosodyctl.lua @@ -15,7 +15,6 @@ local usermanager = require "core.usermanager"; local signal = require "util.signal"; local set = require "util.set"; local lfs = require "lfs"; -local pcall = pcall; local type = type; local nodeprep, nameprep = stringprep.nodeprep, stringprep.nameprep; @@ -27,10 +26,22 @@ local tonumber = tonumber; local _G = _G; local prosody = prosody; +local error_messages = setmetatable({ + ["invalid-username"] = "The given username is invalid in a Jabber ID"; + ["invalid-hostname"] = "The given hostname is invalid"; + ["no-password"] = "No password was supplied"; + ["no-such-user"] = "The given user does not exist on the server"; + ["no-such-host"] = "The given hostname does not exist in the config"; + ["unable-to-save-data"] = "Unable to store, perhaps you don't have permission?"; + ["no-pidfile"] = "There is no 'pidfile' option in the configuration file, see https://prosody.im/doc/prosodyctl#pidfile for help"; + ["invalid-pidfile"] = "The 'pidfile' option in the configuration file is not a string, see https://prosody.im/doc/prosodyctl#pidfile for help"; + ["no-posix"] = "The mod_posix module is not enabled in the Prosody config file, see https://prosody.im/doc/prosodyctl for more info"; + ["no-such-method"] = "This module has no commands"; + ["not-running"] = "Prosody is not running"; + }, { __index = function (_,k) return "Error: "..(tostring(k):gsub("%-", " "):gsub("^.", string.upper)); end }); + -- UI helpers -local function show_message(msg, ...) - print(msg:format(...)); -end +local show_message = require "util.human.io".printf; local function show_usage(usage, desc) print("Usage: ".._G.arg[0].." "..usage); @@ -49,89 +60,6 @@ local function show_module_configuration_help(mod_name) print(" "..mod_name..": https://modules.prosody.im/"..mod_name..".html") end -local function getchar(n) - local stty_ret = os.execute("stty raw -echo 2>/dev/null"); - local ok, char; - if stty_ret == true or stty_ret == 0 then - ok, char = pcall(io.read, n or 1); - os.execute("stty sane"); - else - ok, char = pcall(io.read, "*l"); - if ok then - char = char:sub(1, n or 1); - end - end - if ok then - return char; - end -end - -local function getline() - local ok, line = pcall(io.read, "*l"); - if ok then - return line; - end -end - -local function getpass() - local stty_ret, _, status_code = os.execute("stty -echo 2>/dev/null"); - if status_code then -- COMPAT w/ Lua 5.1 - stty_ret = status_code; - end - if stty_ret ~= 0 then - io.write("\027[08m"); -- ANSI 'hidden' text attribute - end - local ok, pass = pcall(io.read, "*l"); - if stty_ret == 0 then - os.execute("stty sane"); - else - io.write("\027[00m"); - end - io.write("\n"); - if ok then - return pass; - end -end - -local function show_yesno(prompt) - io.write(prompt, " "); - local choice = getchar():lower(); - io.write("\n"); - if not choice:match("%a") then - choice = prompt:match("%[.-(%U).-%]$"); - if not choice then return nil; end - end - return (choice == "y"); -end - -local function read_password() - local password; - while true do - io.write("Enter new password: "); - password = getpass(); - if not password then - show_message("No password - cancelled"); - return; - end - io.write("Retype new password: "); - if getpass() ~= password then - if not show_yesno [=[Passwords did not match, try again? [Y/n]]=] then - return; - end - else - break; - end - end - return password; -end - -local function show_prompt(prompt) - io.write(prompt, " "); - local line = getline(); - line = line and line:gsub("\n$",""); - return (line and #line > 0) and line or nil; -end - -- Server control local function adduser(params) local user, host, password = nodeprep(params.user, true), nameprep(params.host), params.password; @@ -318,12 +246,6 @@ return { show_warning = show_message; show_usage = show_usage; show_module_configuration_help = show_module_configuration_help; - getchar = getchar; - getline = getline; - getpass = getpass; - show_yesno = show_yesno; - read_password = read_password; - show_prompt = show_prompt; adduser = adduser; user_exists = user_exists; passwd = passwd; @@ -335,4 +257,5 @@ return { reload = reload; get_path_custom_plugins = get_path_custom_plugins; call_luarocks = call_luarocks; + error_messages = error_messages; }; diff --git a/util/prosodyctl/cert.lua b/util/prosodyctl/cert.lua new file mode 100644 index 00000000..29e26ed8 --- /dev/null +++ b/util/prosodyctl/cert.lua @@ -0,0 +1,293 @@ +local lfs = require "lfs"; + +local pctl = require "util.prosodyctl"; +local configmanager = require "core.configmanager"; + +local openssl; + +local cert_commands = {}; + +-- If a file already exists, ask if the user wants to use it or replace it +-- Backups the old file if replaced +local function use_existing(filename) + local attrs = lfs.attributes(filename); + if attrs then + if pctl.show_yesno(filename .. " exists, do you want to replace it? [y/n]") then + local backup = filename..".bkp~"..os.date("%FT%T", attrs.change); + os.rename(filename, backup); + pctl.show_message("%s backed up to %s", filename, backup); + else + -- Use the existing file + return true; + end + end +end + +local have_pposix, pposix = pcall(require, "util.pposix"); +local cert_basedir = prosody.paths.data == "." and "./certs" or prosody.paths.data; +if have_pposix and pposix.getuid() == 0 then + -- FIXME should be enough to check if this directory is writable + local cert_dir = configmanager.get("*", "certificates") or "certs"; + cert_basedir = configmanager.resolve_relative_path(prosody.paths.config, cert_dir); +end + +function cert_commands.config(arg) + if #arg >= 1 and arg[1] ~= "--help" then + local conf_filename = cert_basedir .. "/" .. arg[1] .. ".cnf"; + if use_existing(conf_filename) then + return nil, conf_filename; + end + local distinguished_name; + if arg[#arg]:find("^/") then + distinguished_name = table.remove(arg); + end + local conf = openssl.config.new(); + conf:from_prosody(prosody.hosts, configmanager, arg); + if distinguished_name then + local dn = {}; + for k, v in distinguished_name:gmatch("/([^=/]+)=([^/]+)") do + table.insert(dn, k); + dn[k] = v; + end + conf.distinguished_name = dn; + else + pctl.show_message("Please provide details to include in the certificate config file."); + pctl.show_message("Leave the field empty to use the default value or '.' to exclude the field.") + for _, k in ipairs(openssl._DN_order) do + local v = conf.distinguished_name[k]; + if v then + local nv = nil; + if k == "commonName" then + v = arg[1] + elseif k == "emailAddress" then + v = "xmpp@" .. arg[1]; + elseif k == "countryName" then + local tld = arg[1]:match"%.([a-z]+)$"; + if tld and #tld == 2 and tld ~= "uk" then + v = tld:upper(); + end + end + nv = pctl.show_prompt(("%s (%s):"):format(k, nv or v)); + nv = (not nv or nv == "") and v or nv; + if nv:find"[\192-\252][\128-\191]+" then + conf.req.string_mask = "utf8only" + end + conf.distinguished_name[k] = nv ~= "." and nv or nil; + end + end + end + local conf_file, err = io.open(conf_filename, "w"); + if not conf_file then + pctl.show_warning("Could not open OpenSSL config file for writing"); + pctl.show_warning(err); + os.exit(1); + end + conf_file:write(conf:serialize()); + conf_file:close(); + print(""); + pctl.show_message("Config written to %s", conf_filename); + return nil, conf_filename; + else + pctl.show_usage("cert config HOSTNAME [HOSTNAME+]", "Builds a certificate config file covering the supplied hostname(s)") + end +end + +function cert_commands.key(arg) + if #arg >= 1 and arg[1] ~= "--help" then + local key_filename = cert_basedir .. "/" .. arg[1] .. ".key"; + if use_existing(key_filename) then + return nil, key_filename; + end + os.remove(key_filename); -- This file, if it exists is unlikely to have write permissions + local key_size = tonumber(arg[2] or pctl.show_prompt("Choose key size (2048):") or 2048); + local old_umask = pposix.umask("0377"); + if openssl.genrsa{out=key_filename, key_size} then + os.execute(("chmod 400 '%s'"):format(key_filename)); + pctl.show_message("Key written to %s", key_filename); + pposix.umask(old_umask); + return nil, key_filename; + end + pctl.show_message("There was a problem, see OpenSSL output"); + else + pctl.show_usage("cert key HOSTNAME ", "Generates a RSA key named HOSTNAME.key\n " + .."Prompts for a key size if none given") + end +end + +function cert_commands.request(arg) + if #arg >= 1 and arg[1] ~= "--help" then + local req_filename = cert_basedir .. "/" .. arg[1] .. ".req"; + if use_existing(req_filename) then + return nil, req_filename; + end + local _, key_filename = cert_commands.key({arg[1]}); + local _, conf_filename = cert_commands.config(arg); + if openssl.req{new=true, key=key_filename, utf8=true, sha256=true, config=conf_filename, out=req_filename} then + pctl.show_message("Certificate request written to %s", req_filename); + else + pctl.show_message("There was a problem, see OpenSSL output"); + end + else + pctl.show_usage("cert request HOSTNAME [HOSTNAME+]", "Generates a certificate request for the supplied hostname(s)") + end +end + +function cert_commands.generate(arg) + if #arg >= 1 and arg[1] ~= "--help" then + local cert_filename = cert_basedir .. "/" .. arg[1] .. ".crt"; + if use_existing(cert_filename) then + return nil, cert_filename; + end + local _, key_filename = cert_commands.key({arg[1]}); + local _, conf_filename = cert_commands.config(arg); + if key_filename and conf_filename and cert_filename + and openssl.req{new=true, x509=true, nodes=true, key=key_filename, + days=365, sha256=true, utf8=true, config=conf_filename, out=cert_filename} then + pctl.show_message("Certificate written to %s", cert_filename); + print(); + else + pctl.show_message("There was a problem, see OpenSSL output"); + end + else + pctl.show_usage("cert generate HOSTNAME [HOSTNAME+]", "Generates a self-signed certificate for the current hostname(s)") + end +end + +local function sh_esc(s) + return "'" .. s:gsub("'", "'\\''") .. "'"; +end + +local function copy(from, to, umask, owner, group) + local old_umask = umask and pposix.umask(umask); + local attrs = lfs.attributes(to); + if attrs then -- Move old file out of the way + local backup = to..".bkp~"..os.date("%FT%T", attrs.change); + os.rename(to, backup); + end + -- FIXME friendlier error handling, maybe move above backup back? + local input = assert(io.open(from)); + local output = assert(io.open(to, "w")); + local data = input:read(2^11); + while data and output:write(data) do + data = input:read(2^11); + end + assert(input:close()); + assert(output:close()); + if not prosody.installed then + -- FIXME this is possibly specific to GNU chown + os.execute(("chown -c --reference=%s %s"):format(sh_esc(cert_basedir), sh_esc(to))); + elseif owner and group then + local ok = os.execute(("chown %s:%s %s"):format(sh_esc(owner), sh_esc(group), sh_esc(to))); + assert(ok == true or ok == 0, "Failed to change ownership of "..to); + end + if old_umask then pposix.umask(old_umask); end + return true; +end + +function cert_commands.import(arg) + local hostnames = {}; + -- Move hostname arguments out of arg, the rest should be a list of paths + while arg[1] and prosody.hosts[ arg[1] ] do + table.insert(hostnames, table.remove(arg, 1)); + end + if hostnames[1] == nil then + local domains = os.getenv"RENEWED_DOMAINS"; -- Set if invoked via certbot + if domains then + for host in domains:gmatch("%S+") do + table.insert(hostnames, host); + end + else + for host in pairs(prosody.hosts) do + if host ~= "*" and configmanager.get(host, "enabled") ~= false then + table.insert(hostnames, host); + end + end + end + end + if not arg[1] or arg[1] == "--help" then -- Probably forgot the path + pctl.show_usage("cert import [HOSTNAME+] /path/to/certs [/other/paths/]+", + "Copies certificates to "..cert_basedir); + return 1; + end + local owner, group; + if pposix.getuid() == 0 then -- We need root to change ownership + owner = configmanager.get("*", "prosody_user") or "prosody"; + group = configmanager.get("*", "prosody_group") or owner; + end + local cm = require "core.certmanager"; + local imported = {}; + for _, host in ipairs(hostnames) do + for _, dir in ipairs(arg) do + local paths = cm.find_cert(dir, host); + if paths then + copy(paths.certificate, cert_basedir .. "/" .. host .. ".crt", nil, owner, group); + copy(paths.key, cert_basedir .. "/" .. host .. ".key", "0377", owner, group); + table.insert(imported, host); + else + -- TODO Say where we looked + pctl.show_warning("No certificate for host "..host.." found :("); + end + -- TODO Additional checks + -- Certificate names matches the hostname + -- Private key matches public key in certificate + end + end + if imported[1] then + pctl.show_message("Imported certificate and key for hosts %s", table.concat(imported, ", ")); + local ok, err = pctl.reload(); + if not ok and err ~= "not-running" then + pctl.show_message(pctl.error_messages[err]); + end + else + pctl.show_warning("No certificates imported :("); + return 1; + end +end + +local function cert(arg) + if #arg >= 1 and arg[1] ~= "--help" then + openssl = require "util.openssl"; + lfs = require "lfs"; + local cert_dir_attrs = lfs.attributes(cert_basedir); + if not cert_dir_attrs then + pctl.show_warning("The directory "..cert_basedir.." does not exist"); + return 1; -- TODO Should we create it? + end + local uid = pposix.getuid(); + if uid ~= 0 and uid ~= cert_dir_attrs.uid then + pctl.show_warning("The directory "..cert_basedir.." is not owned by the current user, won't be able to write files to it"); + return 1; + elseif not cert_dir_attrs.permissions then -- COMPAT with LuaFilesystem < 1.6.2 (hey CentOS!) + pctl.show_message("Unable to check permissions on %s (LuaFilesystem 1.6.2+ required)", cert_basedir); + pctl.show_message("Please confirm that Prosody (and only Prosody) can write to this directory)"); + elseif cert_dir_attrs.permissions:match("^%.w..%-..%-.$") then + pctl.show_warning("The directory "..cert_basedir.." not only writable by its owner"); + return 1; + end + local subcmd = table.remove(arg, 1); + if type(cert_commands[subcmd]) == "function" then + if subcmd ~= "import" then -- hostnames are optional for import + if not arg[1] then + pctl.show_message"You need to supply at least one hostname" + arg = { "--help" }; + end + if arg[1] ~= "--help" and not prosody.hosts[arg[1]] then + pctl.show_message(pctl.error_messages["no-such-host"]); + return 1; + end + end + return cert_commands[subcmd](arg); + elseif subcmd == "check" then + return require "util.prosodyctl.check".check({"certs"}); + end + end + pctl.show_usage("cert config|request|generate|key|import", "Helpers for generating X.509 certificates and keys.") + for _, cmd in pairs(cert_commands) do + print() + cmd{ "--help" } + end +end + +return { + cert = cert; +}; diff --git a/util/prosodyctl/check.lua b/util/prosodyctl/check.lua new file mode 100644 index 00000000..d22d5f45 --- /dev/null +++ b/util/prosodyctl/check.lua @@ -0,0 +1,530 @@ +local configmanager = require "core.configmanager"; +local show_usage = require "util.prosodyctl".show_usage; +local show_warning = require "util.prosodyctl".show_warning; +local dependencies = require "util.dependencies"; +local socket = require "socket"; +local jid_split = require "util.jid".prepped_split; +local modulemanager = require "core.modulemanager"; + +local function check(arg) + if arg[1] == "--help" then + show_usage([[check]], [[Perform basic checks on your Prosody installation]]); + return 1; + end + local what = table.remove(arg, 1); + local set = require "util.set"; + local it = require "util.iterators"; + local ok = true; + local function disabled_hosts(host, conf) return host ~= "*" and conf.enabled ~= false; end + local function enabled_hosts() return it.filter(disabled_hosts, pairs(configmanager.getconfig())); end + if not (what == nil or what == "disabled" or what == "config" or what == "dns" or what == "certs") then + show_warning("Don't know how to check '%s'. Try one of 'config', 'dns', 'certs' or 'disabled'.", what); + return 1; + end + if not what or what == "disabled" then + local disabled_hosts_set = set.new(); + for host, host_options in it.filter("*", pairs(configmanager.getconfig())) do + if host_options.enabled == false then + disabled_hosts_set:add(host); + end + end + if not disabled_hosts_set:empty() then + local msg = "Checks will be skipped for these disabled hosts: %s"; + if what then msg = "These hosts are disabled: %s"; end + show_warning(msg, tostring(disabled_hosts_set)); + if what then return 0; end + print"" + end + end + if not what or what == "config" then + print("Checking config..."); + local deprecated = set.new({ + "bosh_ports", "disallow_s2s", "no_daemonize", "anonymous_login", "require_encryption", + "vcard_compatibility", "cross_domain_bosh", "cross_domain_websocket", "daemonize", + }); + local known_global_options = set.new({ + "pidfile", "log", "plugin_paths", "prosody_user", "prosody_group", "daemonize", + "umask", "prosodyctl_timeout", "use_ipv6", "use_libevent", "network_settings", + "network_backend", "http_default_host", + "statistics_interval", "statistics", "statistics_config", + }); + local config = configmanager.getconfig(); + -- Check that we have any global options (caused by putting a host at the top) + if it.count(it.filter("log", pairs(config["*"]))) == 0 then + ok = false; + print(""); + print(" No global options defined. Perhaps you have put a host definition at the top") + print(" of the config file? They should be at the bottom, see https://prosody.im/doc/configure#overview"); + end + if it.count(enabled_hosts()) == 0 then + ok = false; + print(""); + if it.count(it.filter("*", pairs(config))) == 0 then + print(" No hosts are defined, please add at least one VirtualHost section") + elseif config["*"]["enabled"] == false then + print(" No hosts are enabled. Remove enabled = false from the global section or put enabled = true under at least one VirtualHost section") + else + print(" All hosts are disabled. Remove enabled = false from at least one VirtualHost section") + end + end + if not config["*"].modules_enabled then + print(" No global modules_enabled is set?"); + local suggested_global_modules; + for host, options in enabled_hosts() do --luacheck: ignore 213/host + if not options.component_module and options.modules_enabled then + suggested_global_modules = set.intersection(suggested_global_modules or set.new(options.modules_enabled), set.new(options.modules_enabled)); + end + end + if suggested_global_modules and not suggested_global_modules:empty() then + print(" Consider moving these modules into modules_enabled in the global section:") + print(" "..tostring(suggested_global_modules / function (x) return ("%q"):format(x) end)); + end + print(); + end + + do -- Check for modules enabled both normally and as components + local modules = set.new(config["*"]["modules_enabled"]); + for host, options in enabled_hosts() do + local component_module = options.component_module; + if component_module and modules:contains(component_module) then + print((" mod_%s is enabled both in modules_enabled and as Component %q %q"):format(component_module, host, component_module)); + print(" This means the service is enabled on all VirtualHosts as well as the Component."); + print(" Are you sure this what you want? It may cause unexpected behaviour."); + end + end + end + + -- Check for global options under hosts + local global_options = set.new(it.to_array(it.keys(config["*"]))); + local deprecated_global_options = set.intersection(global_options, deprecated); + if not deprecated_global_options:empty() then + print(""); + print(" You have some deprecated options in the global section:"); + print(" "..tostring(deprecated_global_options)) + ok = false; + end + for host, options in it.filter(function (h) return h ~= "*" end, pairs(configmanager.getconfig())) do + local host_options = set.new(it.to_array(it.keys(options))); + local misplaced_options = set.intersection(host_options, known_global_options); + for name in pairs(options) do + if name:match("^interfaces?") + or name:match("_ports?$") or name:match("_interfaces?$") + or (name:match("_ssl$") and not name:match("^[cs]2s_ssl$")) then + misplaced_options:add(name); + end + end + if not misplaced_options:empty() then + ok = false; + print(""); + local n = it.count(misplaced_options); + print(" You have "..n.." option"..(n>1 and "s " or " ").."set under "..host.." that should be"); + print(" in the global section of the config file, above any VirtualHost or Component definitions,") + print(" see https://prosody.im/doc/configure#overview for more information.") + print(""); + print(" You need to move the following option"..(n>1 and "s" or "")..": "..table.concat(it.to_array(misplaced_options), ", ")); + end + end + for host, options in enabled_hosts() do + local host_options = set.new(it.to_array(it.keys(options))); + local subdomain = host:match("^[^.]+"); + if not(host_options:contains("component_module")) and (subdomain == "jabber" or subdomain == "xmpp" + or subdomain == "chat" or subdomain == "im") then + print(""); + print(" Suggestion: If "..host.. " is a new host with no real users yet, consider renaming it now to"); + print(" "..host:gsub("^[^.]+%.", "")..". You can use SRV records to redirect XMPP clients and servers to "..host.."."); + print(" For more information see: https://prosody.im/doc/dns"); + end + end + local all_modules = set.new(config["*"].modules_enabled); + local all_options = set.new(it.to_array(it.keys(config["*"]))); + for host in enabled_hosts() do + all_options:include(set.new(it.to_array(it.keys(config[host])))); + all_modules:include(set.new(config[host].modules_enabled)); + end + for mod in all_modules do + if mod:match("^mod_") then + print(""); + print(" Modules in modules_enabled should not have the 'mod_' prefix included."); + print(" Change '"..mod.."' to '"..mod:match("^mod_(.*)").."'."); + elseif mod:match("^auth_") then + print(""); + print(" Authentication modules should not be added to modules_enabled,"); + print(" but be specified in the 'authentication' option."); + print(" Remove '"..mod.."' from modules_enabled and instead add"); + print(" authentication = '"..mod:match("^auth_(.*)").."'"); + print(" For more information see https://prosody.im/doc/authentication"); + elseif mod:match("^storage_") then + print(""); + print(" storage modules should not be added to modules_enabled,"); + print(" but be specified in the 'storage' option."); + print(" Remove '"..mod.."' from modules_enabled and instead add"); + print(" storage = '"..mod:match("^storage_(.*)").."'"); + print(" For more information see https://prosody.im/doc/storage"); + end + end + if all_modules:contains("vcard") and all_modules:contains("vcard_legacy") then + print(""); + print(" Both mod_vcard_legacy and mod_vcard are enabled but they conflict"); + print(" with each other. Remove one."); + end + if all_modules:contains("pep") and all_modules:contains("pep_simple") then + print(""); + print(" Both mod_pep_simple and mod_pep are enabled but they conflict"); + print(" with each other. Remove one."); + end + for host, host_config in pairs(config) do --luacheck: ignore 213/host + if type(rawget(host_config, "storage")) == "string" and rawget(host_config, "default_storage") then + print(""); + print(" The 'default_storage' option is not needed if 'storage' is set to a string."); + break; + end + end + local require_encryption = set.intersection(all_options, set.new({ + "require_encryption", "c2s_require_encryption", "s2s_require_encryption" + })):empty(); + local ssl = dependencies.softreq"ssl"; + if not ssl then + if not require_encryption then + print(""); + print(" You require encryption but LuaSec is not available."); + print(" Connections will fail."); + ok = false; + end + elseif not ssl.loadcertificate then + if all_options:contains("s2s_secure_auth") then + print(""); + print(" You have set s2s_secure_auth but your version of LuaSec does "); + print(" not support certificate validation, so all s2s connections will"); + print(" fail."); + ok = false; + elseif all_options:contains("s2s_secure_domains") then + local secure_domains = set.new(); + for host in enabled_hosts() do + if config[host].s2s_secure_auth == true then + secure_domains:add("*"); + else + secure_domains:include(set.new(config[host].s2s_secure_domains)); + end + end + if not secure_domains:empty() then + print(""); + print(" You have set s2s_secure_domains but your version of LuaSec does "); + print(" not support certificate validation, so s2s connections to/from "); + print(" these domains will fail."); + ok = false; + end + end + elseif require_encryption and not all_modules:contains("tls") then + print(""); + print(" You require encryption but mod_tls is not enabled."); + print(" Connections will fail."); + ok = false; + end + + print("Done.\n"); + end + if not what or what == "dns" then + local dns = require "net.dns"; + local idna = require "util.encodings".idna; + local ip = require "util.ip"; + local c2s_ports = set.new(configmanager.get("*", "c2s_ports") or {5222}); + local s2s_ports = set.new(configmanager.get("*", "s2s_ports") or {5269}); + + local c2s_srv_required, s2s_srv_required; + if not c2s_ports:contains(5222) then + c2s_srv_required = true; + end + if not s2s_ports:contains(5269) then + s2s_srv_required = true; + end + + local problem_hosts = set.new(); + + local external_addresses, internal_addresses = set.new(), set.new(); + + local fqdn = socket.dns.tohostname(socket.dns.gethostname()); + if fqdn then + do + local res = dns.lookup(idna.to_ascii(fqdn), "A"); + if res then + for _, record in ipairs(res) do + external_addresses:add(record.a); + end + end + end + do + local res = dns.lookup(idna.to_ascii(fqdn), "AAAA"); + if res then + for _, record in ipairs(res) do + external_addresses:add(record.aaaa); + end + end + end + end + + local local_addresses = require"util.net".local_addresses() or {}; + + for addr in it.values(local_addresses) do + if not ip.new_ip(addr).private then + external_addresses:add(addr); + else + internal_addresses:add(addr); + end + end + + if external_addresses:empty() then + print(""); + print(" Failed to determine the external addresses of this server. Checks may be inaccurate."); + c2s_srv_required, s2s_srv_required = true, true; + end + + local v6_supported = not not socket.tcp6; + + for jid, host_options in enabled_hosts() do + local all_targets_ok, some_targets_ok = true, false; + local node, host = jid_split(jid); + + local modules, component_module = modulemanager.get_modules_for_host(host); + if component_module then + modules:add(component_module); + end + + local is_component = not not host_options.component_module; + print("Checking DNS for "..(is_component and "component" or "host").." "..jid.."..."); + if node then + print("Only the domain part ("..host..") is used in DNS.") + end + local target_hosts = set.new(); + if modules:contains("c2s") then + local res = dns.lookup("_xmpp-client._tcp."..idna.to_ascii(host)..".", "SRV"); + if res then + for _, record in ipairs(res) do + target_hosts:add(record.srv.target); + if not c2s_ports:contains(record.srv.port) then + print(" SRV target "..record.srv.target.." contains unknown client port: "..record.srv.port); + end + end + else + if c2s_srv_required then + print(" No _xmpp-client SRV record found for "..host..", but it looks like you need one."); + all_targets_ok = false; + else + target_hosts:add(host); + end + end + end + if modules:contains("s2s") then + local res = dns.lookup("_xmpp-server._tcp."..idna.to_ascii(host)..".", "SRV"); + if res then + for _, record in ipairs(res) do + target_hosts:add(record.srv.target); + if not s2s_ports:contains(record.srv.port) then + print(" SRV target "..record.srv.target.." contains unknown server port: "..record.srv.port); + end + end + else + if s2s_srv_required then + print(" No _xmpp-server SRV record found for "..host..", but it looks like you need one."); + all_targets_ok = false; + else + target_hosts:add(host); + end + end + end + if target_hosts:empty() then + target_hosts:add(host); + end + + if target_hosts:contains("localhost") then + print(" Target 'localhost' cannot be accessed from other servers"); + target_hosts:remove("localhost"); + end + + if modules:contains("proxy65") then + local proxy65_target = configmanager.get(host, "proxy65_address") or host; + if type(proxy65_target) == "string" then + local A, AAAA = dns.lookup(idna.to_ascii(proxy65_target), "A"), dns.lookup(idna.to_ascii(proxy65_target), "AAAA"); + local prob = {}; + if not A then + table.insert(prob, "A"); + end + if v6_supported and not AAAA then + table.insert(prob, "AAAA"); + end + if #prob > 0 then + print(" File transfer proxy "..proxy65_target.." has no "..table.concat(prob, "/") + .." record. Create one or set 'proxy65_address' to the correct host/IP."); + end + else + print(" proxy65_address for "..host.." should be set to a string, unable to perform DNS check"); + end + end + + for target_host in target_hosts do + local host_ok_v4, host_ok_v6; + do + local res = dns.lookup(idna.to_ascii(target_host), "A"); + if res then + for _, record in ipairs(res) do + if external_addresses:contains(record.a) then + some_targets_ok = true; + host_ok_v4 = true; + elseif internal_addresses:contains(record.a) then + host_ok_v4 = true; + some_targets_ok = true; + print(" "..target_host.." A record points to internal address, external connections might fail"); + else + print(" "..target_host.." A record points to unknown address "..record.a); + all_targets_ok = false; + end + end + end + end + do + local res = dns.lookup(idna.to_ascii(target_host), "AAAA"); + if res then + for _, record in ipairs(res) do + if external_addresses:contains(record.aaaa) then + some_targets_ok = true; + host_ok_v6 = true; + elseif internal_addresses:contains(record.aaaa) then + host_ok_v6 = true; + some_targets_ok = true; + print(" "..target_host.." AAAA record points to internal address, external connections might fail"); + else + print(" "..target_host.." AAAA record points to unknown address "..record.aaaa); + all_targets_ok = false; + end + end + end + end + + local bad_protos = {} + if not host_ok_v4 then + table.insert(bad_protos, "IPv4"); + end + if not host_ok_v6 then + table.insert(bad_protos, "IPv6"); + end + if #bad_protos > 0 then + print(" Host "..target_host.." does not seem to resolve to this server ("..table.concat(bad_protos, "/")..")"); + end + if host_ok_v6 and not v6_supported then + print(" Host "..target_host.." has AAAA records, but your version of LuaSocket does not support IPv6."); + print(" Please see https://prosody.im/doc/ipv6 for more information."); + end + end + if not all_targets_ok then + print(" "..(some_targets_ok and "Only some" or "No").." targets for "..host.." appear to resolve to this server."); + if is_component then + print(" DNS records are necessary if you want users on other servers to access this component."); + end + problem_hosts:add(host); + end + print(""); + end + if not problem_hosts:empty() then + print(""); + print("For more information about DNS configuration please see https://prosody.im/doc/dns"); + print(""); + ok = false; + end + end + if not what or what == "certs" then + local cert_ok; + print"Checking certificates..." + local x509_verify_identity = require"util.x509".verify_identity; + local create_context = require "core.certmanager".create_context; + local ssl = dependencies.softreq"ssl"; + -- local datetime_parse = require"util.datetime".parse_x509; + local load_cert = ssl and ssl.loadcertificate; + -- or ssl.cert_from_pem + if not ssl then + print("LuaSec not available, can't perform certificate checks") + if what == "certs" then cert_ok = false end + elseif not load_cert then + print("This version of LuaSec (" .. ssl._VERSION .. ") does not support certificate checking"); + cert_ok = false + else + local function skip_bare_jid_hosts(host) + if jid_split(host) then + -- See issue #779 + return false; + end + return true; + end + for host in it.filter(skip_bare_jid_hosts, enabled_hosts()) do + print("Checking certificate for "..host); + -- First, let's find out what certificate this host uses. + local host_ssl_config = configmanager.rawget(host, "ssl") + or configmanager.rawget(host:match("%.(.*)"), "ssl"); + local global_ssl_config = configmanager.rawget("*", "ssl"); + local ok, err, ssl_config = create_context(host, "server", host_ssl_config, global_ssl_config); + if not ok then + print(" Error: "..err); + cert_ok = false + elseif not ssl_config.certificate then + print(" No 'certificate' found for "..host) + cert_ok = false + elseif not ssl_config.key then + print(" No 'key' found for "..host) + cert_ok = false + else + local key, err = io.open(ssl_config.key); -- Permissions check only + if not key then + print(" Could not open "..ssl_config.key..": "..err); + cert_ok = false + else + key:close(); + end + local cert_fh, err = io.open(ssl_config.certificate); -- Load the file. + if not cert_fh then + print(" Could not open "..ssl_config.certificate..": "..err); + cert_ok = false + else + print(" Certificate: "..ssl_config.certificate) + local cert = load_cert(cert_fh:read"*a"); cert_fh:close(); + if not cert:validat(os.time()) then + print(" Certificate has expired.") + cert_ok = false + elseif not cert:validat(os.time() + 86400) then + print(" Certificate expires within one day.") + cert_ok = false + elseif not cert:validat(os.time() + 86400*7) then + print(" Certificate expires within one week.") + elseif not cert:validat(os.time() + 86400*31) then + print(" Certificate expires within one month.") + end + if configmanager.get(host, "component_module") == nil + and not x509_verify_identity(host, "_xmpp-client", cert) then + print(" Not valid for client connections to "..host..".") + cert_ok = false + end + if (not (configmanager.get(host, "anonymous_login") + or configmanager.get(host, "authentication") == "anonymous")) + and not x509_verify_identity(host, "_xmpp-server", cert) then + print(" Not valid for server-to-server connections to "..host..".") + cert_ok = false + end + end + end + end + end + if cert_ok == false then + print("") + print("For more information about certificates please see https://prosody.im/doc/certificates"); + ok = false + end + print("") + end + if not ok then + print("Problems found, see above."); + else + print("All checks passed, congratulations!"); + end + return ok and 0 or 2; +end + +return { + check = check; +}; diff --git a/util/prosodyctl/shell.lua b/util/prosodyctl/shell.lua index cbcea927..1d07e6ec 100644 --- a/util/prosodyctl/shell.lua +++ b/util/prosodyctl/shell.lua @@ -126,5 +126,5 @@ local function start(arg) --luacheck: ignore 212/arg end return { - start = start; + shell = start; }; -- cgit v1.2.3 From cf230c2b431ba23d19a08b69cff08948531dfc29 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 2 Jun 2020 08:02:03 +0100 Subject: util.human.io: Fix variable name [luacheck] --- util/human/io.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/human/io.lua b/util/human/io.lua index bfd1c00d..4c84c4a4 100644 --- a/util/human/io.lua +++ b/util/human/io.lua @@ -82,7 +82,7 @@ local function show_prompt(prompt) end local function printf(fmt, ...) - print(msg:format(...)); + print(fmt:format(...)); end return { -- cgit v1.2.3 From c3764ecf3ee523b4fb88805b17e84fc1cfed22a8 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 2 Jun 2020 09:07:29 +0200 Subject: util.prosodyctl.shell: Correct check for --socket --- util/prosodyctl/shell.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/prosodyctl/shell.lua b/util/prosodyctl/shell.lua index 1d07e6ec..1d312d3f 100644 --- a/util/prosodyctl/shell.lua +++ b/util/prosodyctl/shell.lua @@ -114,7 +114,7 @@ local function start(arg) --luacheck: ignore 212/arg end end); - local socket_path = path.resolve_relative_path(prosody.paths.data, arg.socket or config.get("*", "admin_socket") or "prosody.sock"); + local socket_path = path.resolve_relative_path(prosody.paths.data, prosody.opts.socket or config.get("*", "admin_socket") or "prosody.sock"); local conn = connection(socket_path, client.listeners); local ok, err = conn:connect(); if not ok then -- cgit v1.2.3 From 23cef5c6c53809c5be09581f1bfdab10f5af6bb8 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 2 Jun 2020 09:19:07 +0200 Subject: util.prosodyctl.shell: Really fix --socket option Forgot it stops parsing --foo options at the first argument, so subsequent commands need to parse their own options like this. --- util/prosodyctl/shell.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/prosodyctl/shell.lua b/util/prosodyctl/shell.lua index 1d312d3f..8c8769e2 100644 --- a/util/prosodyctl/shell.lua +++ b/util/prosodyctl/shell.lua @@ -10,6 +10,7 @@ local config = require "core.configmanager"; local server = require "net.server"; local st = require "util.stanza"; local path = require "util.paths"; +local parse_args = require "util.argparse".parse; local have_readline, readline = pcall(require, "readline"); @@ -91,6 +92,7 @@ end local function start(arg) --luacheck: ignore 212/arg local client = adminstream.client(); + local opts = parse_args(arg); client.events.add_handler("connected", function () if not arg.quiet then @@ -114,7 +116,7 @@ local function start(arg) --luacheck: ignore 212/arg end end); - local socket_path = path.resolve_relative_path(prosody.paths.data, prosody.opts.socket or config.get("*", "admin_socket") or "prosody.sock"); + local socket_path = path.resolve_relative_path(prosody.paths.data, opts.socket or config.get("*", "admin_socket") or "prosody.sock"); local conn = connection(socket_path, client.listeners); local ok, err = conn:connect(); if not ok then -- cgit v1.2.3 From 5c3b43f01416ba31fa3f81b87c82e046e165a8e8 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 2 Jun 2020 08:28:39 +0100 Subject: util.prosodyctl.shell, util.adminstream: Move connection logic into adminstream for easier reuse --- util/adminstream.lua | 39 ++++++++++++++++++++++++++++++++++++ util/prosodyctl/shell.lua | 50 ++++++++--------------------------------------- 2 files changed, 47 insertions(+), 42 deletions(-) (limited to 'util') diff --git a/util/adminstream.lua b/util/adminstream.lua index 186cb0e9..b1bc0e64 100644 --- a/util/adminstream.lua +++ b/util/adminstream.lua @@ -136,6 +136,44 @@ end --- Public methods +local function new_connection(socket_path, listeners) + local have_unix, unix = pcall(require, "socket.unix"); + if type(unix) ~= "table" then + have_unix = false; + end + local conn, sock; + + return { + connect = function () + if not have_unix then + return nil, "no unix socket support"; + end + if sock or conn then + return nil, "already connected"; + end + sock = unix.stream(); + sock:settimeout(0); + local ok, err = sock:connect(socket_path); + if not ok then + return nil, err; + end + conn = server.wrapclient(sock, nil, nil, listeners, "*a"); + return true; + end; + disconnect = function () + if conn then + conn:close(); + conn = nil; + end + if sock then + sock:close(); + sock = nil; + end + return true; + end; + }; +end + local function new_server(sessions, stanza_handler) local listeners = {}; @@ -280,6 +318,7 @@ local function new_client() end return { + connection = new_connection; server = new_server; client = new_client; }; diff --git a/util/prosodyctl/shell.lua b/util/prosodyctl/shell.lua index 8c8769e2..bbf0c83a 100644 --- a/util/prosodyctl/shell.lua +++ b/util/prosodyctl/shell.lua @@ -1,11 +1,3 @@ -local have_unix, unix = pcall(require, "socket.unix"); - -if not have_unix or type(unix) ~= "table" then - print("** LuaSocket unix socket support not available or incompatible, ensure your"); - print("** version is up to date."); - os.exit(1); -end - local config = require "core.configmanager"; local server = require "net.server"; local st = require "util.stanza"; @@ -44,37 +36,6 @@ local function repl(client) send_line(client, line); end -local function connection(socket_path, listeners) - local conn, sock; - - return { - connect = function () - if sock or conn then - return nil, "already connected"; - end - sock = unix.stream(); - sock:settimeout(0); - local ok, err = sock:connect(socket_path); - if not ok then - return nil, err; - end - conn = server.wrapclient(sock, nil, nil, listeners, "*a"); - return true; - end; - disconnect = function () - if conn then - conn:close(); - conn = nil; - end - if sock then - sock:close(); - sock = nil; - end - return true; - end; - }; -end - local function printbanner() print([[ ____ \ / _ @@ -117,11 +78,16 @@ local function start(arg) --luacheck: ignore 212/arg end); local socket_path = path.resolve_relative_path(prosody.paths.data, opts.socket or config.get("*", "admin_socket") or "prosody.sock"); - local conn = connection(socket_path, client.listeners); + local conn = adminstream.connection(socket_path, client.listeners); local ok, err = conn:connect(); if not ok then - print("** Unable to connect to server - is it running? Is mod_admin_shell enabled?"); - print("** Connection error: "..err); + if err == "no unix socket support" then + print("** LuaSocket unix socket support not available or incompatible, ensure your"); + print("** version is up to date."); + else + print("** Unable to connect to server - is it running? Is mod_admin_shell enabled?"); + print("** Connection error: "..err); + end os.exit(1); end server.loop(); -- cgit v1.2.3 From 50b12ee71606356d944691772859bf629fa1f9fb Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 2 Jun 2020 08:41:05 +0100 Subject: util.adminstream: Import net.server [luacheck] --- util/adminstream.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'util') diff --git a/util/adminstream.lua b/util/adminstream.lua index b1bc0e64..4583b322 100644 --- a/util/adminstream.lua +++ b/util/adminstream.lua @@ -5,6 +5,7 @@ local gettime = require "util.time".now; local runner = require "util.async".runner; local add_task = require "util.timer".add_task; local events = require "util.events"; +local server = require "net.server"; local stream_close_timeout = 5; -- cgit v1.2.3 From c382e09470c10a66dc58a40e7367f28c5073f71f Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 2 Jun 2020 09:35:07 +0200 Subject: util.prosodyctl.shell: Save readline history --- util/prosodyctl/shell.lua | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'util') diff --git a/util/prosodyctl/shell.lua b/util/prosodyctl/shell.lua index bbf0c83a..3e98540f 100644 --- a/util/prosodyctl/shell.lua +++ b/util/prosodyctl/shell.lua @@ -10,6 +10,10 @@ local adminstream = require "util.adminstream"; if have_readline then readline.set_readline_name("prosody"); + readline.set_options({ + histfile = path.join(prosody.paths.data, ".shell_history"); + ignoredups = true; + }); end local function read_line() @@ -31,6 +35,9 @@ local function repl(client) if not line then print(""); end + if have_readline then + readline.save_history(); + end os.exit(); end send_line(client, line); -- cgit v1.2.3 From 7a662dd66d0210a3a4290f6c0d2be3c8130c3495 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 4 Jan 2019 06:56:45 +0100 Subject: util.statistics: Unify API of methods to include a config table The primary goal here is to allow specifying an unit that each statistic is measured in. --- util/statistics.lua | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) (limited to 'util') diff --git a/util/statistics.lua b/util/statistics.lua index 0ec88e21..db608217 100644 --- a/util/statistics.lua +++ b/util/statistics.lua @@ -44,19 +44,23 @@ local function new_registry(config) local registry = {}; local methods; methods = { - amount = function (name, initial) - local v = initial or 0; - registry[name..":amount"] = function () return "amount", v; end + amount = function (name, conf) + local v = conf and conf.initial or 0; + registry[name..":amount"] = function () + return "amount", v, conf; + end return function (new_v) v = new_v; end end; - counter = function (name, initial) - local v = initial or 0; - registry[name..":amount"] = function () return "amount", v; end + counter = function (name, conf) + local v = conf and conf.initial or 0; + registry[name..":amount"] = function () + return "amount", v, conf; + end return function (delta) v = v + delta; end; end; - rate = function (name) + rate = function (name, conf) local since, n, total = time(), 0, 0; registry[name..":rate"] = function () total = total + n; @@ -65,6 +69,8 @@ local function new_registry(config) rate = n/(t-since); count = n; total = total; + units = conf and conf.units; + type = conf and conf.type; }; since, n = t, 0; return "rate", stats.rate, stats; @@ -73,15 +79,16 @@ local function new_registry(config) n = n + 1; end; end; - distribution = function (name, unit, type) - type = type or "distribution"; + distribution = function (name, conf) + local units = conf and conf.units; + local type = conf and conf.type or "distribution"; local events, last_event = {}, 0; local n_actual_events = 0; local since = time(); registry[name..":"..type] = function () local new_time = time(); - local stats = get_distribution_stats(events, n_actual_events, since, new_time, unit); + local stats = get_distribution_stats(events, n_actual_events, since, new_time, units); events, last_event = {}, 0; n_actual_events = 0; since = new_time; @@ -96,17 +103,19 @@ local function new_registry(config) end end; end; - sizes = function (name) - return methods.distribution(name, "bytes", "size"); + sizes = function (name, conf) + conf = conf or { units = "bytes", type = "size" } + return methods.distribution(name, conf); end; - times = function (name) + times = function (name, conf) + local units = conf and conf.units or "seconds"; local events, last_event = {}, 0; local n_actual_events = 0; local since = time(); registry[name..":duration"] = function () local new_time = time(); - local stats = get_distribution_stats(events, n_actual_events, since, new_time, "seconds"); + local stats = get_distribution_stats(events, n_actual_events, since, new_time, units); events, last_event = {}, 0; n_actual_events = 0; since = new_time; -- cgit v1.2.3 From 30b218c7fe878f81e43fbae1a035ecde14f935b9 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 4 Jan 2019 08:46:26 +0100 Subject: util.human.units: A library for formatting numbers with SI units --- util/human/units.lua | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 util/human/units.lua (limited to 'util') diff --git a/util/human/units.lua b/util/human/units.lua new file mode 100644 index 00000000..2c4662cd --- /dev/null +++ b/util/human/units.lua @@ -0,0 +1,58 @@ +local large = { + "k", 1000, + "M", 1000000, + "G", 1000000000, + "T", 1000000000000, + "P", 1000000000000000, + "E", 1000000000000000000, + "Z", 1000000000000000000000, + "Y", 1000000000000000000000000, +} +local small = { + "m", 0.001, + "μ", 0.000001, + "n", 0.000000001, + "p", 0.000000000001, + "f", 0.000000000000001, + "a", 0.000000000000000001, + "z", 0.000000000000000000001, + "y", 0.000000000000000000000001, +} + +local binary = { + "Ki", 2^10, + "Mi", 2^20, + "Gi", 2^30, + "Ti", 2^40, + "Pi", 2^50, + "Ei", 2^60, + "Zi", 2^70, + "Yi", 2^80, +} + +-- n: number, the number to format +-- unit: string, the base unit +-- b: optional enum 'b', thousands base +local function format(n, unit, b) --> string + local round = math.floor; + local prefixes = large; + local logbase = 1000; + local fmt = "%.3g %s%s"; + if n == 0 then + return fmt:format(n, "", unit); + end + if b == 'b' then + prefixes = binary; + logbase = 1024; + elseif n < 1 then + prefixes = small; + round = math.ceil; + end + local m = math.max(0, math.min(8, round(math.abs(math.log(math.abs(n), logbase))))); + local prefix, multiplier = table.unpack(prefixes, m * 2-1, m*2); + return fmt:format(n / (multiplier or 1), prefix or "", unit); +end + +return { + format = format; +}; -- cgit v1.2.3 From 3a3d0bad1c2dfe2437488c1dfa12ca98f9a1b5b5 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 3 Jun 2020 19:46:17 +0200 Subject: util.human.units: Handle location of unpack() in Lua 5.1 --- util/human/units.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/human/units.lua b/util/human/units.lua index 2c4662cd..91d6f0d5 100644 --- a/util/human/units.lua +++ b/util/human/units.lua @@ -1,3 +1,5 @@ +local unpack = table.unpack or unpack; --luacheck: ignore 113 + local large = { "k", 1000, "M", 1000000, @@ -49,7 +51,7 @@ local function format(n, unit, b) --> string round = math.ceil; end local m = math.max(0, math.min(8, round(math.abs(math.log(math.abs(n), logbase))))); - local prefix, multiplier = table.unpack(prefixes, m * 2-1, m*2); + local prefix, multiplier = unpack(prefixes, m * 2-1, m*2); return fmt:format(n / (multiplier or 1), prefix or "", unit); end -- cgit v1.2.3 From bed3607b09feff29c48bc543a5ff5d93deffda5f Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 3 Jun 2020 20:16:00 +0200 Subject: util.human.units: Put math functions into locals Primarily because the next commit will deal with math.log behaving differently on Lua 5.1 and that's eaiser with locals. --- util/human/units.lua | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'util') diff --git a/util/human/units.lua b/util/human/units.lua index 91d6f0d5..471c82a0 100644 --- a/util/human/units.lua +++ b/util/human/units.lua @@ -1,3 +1,9 @@ +local math_abs = math.abs; +local math_ceil = math.ceil; +local math_floor = math.floor; +local math_log = math.log; +local math_max = math.max; +local math_min = math.min; local unpack = table.unpack or unpack; --luacheck: ignore 113 local large = { @@ -36,7 +42,7 @@ local binary = { -- unit: string, the base unit -- b: optional enum 'b', thousands base local function format(n, unit, b) --> string - local round = math.floor; + local round = math_floor; local prefixes = large; local logbase = 1000; local fmt = "%.3g %s%s"; @@ -48,9 +54,9 @@ local function format(n, unit, b) --> string logbase = 1024; elseif n < 1 then prefixes = small; - round = math.ceil; + round = math_ceil; end - local m = math.max(0, math.min(8, round(math.abs(math.log(math.abs(n), logbase))))); + local m = math_max(0, math_min(8, round(math_abs(math_log(math_abs(n), logbase))))); local prefix, multiplier = unpack(prefixes, m * 2-1, m*2); return fmt:format(n / (multiplier or 1), prefix or "", unit); end -- cgit v1.2.3 From 715aaf3e80a330a4c74f495f5659faa4b7c5c540 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 3 Jun 2020 20:17:33 +0200 Subject: util.human.units: Handle lack of math.log(n, base) on Lua 5.1 --- util/human/units.lua | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'util') diff --git a/util/human/units.lua b/util/human/units.lua index 471c82a0..5a083783 100644 --- a/util/human/units.lua +++ b/util/human/units.lua @@ -6,6 +6,14 @@ local math_max = math.max; local math_min = math.min; local unpack = table.unpack or unpack; --luacheck: ignore 113 +if math_log(10, 10) ~= 1 then + -- Lua 5.1 COMPAT + local log10 = math.log10; + function math_log(n, base) + return log10(n) / log10(base); + end +end + local large = { "k", 1000, "M", 1000000, -- cgit v1.2.3 From 59b96c51b0037eeaf02da285cb66b6910e5efe49 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 3 Jun 2020 22:21:17 +0100 Subject: util.human.io: Add padleft, padright and a table printing function --- util/human/io.lua | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) (limited to 'util') diff --git a/util/human/io.lua b/util/human/io.lua index 4c84c4a4..338509b1 100644 --- a/util/human/io.lua +++ b/util/human/io.lua @@ -85,6 +85,56 @@ local function printf(fmt, ...) print(fmt:format(...)); end +local function padright(s, width) + return s..string.rep(" ", width-#s); +end + +local function padleft(s, width) + return string.rep(" ", width-#s)..s; +end + +local function table(col_specs, max_width, padding) + max_width = max_width or 80; + padding = padding or 4; + + local widths = {}; + local total_width = max_width - padding; + local free_width = total_width; + -- Calculate width of fixed-size columns + for i = 1, #col_specs do + local width = col_specs[i].width or "0"; + if not(type(width) == "string" and width:sub(-1) == "%") then + local title = col_specs[i].title; + width = math.max(tonumber(width), title and (#title+1) or 0); + widths[i] = width; + free_width = free_width - width; + end + end + -- Calculate width of %-based columns + for i = 1, #col_specs do + if not widths[i] then + local pc_width = tonumber((col_specs[i].width:gsub("%%$", ""))); + widths[i] = math.floor(free_width*(pc_width/100)); + end + end + + return function (row, f) + for i, column in ipairs(col_specs) do + local width = widths[i]; + local v = tostring(row[column.key or i] or ""):sub(1, width); + if #v < width then + if column.align == "right" then + v = padleft(v, width-1).." "; + else + v = padright(v, width); + end + end + (f or io.stdout):write(v); + end + (f or io.stdout):write("\n"); + end; +end + return { getchar = getchar; getline = getline; @@ -93,4 +143,7 @@ return { read_password = read_password; show_prompt = show_prompt; printf = printf; + padleft = padleft; + padright = padright; + table = table; }; -- cgit v1.2.3 From 43c83b93420116f1ed88981268bbe8e7919dd27e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 3 Jun 2020 22:26:48 +0100 Subject: util.adminstream: Fire event based on stanza name too for convenience --- util/adminstream.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/adminstream.lua b/util/adminstream.lua index 4583b322..70fa2b19 100644 --- a/util/adminstream.lua +++ b/util/adminstream.lua @@ -271,7 +271,9 @@ local function new_client() client.thread = runner(function (stanza) if st.is_stanza(stanza) then - client.events.fire_event("received", stanza); + if not client.events.fire_event("received", stanza) and not stanza.attr.xmlns then + client.events.fire_event("received/"..stanza.name, stanza); + end elseif stanza.stream == "opened" then stream_callbacks._streamopened(client, stanza.attr); client.events.fire_event("connected"); -- cgit v1.2.3 From 7c813b45d875f59e43a357736ac6b3716b112ed8 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 3 Jun 2020 22:45:33 +0100 Subject: util.human.io: table: switch row function to simply returning prepared row string --- util/human/io.lua | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'util') diff --git a/util/human/io.lua b/util/human/io.lua index 338509b1..dfed3b09 100644 --- a/util/human/io.lua +++ b/util/human/io.lua @@ -93,7 +93,7 @@ local function padleft(s, width) return string.rep(" ", width-#s)..s; end -local function table(col_specs, max_width, padding) +local function new_table(col_specs, max_width, padding) max_width = max_width or 80; padding = padding or 4; @@ -118,7 +118,8 @@ local function table(col_specs, max_width, padding) end end - return function (row, f) + return function (row) + local output = {}; for i, column in ipairs(col_specs) do local width = widths[i]; local v = tostring(row[column.key or i] or ""):sub(1, width); @@ -129,9 +130,9 @@ local function table(col_specs, max_width, padding) v = padright(v, width); end end - (f or io.stdout):write(v); + table.insert(output, v); end - (f or io.stdout):write("\n"); + return table.concat(output); end; end @@ -145,5 +146,5 @@ return { printf = printf; padleft = padleft; padright = padright; - table = table; + table = new_table; }; -- cgit v1.2.3 From f9afa34c93a18fad6fd667e176cd5d1fe6c359c0 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 3 Jun 2020 22:58:29 +0100 Subject: util.human.io: table: Return title row when no row data passed --- util/human/io.lua | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'util') diff --git a/util/human/io.lua b/util/human/io.lua index dfed3b09..8c328c14 100644 --- a/util/human/io.lua +++ b/util/human/io.lua @@ -1,3 +1,5 @@ +local array = require "util.array"; + local function getchar(n) local stty_ret = os.execute("stty raw -echo 2>/dev/null"); local ok, char; @@ -119,6 +121,9 @@ local function new_table(col_specs, max_width, padding) end return function (row) + if not row then + row = array.pluck(col_specs, "title"); + end local output = {}; for i, column in ipairs(col_specs) do local width = widths[i]; -- cgit v1.2.3 From 6950744de9817d0d29897d72605656807853588d Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 4 Jun 2020 10:39:12 +0100 Subject: util.array: pluck: Support default value to avoid holes --- util/array.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/array.lua b/util/array.lua index 32d2d6a5..6e5c8383 100644 --- a/util/array.lua +++ b/util/array.lua @@ -134,9 +134,13 @@ function array_base.unique(outa, ina) end); end -function array_base.pluck(outa, ina, key) +function array_base.pluck(outa, ina, key, default) for i = 1, #ina do - outa[i] = ina[i][key]; + local v = ina[i][key]; + if v == nil then + v = default; + end + outa[i] = v; end return outa; end -- cgit v1.2.3 From 506a747629dcbeb61be9735a86994d70c1379e40 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 4 Jun 2020 10:39:55 +0100 Subject: util.human.io: table: Fix title printing when columns use named keys --- util/human/io.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/human/io.lua b/util/human/io.lua index 8c328c14..9e700e89 100644 --- a/util/human/io.lua +++ b/util/human/io.lua @@ -121,13 +121,14 @@ local function new_table(col_specs, max_width, padding) end return function (row) + local titles; if not row then - row = array.pluck(col_specs, "title"); + titles, row = true, array.pluck(col_specs, "title", ""); end local output = {}; for i, column in ipairs(col_specs) do local width = widths[i]; - local v = tostring(row[column.key or i] or ""):sub(1, width); + local v = tostring(row[not titles and column.key or i] or ""):sub(1, width); if #v < width then if column.align == "right" then v = padleft(v, width-1).." "; -- cgit v1.2.3 From 88833e9c01a27fa19f01ed73009e7c82575f3b11 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 4 Jun 2020 16:17:14 +0200 Subject: util.adminstream: Set a read timeout handler So that net.server doesn't close the connection on inactivity. --- util/adminstream.lua | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'util') diff --git a/util/adminstream.lua b/util/adminstream.lua index 70fa2b19..782a6b0f 100644 --- a/util/adminstream.lua +++ b/util/adminstream.lua @@ -247,6 +247,11 @@ local function new_server(sessions, stanza_handler) sessions[conn] = nil; end end + + function listeners.onreadtimeout(conn) + conn:send(" "); + end + return { listeners = listeners; }; @@ -315,6 +320,10 @@ local function new_client() client.conn = nil; end + function listeners.onreadtimeout(conn) + conn:send(" "); + end + client.listeners = listeners; return client; -- cgit v1.2.3 From 15b298c191115c9acdba05d9bd2cf8d3d64f0468 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 4 Jun 2020 16:56:28 +0200 Subject: util.human.units: Factor out function for getting multiplier --- util/human/units.lua | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'util') diff --git a/util/human/units.lua b/util/human/units.lua index 5a083783..af233e98 100644 --- a/util/human/units.lua +++ b/util/human/units.lua @@ -46,17 +46,10 @@ local binary = { "Yi", 2^80, } --- n: number, the number to format --- unit: string, the base unit --- b: optional enum 'b', thousands base -local function format(n, unit, b) --> string +local function adjusted_unit(n, b) local round = math_floor; local prefixes = large; local logbase = 1000; - local fmt = "%.3g %s%s"; - if n == 0 then - return fmt:format(n, "", unit); - end if b == 'b' then prefixes = binary; logbase = 1024; @@ -66,9 +59,22 @@ local function format(n, unit, b) --> string end local m = math_max(0, math_min(8, round(math_abs(math_log(math_abs(n), logbase))))); local prefix, multiplier = unpack(prefixes, m * 2-1, m*2); - return fmt:format(n / (multiplier or 1), prefix or "", unit); + return multiplier or 1, prefix; +end + +-- n: number, the number to format +-- unit: string, the base unit +-- b: optional enum 'b', thousands base +local function format(n, unit, b) --> string + local fmt = "%.3g %s%s"; + if n == 0 then + return fmt:format(n, "", unit); + end + local multiplier, prefix = adjusted_unit(n, b); + return fmt:format(n / multiplier, prefix or "", unit); end return { + adjust = adjusted_unit; format = format; }; -- cgit v1.2.3 From 3a849a580d3fabdfb22c7da60710d3ec8ec75e42 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 4 Jun 2020 17:24:30 +0100 Subject: util.human.io: Remove padding option and use $COLUMNS as default width --- util/human/io.lua | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'util') diff --git a/util/human/io.lua b/util/human/io.lua index 9e700e89..2cea4f6b 100644 --- a/util/human/io.lua +++ b/util/human/io.lua @@ -95,12 +95,11 @@ local function padleft(s, width) return string.rep(" ", width-#s)..s; end -local function new_table(col_specs, max_width, padding) - max_width = max_width or 80; - padding = padding or 4; +local function new_table(col_specs, max_width) + max_width = max_width or tonumber(os.getenv("COLUMNS")) or 80; local widths = {}; - local total_width = max_width - padding; + local total_width = max_width; local free_width = total_width; -- Calculate width of fixed-size columns for i = 1, #col_specs do -- cgit v1.2.3 From 24c071f9a17c2b5252ba2854b0f958669f822bd1 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 4 Jun 2020 17:30:44 +0100 Subject: util.dependencies: Use util.human.io.table to replace custom layout code --- util/dependencies.lua | 47 ++++++++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 25 deletions(-) (limited to 'util') diff --git a/util/dependencies.lua b/util/dependencies.lua index ede8c6ac..b53e385b 100644 --- a/util/dependencies.lua +++ b/util/dependencies.lua @@ -7,6 +7,7 @@ -- local function softreq(...) local ok, lib = pcall(require, ...); if ok then return lib; else return nil, lib; end end +local platform_table = require "util.human.io".table({ { width = 15, align = "right" }, { width = "100%" } }); -- Required to be able to find packages installed with luarocks if not softreq "luarocks.loader" then -- LuaRocks 2.x @@ -20,12 +21,8 @@ local function missingdep(name, sources, msg, err) -- luacheck: ignore err print("Prosody was unable to find "..tostring(name)); print("This package can be obtained in the following ways:"); print(""); - local longest_platform = 0; - for platform in pairs(sources) do - longest_platform = math.max(longest_platform, #platform); - end - for platform, source in pairs(sources) do - print("", platform..":"..(" "):rep(4+longest_platform-#platform)..source); + for _, row in ipairs(sources) do + print(platform_table(row)); end print(""); print(msg or (name.." is required for Prosody to run, so we will now exit.")); @@ -49,9 +46,9 @@ local function check_dependencies() if not lxp then missingdep("luaexpat", { - ["Debian/Ubuntu"] = "sudo apt-get install lua-expat"; - ["luarocks"] = "luarocks install luaexpat"; - ["Source"] = "http://matthewwild.co.uk/projects/luaexpat/"; + { "Debian/Ubuntu", "sudo apt-get install lua-expat" }; + { "luarocks", "luarocks install luaexpat" }; + { "Source", "http://matthewwild.co.uk/projects/luaexpat/" }; }, nil, err); fatal = true; end @@ -60,9 +57,9 @@ local function check_dependencies() if not socket then missingdep("luasocket", { - ["Debian/Ubuntu"] = "sudo apt-get install lua-socket"; - ["luarocks"] = "luarocks install luasocket"; - ["Source"] = "http://www.tecgraf.puc-rio.br/~diego/professional/luasocket/"; + { "Debian/Ubuntu", "sudo apt-get install lua-socket" }; + { "luarocks", "luarocks install luasocket" }; + { "Source", "http://www.tecgraf.puc-rio.br/~diego/professional/luasocket/" }; }, nil, err); fatal = true; elseif not socket.tcp4 then @@ -74,9 +71,9 @@ local function check_dependencies() local lfs, err = softreq "lfs" if not lfs then missingdep("luafilesystem", { - ["luarocks"] = "luarocks install luafilesystem"; - ["Debian/Ubuntu"] = "sudo apt-get install lua-filesystem"; - ["Source"] = "http://www.keplerproject.org/luafilesystem/"; + { "luarocks", "luarocks install luafilesystem" }; + { "Debian/Ubuntu", "sudo apt-get install lua-filesystem" }; + { "Source", "http://www.keplerproject.org/luafilesystem/" }; }, nil, err); fatal = true; end @@ -85,9 +82,9 @@ local function check_dependencies() if not ssl then missingdep("LuaSec", { - ["Debian/Ubuntu"] = "sudo apt-get install lua-sec"; - ["luarocks"] = "luarocks install luasec"; - ["Source"] = "https://github.com/brunoos/luasec"; + { "Debian/Ubuntu", "sudo apt-get install lua-sec" }; + { "luarocks", "luarocks install luasec" }; + { "Source", "https://github.com/brunoos/luasec" }; }, "SSL/TLS support will not be available", err); end @@ -95,9 +92,9 @@ local function check_dependencies() if not bit then missingdep("lua-bitops", { - ["Debian/Ubuntu"] = "sudo apt-get install lua-bitop"; - ["luarocks"] = "luarocks install luabitop"; - ["Source"] = "http://bitop.luajit.org/"; + { "Debian/Ubuntu", "sudo apt-get install lua-bitop" }; + { "luarocks", "luarocks install luabitop" }; + { "Source", "http://bitop.luajit.org/" }; }, "WebSocket support will not be available", err); end @@ -105,8 +102,8 @@ local function check_dependencies() if not encodings then if err:match("module '[^']*' not found") then missingdep("util.encodings", { - ["Windows"] = "Make sure you have encodings.dll from the Prosody distribution in util/"; - ["GNU/Linux"] = "Run './configure' and 'make' in the Prosody source directory to build util/encodings.so"; + { "Windows", "Make sure you have encodings.dll from the Prosody distribution in util/" }; + { "GNU/Linux", "Run './configure' and 'make' in the Prosody source directory to build util/encodings.so" }; }); else print "***********************************" @@ -123,8 +120,8 @@ local function check_dependencies() if not hashes then if err:match("module '[^']*' not found") then missingdep("util.hashes", { - ["Windows"] = "Make sure you have hashes.dll from the Prosody distribution in util/"; - ["GNU/Linux"] = "Run './configure' and 'make' in the Prosody source directory to build util/hashes.so"; + { "Windows", "Make sure you have hashes.dll from the Prosody distribution in util/" }; + { "GNU/Linux", "Run './configure' and 'make' in the Prosody source directory to build util/hashes.so" }; }); else print "***********************************" -- cgit v1.2.3 From 6bcf492c34189308db20ff64e344f823e341aa77 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 4 Jun 2020 18:31:50 +0200 Subject: util.human.io: Draw a separator between columns --- util/human/io.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/human/io.lua b/util/human/io.lua index 2cea4f6b..0f0c9155 100644 --- a/util/human/io.lua +++ b/util/human/io.lua @@ -97,9 +97,10 @@ end local function new_table(col_specs, max_width) max_width = max_width or tonumber(os.getenv("COLUMNS")) or 80; + local separator = " | "; local widths = {}; - local total_width = max_width; + local total_width = max_width - #separator * (#col_specs-1); local free_width = total_width; -- Calculate width of fixed-size columns for i = 1, #col_specs do @@ -137,7 +138,7 @@ local function new_table(col_specs, max_width) end table.insert(output, v); end - return table.concat(output); + return table.concat(output, separator); end; end -- cgit v1.2.3 From d2f09cb37f8cadb2a6de205b93a7e9c80d29c5d2 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 4 Jun 2020 18:32:33 +0200 Subject: util.human.io: Replace overflow with ellipsis --- util/human/io.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/human/io.lua b/util/human/io.lua index 0f0c9155..389ed25a 100644 --- a/util/human/io.lua +++ b/util/human/io.lua @@ -128,13 +128,15 @@ local function new_table(col_specs, max_width) local output = {}; for i, column in ipairs(col_specs) do local width = widths[i]; - local v = tostring(row[not titles and column.key or i] or ""):sub(1, width); + local v = tostring(row[not titles and column.key or i] or ""); if #v < width then if column.align == "right" then v = padleft(v, width-1).." "; else v = padright(v, width); end + elseif #v > width then + v = v:sub(1, width-1) .. "\u{2026}"; end table.insert(output, v); end -- cgit v1.2.3 From 7dacb1176e6c042ea407b9973dfe9a7ecd32bda0 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 4 Jun 2020 18:36:47 +0200 Subject: util.human.io: Use literal ellipsis instead of \u escape For compat with Lua 5.2 and before --- util/human/io.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/human/io.lua b/util/human/io.lua index 389ed25a..8d987355 100644 --- a/util/human/io.lua +++ b/util/human/io.lua @@ -136,7 +136,7 @@ local function new_table(col_specs, max_width) v = padright(v, width); end elseif #v > width then - v = v:sub(1, width-1) .. "\u{2026}"; + v = v:sub(1, width-1) .. "…"; end table.insert(output, v); end -- cgit v1.2.3 From f5a1f9a5559a25a5d7d47991d1619f2a1d617077 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 4 Jun 2020 18:40:37 +0200 Subject: util.human.io: Consider separator when calculating remaining width --- util/human/io.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util') diff --git a/util/human/io.lua b/util/human/io.lua index 8d987355..76553fac 100644 --- a/util/human/io.lua +++ b/util/human/io.lua @@ -110,6 +110,9 @@ local function new_table(col_specs, max_width) width = math.max(tonumber(width), title and (#title+1) or 0); widths[i] = width; free_width = free_width - width; + if i > 1 then + free_width = free_width - #separator; + end end end -- Calculate width of %-based columns -- cgit v1.2.3 From 72f890e6914a50ff3efd9e51395453fcf1a8857c Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 4 Jun 2020 21:32:28 +0200 Subject: util.human.io.table: Allow a map callaback per column This allows e.g. mapping booleans to "yes" or "no", specific number formatting or generating virtual columns. All while not mutating the underlying data or creating additional temporary tables. --- util/human/io.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/human/io.lua b/util/human/io.lua index 76553fac..7285c79f 100644 --- a/util/human/io.lua +++ b/util/human/io.lua @@ -131,7 +131,7 @@ local function new_table(col_specs, max_width) local output = {}; for i, column in ipairs(col_specs) do local width = widths[i]; - local v = tostring(row[not titles and column.key or i] or ""); + local v = (not titles and column.mapper or tostring)(row[not titles and column.key or i] or "", row); if #v < width then if column.align == "right" then v = padleft(v, width-1).." "; -- cgit v1.2.3 From cf640c8ded8615a2721208df1274b1eeee5a2b42 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 6 Jun 2020 16:43:28 +0200 Subject: util.human.io: Fix right-alignment --- util/human/io.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/human/io.lua b/util/human/io.lua index 7285c79f..a38ab5dd 100644 --- a/util/human/io.lua +++ b/util/human/io.lua @@ -134,7 +134,7 @@ local function new_table(col_specs, max_width) local v = (not titles and column.mapper or tostring)(row[not titles and column.key or i] or "", row); if #v < width then if column.align == "right" then - v = padleft(v, width-1).." "; + v = padleft(v, width); else v = padright(v, width); end -- cgit v1.2.3 From 733df3d5d71cadace023cf5a2b0ef13ecb83650f Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 7 Jun 2020 02:14:55 +0200 Subject: util.sslconfig: Process TLS 1.3-specific cipher list Same way as with other cipher list options --- util/sslconfig.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util') diff --git a/util/sslconfig.lua b/util/sslconfig.lua index a5827a76..6074a1fb 100644 --- a/util/sslconfig.lua +++ b/util/sslconfig.lua @@ -67,6 +67,9 @@ end -- Curve list too finalisers.curveslist = finalisers.ciphers; +-- TLS 1.3 ciphers +finalisers.ciphersuites = finalisers.ciphers; + -- protocol = "x" should enable only that protocol -- protocol = "x+" should enable x and later versions -- cgit v1.2.3 From fdf2f0e9f4775809f72c6c283038f2e48a50e081 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 8 Jun 2020 14:01:02 +0100 Subject: util.promise: Add all_settled, which follows semantics of allSettled from ES2020 --- util/promise.lua | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'util') diff --git a/util/promise.lua b/util/promise.lua index 0b182b54..6ebb0dd6 100644 --- a/util/promise.lua +++ b/util/promise.lua @@ -104,6 +104,27 @@ local function all(promises) end); end +local function all_settled(promises) + return new(function (resolve) + local count, total, results = 0, #promises, {}; + for i = 1, total do + promises[i]:next(function (v) + results[i] = { status = "fulfilled", value = v }; + count = count + 1; + if count == total then + resolve(results); + end + end, function (e) + results[i] = { status = "rejected", reason = e }; + count = count + 1; + if count == total then + resolve(results); + end + end); + end + end); +end + local function race(promises) return new(function (resolve, reject) for i = 1, #promises do @@ -149,6 +170,7 @@ return { resolve = resolve; reject = reject; all = all; + all_settled = all_settled; race = race; try = try; is_promise = is_promise; -- cgit v1.2.3 From 694bdc972b3ec75320e14a26a2b34d7e3d498c8c Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 11 Jun 2020 22:02:54 +0200 Subject: util.statsd: Update for API change See change d75d805c852f to util.statistics --- util/statsd.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'util') diff --git a/util/statsd.lua b/util/statsd.lua index 67481c36..8f6151c6 100644 --- a/util/statsd.lua +++ b/util/statsd.lua @@ -38,13 +38,13 @@ local function new(config) local methods; methods = { - amount = function (name, initial) - if initial then - send_gauge(name, initial); + amount = function (name, conf) + if conf and conf.initial then + send_gauge(name, conf.initial); end return function (new_v) send_gauge(name, new_v); end end; - counter = function (name, initial) --luacheck: ignore 212/initial + counter = function (name, conf) --luacheck: ignore 212/conf return function (delta) send_gauge(name, delta, true); end; @@ -54,7 +54,7 @@ local function new(config) send_counter(name, 1); end; end; - distribution = function (name, unit, type) --luacheck: ignore 212/unit 212/type + distribution = function (name, conf) --luacheck: ignore 212/conf return function (value) send_histogram_sample(name, value); end; -- cgit v1.2.3 From 6b9051637f99f8c03be6864871be169fbfe5c537 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 13 Jun 2020 08:01:57 +0100 Subject: util.async: Rename wait -> wait_for (w/compat) Agreed this name is clearer and wait_for(thing) reads well in code. --- util/async.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/async.lua b/util/async.lua index d338071f..16dce200 100644 --- a/util/async.lua +++ b/util/async.lua @@ -246,7 +246,7 @@ local function ready() return pcall(checkthread); end -local function wait(promise) +local function wait_for(promise) local async_wait, async_done = waiter(); local ret, err = nil, nil; promise:next( @@ -266,5 +266,6 @@ return { waiter = waiter; guarder = guarder; runner = runner; - wait = wait; + wait = wait_for; -- COMPAT w/trunk pre-0.12 + wait_for = wait_for; }; -- cgit v1.2.3 From cdfa305a67507d6e7f36a943c2bb599359a54059 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 14 Jun 2020 08:49:32 +0100 Subject: util.async: Call coroutine.close() on dead threads (Lua 5.4) --- util/async.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/async.lua b/util/async.lua index 16dce200..24378d8c 100644 --- a/util/async.lua +++ b/util/async.lua @@ -53,7 +53,7 @@ local function runner_continue(thread) return false; end call_watcher(runner, "error", debug.traceback(thread, err)); - runner.state, runner.thread = "ready", nil; + runner.state = "ready"; return runner:run(); elseif state == "ready" then -- If state is 'ready', it is our responsibility to update runner.state from 'waiting'. @@ -159,6 +159,10 @@ function runner_mt:run(input) local q, thread = self.queue, self.thread; if not thread or coroutine.status(thread) == "dead" then + --luacheck: ignore 143/coroutine + if coroutine.close then + coroutine.close(thread); + end self:log("debug", "creating new coroutine"); -- Create a new coroutine for this runner thread = runner_create_thread(self.func, self); -- cgit v1.2.3 From e40ea08629de93e021547cba05c36eaf55945c01 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 14 Jun 2020 09:40:08 +0100 Subject: util.async: Don't attempt to close thread if not created yet --- util/async.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/async.lua b/util/async.lua index 24378d8c..341128d2 100644 --- a/util/async.lua +++ b/util/async.lua @@ -160,7 +160,7 @@ function runner_mt:run(input) local q, thread = self.queue, self.thread; if not thread or coroutine.status(thread) == "dead" then --luacheck: ignore 143/coroutine - if coroutine.close then + if thread and coroutine.close then coroutine.close(thread); end self:log("debug", "creating new coroutine"); -- cgit v1.2.3 From b56771c8d1ac59f875211c8a56d721fbeaf6d66e Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 14 Jun 2020 12:57:50 +0200 Subject: util.prosodyctl.check: Fix traceback by handling SRV '.' target to The IDNA to-ASCII operation returns nil in this case, which causes an error in net.dns --- util/prosodyctl/check.lua | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'util') diff --git a/util/prosodyctl/check.lua b/util/prosodyctl/check.lua index d22d5f45..ca5c51c9 100644 --- a/util/prosodyctl/check.lua +++ b/util/prosodyctl/check.lua @@ -299,6 +299,10 @@ local function check(arg) local res = dns.lookup("_xmpp-client._tcp."..idna.to_ascii(host)..".", "SRV"); if res then for _, record in ipairs(res) do + if record.srv.target == "." then -- TODO is this an error if mod_c2s is enabled? + print(" 'xmpp-client' service disabled by pointing to '.'"); -- FIXME Explain better what this is + break; + end target_hosts:add(record.srv.target); if not c2s_ports:contains(record.srv.port) then print(" SRV target "..record.srv.target.." contains unknown client port: "..record.srv.port); @@ -317,6 +321,10 @@ local function check(arg) local res = dns.lookup("_xmpp-server._tcp."..idna.to_ascii(host)..".", "SRV"); if res then for _, record in ipairs(res) do + if record.srv.target == "." then -- TODO Is this an error if mod_s2s is enabled? + print(" 'xmpp-server' service disabled by pointing to '.'"); -- FIXME Explain better what this is + break; + end target_hosts:add(record.srv.target); if not s2s_ports:contains(record.srv.port) then print(" SRV target "..record.srv.target.." contains unknown server port: "..record.srv.port); -- cgit v1.2.3 From 257c1c47b2bde0a71247a38d0661ba752bc72696 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 15 Jun 2020 14:16:10 +0100 Subject: util.gc: New module for configuring the Lua garbage collector --- util/gc.lua | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 util/gc.lua (limited to 'util') diff --git a/util/gc.lua b/util/gc.lua new file mode 100644 index 00000000..e02e85c4 --- /dev/null +++ b/util/gc.lua @@ -0,0 +1,50 @@ +local array = require "util.array"; +local set = require "util.set"; + +local known_options = { + incremental = set.new { "mode", "threshold", "speed", "step_size" }; + generational = set.new { "mode", "minor_threshold", "major_threshold" }; +}; + +if _VERSION ~= "5.4" then + known_options.generational = nil; + known_options.incremental:remove("step_size"); +end + +local function configure(user, defaults) + local mode = user.mode or defaults.mode or "incremental"; + if not known_options[mode] then + return nil, "GC mode not supported on ".._VERSION..": "..mode; + end + + for k, v in pairs(user) do + if not known_options[mode]:contains(k) then + return nil, "Unknown GC parameter: "..k; + elseif k ~= "mode" and type(v) ~= "number" then + return nil, "parameter '"..k.."' should be a number"; + end + end + + if mode == "incremental" then + if _VERSION == "Lua 5.4" then + collectgarbage(mode, + user.threshold or defaults.threshold, + user.speed or defaults.speed, + user.step_size or defaults.step_size + ); + else + collectgarbage("setpause", user.threshold or defaults.threshold); + collectgarbage("setstepmul", user.speed or defaults.speed); + end + elseif mode == "generational" then + collectgarbage(mode, + user.minor_threshold or defaults.minor_threshold, + user.major_threshold or defaults.major_threshold + ); + end + return true; +end + +return { + configure = configure; +}; -- cgit v1.2.3 From f7167c53dd46baa8dcc69cb8e7c404e70e28b367 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 15 Jun 2020 14:16:34 +0100 Subject: util.startup: Configure the GC on startup, using the config or built-in defaults --- util/startup.lua | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'util') diff --git a/util/startup.lua b/util/startup.lua index d45855f2..69633c47 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -14,6 +14,8 @@ local dependencies = require "util.dependencies"; local original_logging_config; +local default_gc_params = { mode = "incremental", threshold = 105, speed = 250 }; + local short_params = { D = "daemonize", F = "no-daemonize" }; local value_params = { config = true }; @@ -521,6 +523,19 @@ function startup.check_unwriteable() end end +function startup.init_gc() + -- Apply garbage collector settings from the config file + local gc = require "util.gc"; + local gc_settings = config.get("*", "gc") or { mode = default_gc_params.mode }; + + local ok, err = gc.configure(gc_settings, default_gc_params); + if not ok then + log("error", "Failed to apply GC configuration: %s", err); + return nil, err; + end + return true; +end + function startup.make_host(hostname) return { type = "local", @@ -551,6 +566,7 @@ function startup.prosodyctl() startup.read_config(); startup.force_console_logging(); startup.init_logging(); + startup.init_gc(); startup.setup_plugindir(); -- startup.setup_plugin_install_path(); startup.setup_datadir(); @@ -573,6 +589,7 @@ function startup.prosody() startup.init_global_state(); startup.read_config(); startup.init_logging(); + startup.init_gc(); startup.sanity_check(); startup.sandbox_require(); startup.set_function_metatable(); -- cgit v1.2.3 From 4e5cc92ac3f293b658838e0faa55645b705e3b48 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 15 Jun 2020 14:23:47 +0100 Subject: util.gc: Linter fixes [luacheck] --- util/gc.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'util') diff --git a/util/gc.lua b/util/gc.lua index e02e85c4..b400af6b 100644 --- a/util/gc.lua +++ b/util/gc.lua @@ -1,4 +1,3 @@ -local array = require "util.array"; local set = require "util.set"; local known_options = { @@ -41,7 +40,7 @@ local function configure(user, defaults) user.minor_threshold or defaults.minor_threshold, user.major_threshold or defaults.major_threshold ); - end + end return true; end -- cgit v1.2.3 From d8ec3f98bef461a1e04a1c208aef4e18d537f9f9 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 17 Jun 2020 19:32:12 +0200 Subject: util.argparse: Move exiting and error to util.startup It's not so nice to have a library that exits the entire application from under you, so this and the error reporting belongs in util.startup. The argparse code was originally in util.startup but moved out in 1196f1e8d178 but the error handling should have stayed. --- util/argparse.lua | 7 ++----- util/startup.lua | 12 +++++++++++- 2 files changed, 13 insertions(+), 6 deletions(-) (limited to 'util') diff --git a/util/argparse.lua b/util/argparse.lua index 928fc3eb..dde4fcc3 100644 --- a/util/argparse.lua +++ b/util/argparse.lua @@ -26,17 +26,14 @@ local function parse(arg, config) end if not param then - print("Unknown command-line option: "..tostring(param)); - print("Perhaps you meant to use prosodyctl instead?"); - os.exit(1); + return nil, "param-not-found", param; end local param_k, param_v; if value_params[param] then param_k, param_v = param, table.remove(arg, 1); if not param_v then - print("Expected a value to follow command-line option: "..raw_param); - os.exit(1); + return nil, "missing-value", raw_param; end else param_k, param_v = param:match("^([^=]+)=(.+)$"); diff --git a/util/startup.lua b/util/startup.lua index 69633c47..0e14395b 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -20,10 +20,20 @@ local short_params = { D = "daemonize", F = "no-daemonize" }; local value_params = { config = true }; function startup.parse_args() - prosody.opts = parse_args(arg, { + local opts, err, where = parse_args(arg, { short_params = short_params, value_params = value_params, }); + if not opts then + if err == "param-not-found" then + print("Unknown command-line option: "..tostring(where)); + print("Perhaps you meant to use prosodyctl instead?"); + elseif err == "missing-value" then + print("Expected a value to follow command-line option: "..where); + end + os.exit(1); + end + prosody.opts = opts; end function startup.read_config() -- cgit v1.2.3 From 5dc0e3901776eb546f69536bd0ca80a8715a84df Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 17 Jun 2020 19:36:39 +0200 Subject: util.prosodyctl.shell: Handle argument parsing errors While almost identical to the handling in util.startup, this seems more appropriate. It would also simplify if shell-specific options need to be handled in the future. --- util/prosodyctl/shell.lua | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'util') diff --git a/util/prosodyctl/shell.lua b/util/prosodyctl/shell.lua index 3e98540f..d0a6881e 100644 --- a/util/prosodyctl/shell.lua +++ b/util/prosodyctl/shell.lua @@ -62,6 +62,15 @@ local function start(arg) --luacheck: ignore 212/arg local client = adminstream.client(); local opts = parse_args(arg); + if not opts then + if err == "param-not-found" then + print("Unknown command-line option: "..tostring(where)); + elseif err == "missing-value" then + print("Expected a value to follow command-line option: "..where); + end + os.exit(1); + end + client.events.add_handler("connected", function () if not arg.quiet then printbanner(); -- cgit v1.2.3 From 1c8c4a4b60ed1118629d1004ec1e6639ed1108b6 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 17 Jun 2020 19:39:14 +0200 Subject: util.prosodyctl.shell: Collect extra return values Forgot in previous commit --- util/prosodyctl/shell.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/prosodyctl/shell.lua b/util/prosodyctl/shell.lua index d0a6881e..9ac982f7 100644 --- a/util/prosodyctl/shell.lua +++ b/util/prosodyctl/shell.lua @@ -60,7 +60,7 @@ end local function start(arg) --luacheck: ignore 212/arg local client = adminstream.client(); - local opts = parse_args(arg); + local opts, err, where = parse_args(arg); if not opts then if err == "param-not-found" then -- cgit v1.2.3 From 72c9c5dae88617d7d08f3b3122a06a3239ba419b Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 18 Jun 2020 17:54:28 +0200 Subject: util.adminstream: Prevent closure on read timeout --- util/adminstream.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/adminstream.lua b/util/adminstream.lua index 782a6b0f..5b592e76 100644 --- a/util/adminstream.lua +++ b/util/adminstream.lua @@ -249,7 +249,7 @@ local function new_server(sessions, stanza_handler) end function listeners.onreadtimeout(conn) - conn:send(" "); + return conn:send(" "); end return { -- cgit v1.2.3 From 3cbe9b03daa9fddabc3c46655a1ca105a7c326c5 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 9 Mar 2019 21:16:27 +0100 Subject: util.dns: Library for decoding DNS records Imported from luaunbound-prosody 5f7c771138b1 --- util/dns.lua | 271 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 271 insertions(+) create mode 100644 util/dns.lua (limited to 'util') diff --git a/util/dns.lua b/util/dns.lua new file mode 100644 index 00000000..2a15fbc4 --- /dev/null +++ b/util/dns.lua @@ -0,0 +1,271 @@ +-- libunbound based net.adns replacement for Prosody IM +-- Copyright (C) 2012-2015 Kim Alvefur +-- Copyright (C) 2012 Waqas Hussain +-- +-- This file is MIT licensed. + +local setmetatable = setmetatable; +local table = table; +local t_concat = table.concat; +local t_insert = table.insert; +local s_byte = string.byte; +local s_char = string.char; +local s_format = string.format; +local s_gsub = string.gsub; +local s_sub = string.sub; +local s_match = string.match; +local s_gmatch = string.gmatch; + +local have_net, net_util = pcall(require, "util.net"); + +if have_net and not net_util.ntop then -- Added in Prosody 0.11 + have_net = false; +end + +local chartohex = {}; + +for c = 0, 255 do + chartohex[s_char(c)] = s_format("%02X", c); +end + +local function tohex(s) + return (s_gsub(s, ".", chartohex)); +end + +-- Converted from +-- http://www.iana.org/assignments/dns-parameters +-- 2015-05-19 + +local classes = { + IN = 1; "IN"; + nil; + CH = 3; "CH"; + HS = 4; "HS"; +}; + +local types = { +"A";"NS";"MD";"MF";"CNAME";"SOA";"MB";"MG";"MR";"NULL";"WKS";"PTR";"HINFO"; +"MINFO";"MX";"TXT";"RP";"AFSDB";"X25";"ISDN";"RT";"NSAP";"NSAP-PTR";"SIG"; +"KEY";"PX";"GPOS";"AAAA";"LOC";"NXT";"EID";"NIMLOC";"SRV";"ATMA";"NAPTR"; +"KX";"CERT";"A6";"DNAME";"SINK";"OPT";"APL";"DS";"SSHFP";"IPSECKEY";"RRSIG"; +"NSEC";"DNSKEY";"DHCID";"NSEC3";"NSEC3PARAM";"TLSA";[55]="HIP";[56]="NINFO"; +[57]="RKEY";[58]="TALINK";[59]="CDS";[60]="CDNSKEY";[61]="OPENPGPKEY"; +[62]="CSYNC";TLSA=52;NS=2;[249]="TKEY";[251]="IXFR";NSAP=22;UID=101;APL=42; +MG=8;NIMLOC=32;DHCID=49;TALINK=58;HINFO=13;MINFO=14;EID=31;DS=43;CSYNC=62; +RKEY=57;TKEY=249;NID=104;NAPTR=35;RT=21;LP=107;L32=105;KEY=25;MD=3;MX=15; +A6=38;KX=36;PX=26;CAA=257;WKS=11;TSIG=250;MAILA=254;CDS=59;SINK=40;LOC=29; +DLV=32769;[32769]="DLV";TA=32768;[32768]="TA";GID=102;IXFR=251;MAILB=253; +[256]="URI";[250]="TSIG";[252]="AXFR";NSEC=47;HIP=55;[254]="MAILA";[255]="*"; +NSEC3PARAM=51;["*"]=255;URI=256;[253]="MAILB";AXFR=252;SPF=99;NXT=30;AFSDB=18; +EUI48=108;NINFO=56;CDNSKEY=60;ISDN=20;L64=106;SRV=33;DNSKEY=48;X25=19;TXT=16; +RRSIG=46;OPENPGPKEY=61;DNAME=39;CNAME=5;EUI64=109;A=1;MR=9;IPSECKEY=45;OPT=41; +UNSPEC=103;["NSAP-PTR"]=23;[103]="UNSPEC";[257]="CAA";UINFO=100;[99]="SPF"; +MF=4;[101]="UID";[102]="GID";SOA=6;[104]="NID";[105]="L32";[106]="L64"; +[107]="LP";[108]="EUI48";[109]="EUI64";NSEC3=50;RP=17;PTR=12;[100]="UINFO"; +NULL=10;AAAA=28;MB=7;GPOS=27;SSHFP=44;CERT=37;SIG=24;ATMA=34 +}; + +local errors = { + NoError = "No Error"; [0] = "NoError"; + FormErr = "Format Error"; "FormErr"; + ServFail = "Server Failure"; "ServFail"; + NXDomain = "Non-Existent Domain"; "NXDomain"; + NotImp = "Not Implemented"; "NotImp"; + Refused = "Query Refused"; "Refused"; + YXDomain = "Name Exists when it should not"; "YXDomain"; + YXRRSet = "RR Set Exists when it should not"; "YXRRSet"; + NXRRSet = "RR Set that should exist does not"; "NXRRSet"; + NotAuth = "Server Not Authoritative for zone"; "NotAuth"; + NotZone = "Name not contained in zone"; "NotZone"; +}; + +-- Simplified versions of Waqas DNS parsers +-- Only the per RR parsers are needed and only feed a single RR + +local parsers = {}; + +-- No support for pointers, but libunbound appears to take care of that. +local function readDnsName(packet, pos) + if s_byte(packet, pos) == 0 then return "."; end + local pack_len, r, len = #packet, {}; + pos = pos or 1; + repeat + len = s_byte(packet, pos) or 0; + t_insert(r, s_sub(packet, pos + 1, pos + len)); + pos = pos + len + 1; + until len == 0 or pos >= pack_len; + return t_concat(r, "."), pos; +end + +-- These are just simple names. +parsers.CNAME = readDnsName; +parsers.NS = readDnsName +parsers.PTR = readDnsName; + +local soa_mt = { + __tostring = function(rr) + return s_format("%s %s %d %d %d %d %d", rr.mname, rr.rname, rr.serial, rr.refresh, rr.retry, rr.expire, rr.minimum); + end; +}; +function parsers.SOA(packet) + local mname, rname, offset; + + mname, offset = readDnsName(packet, 1); + rname, offset = readDnsName(packet, offset); + + -- Extract all the bytes of these fields in one call + local + s1, s2, s3, s4, -- serial + r1, r2, r3, r4, -- refresh + t1, t2, t3, t4, -- retry + e1, e2, e3, e4, -- expire + m1, m2, m3, m4 -- minimum + = s_byte(packet, offset, offset + 19); + + return setmetatable({ + mname = mname; + rname = rname; + serial = s1*0x1000000 + s2*0x10000 + s3*0x100 + s4; + refresh = r1*0x1000000 + r2*0x10000 + r3*0x100 + r4; + retry = t1*0x1000000 + t2*0x10000 + t3*0x100 + t4; + expire = e1*0x1000000 + e2*0x10000 + e3*0x100 + e4; + minimum = m1*0x1000000 + m2*0x10000 + m3*0x100 + m4; + }, soa_mt); +end + +function parsers.A(packet) + return s_format("%d.%d.%d.%d", s_byte(packet, 1, 4)); +end + +local aaaa = { nil, nil, nil, nil, nil, nil, nil, nil, }; +function parsers.AAAA(packet) + local hi, lo, ip, len, token; + for i = 1, 8 do + hi, lo = s_byte(packet, i * 2 - 1, i * 2); + aaaa[i] = s_format("%x", hi * 256 + lo); -- skips leading zeros + end + ip = t_concat(aaaa, ":", 1, 8); + len = (s_match(ip, "^0:[0:]+()") or 1) - 1; + for s in s_gmatch(ip, ":0:[0:]+") do + if len < #s then len, token = #s, s; end -- find longest sequence of zeros + end + return (s_gsub(ip, token or "^0:[0:]+", "::", 1)); +end + +if have_net then + parsers.A = net_util.ntop; + parsers.AAAA = net_util.ntop; +end + +local mx_mt = { + __tostring = function(rr) + return s_format("%d %s", rr.pref, rr.mx) + end +}; +function parsers.MX(packet) + local name = readDnsName(packet, 3); + local b1,b2 = s_byte(packet, 1, 2); + return setmetatable({ + pref = b1*256+b2; + mx = name; + }, mx_mt); +end + +local srv_mt = { + __tostring = function(rr) + return s_format("%d %d %d %s", rr.priority, rr.weight, rr.port, rr.target); + end +}; +function parsers.SRV(packet) + local name = readDnsName(packet, 7); + local b1, b2, b3, b4, b5, b6 = s_byte(packet, 1, 6); + return setmetatable({ + priority = b1*256+b2; + weight = b3*256+b4; + port = b5*256+b6; + target = name; + }, srv_mt); +end + +local txt_mt = { __tostring = t_concat }; +function parsers.TXT(packet) + local pack_len = #packet; + local r, pos, len = {}, 1; + repeat + len = s_byte(packet, pos) or 0; + t_insert(r, s_sub(packet, pos + 1, pos + len)); + pos = pos + len + 1; + until pos >= pack_len; + return setmetatable(r, txt_mt); +end + +parsers.SPF = parsers.TXT; + +-- Acronyms from RFC 7218 +local tlsa_usages = { + [0] = "PKIX-CA"; + [1] = "PKIX-EE"; + [2] = "DANE-TA"; + [3] = "DANE-EE"; + [255] = "PrivCert"; +}; +local tlsa_selectors = { + [0] = "Cert", + [1] = "SPKI", + [255] = "PrivSel", +}; +local tlsa_match_types = { + [0] = "Full", + [1] = "SHA2-256", + [2] = "SHA2-512", + [255] = "PrivMatch", +}; +local tlsa_mt = { + __tostring = function(rr) + return s_format("%s %s %s %s", + tlsa_usages[rr.use] or rr.use, + tlsa_selectors[rr.select] or rr.select, + tlsa_match_types[rr.match] or rr.match, + tohex(rr.data)); + end; + __index = { + getUsage = function(rr) return tlsa_usages[rr.use] end; + getSelector = function(rr) return tlsa_selectors[rr.select] end; + getMatchType = function(rr) return tlsa_match_types[rr.match] end; + } +}; +function parsers.TLSA(packet) + local use, select, match = s_byte(packet, 1,3); + return setmetatable({ + use = use; + select = select; + match = match; + data = s_sub(packet, 4); + }, tlsa_mt); +end + +local params = { + TLSA = { + use = tlsa_usages; + select = tlsa_selectors; + match = tlsa_match_types; + }; +}; + +local fallback_mt = { + __tostring = function(rr) + return s_format([[\# %d %s]], #rr.raw, tohex(rr.raw)); + end; +}; +local function fallback_parser(packet) + return setmetatable({ raw = packet },fallback_mt); +end +setmetatable(parsers, { __index = function() return fallback_parser end }); + +return { + parsers = parsers; + classes = classes; + types = types; + errors = errors; + params = params; +}; -- cgit v1.2.3 From cb6df1369856f800fa0a572e8561f72ab0e5b05a Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 25 Jun 2020 17:26:21 +0200 Subject: util.dependencies: Add awareness of luaunbound --- util/dependencies.lua | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'util') diff --git a/util/dependencies.lua b/util/dependencies.lua index b53e385b..b76f7ab1 100644 --- a/util/dependencies.lua +++ b/util/dependencies.lua @@ -98,6 +98,14 @@ local function check_dependencies() }, "WebSocket support will not be available", err); end + local unbound, err = softreq"lunbound"; + if not unbound then + missingdep("lua-unbound", { + { "luarocks", "luarocks install luaunbound" }; + { "Source", "https://www.zash.se/luaunbound.html" }; + }, "Old DNS resolver library will be used", err); + end + local encodings, err = softreq "util.encodings" if not encodings then if err:match("module '[^']*' not found") then -- cgit v1.2.3 From 3b7348fb55baa9c30baf76cef28395b9c8ccc160 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 25 Jun 2020 17:41:07 +0200 Subject: util.dependencies: Prefer net.unbound over net.adns --- util/dependencies.lua | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'util') diff --git a/util/dependencies.lua b/util/dependencies.lua index b76f7ab1..56a04736 100644 --- a/util/dependencies.lua +++ b/util/dependencies.lua @@ -104,6 +104,11 @@ local function check_dependencies() { "luarocks", "luarocks install luaunbound" }; { "Source", "https://www.zash.se/luaunbound.html" }; }, "Old DNS resolver library will be used", err); + else + package.preload["net.adns"] = function () + local ub = require "net.unbound"; + return ub; + end end local encodings, err = softreq "util.encodings" -- cgit v1.2.3 From 0d7e7dd525bcf115a32f1a9465e3420c00239019 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 25 Jun 2020 19:28:51 +0200 Subject: util.prosodyctl.check: Use net.unbound for DNS if available Improves performance somewhat by avoiding the rate limiting in net.dns --- util/prosodyctl/check.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util') diff --git a/util/prosodyctl/check.lua b/util/prosodyctl/check.lua index ca5c51c9..86394a18 100644 --- a/util/prosodyctl/check.lua +++ b/util/prosodyctl/check.lua @@ -225,6 +225,9 @@ local function check(arg) end if not what or what == "dns" then local dns = require "net.dns"; + pcall(function () + dns = require"net.unbound".dns; + end) local idna = require "util.encodings".idna; local ip = require "util.ip"; local c2s_ports = set.new(configmanager.get("*", "c2s_ports") or {5222}); -- cgit v1.2.3 From 932870e8cc35a7db32985ed322bb06db26869374 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 25 Jun 2020 20:45:06 +0200 Subject: util.dns: Update RR types from IANA registry --- util/dns.lua | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) (limited to 'util') diff --git a/util/dns.lua b/util/dns.lua index 2a15fbc4..282c4d42 100644 --- a/util/dns.lua +++ b/util/dns.lua @@ -34,7 +34,7 @@ end -- Converted from -- http://www.iana.org/assignments/dns-parameters --- 2015-05-19 +-- 2020-06-25 local classes = { IN = 1; "IN"; @@ -48,22 +48,28 @@ local types = { "MINFO";"MX";"TXT";"RP";"AFSDB";"X25";"ISDN";"RT";"NSAP";"NSAP-PTR";"SIG"; "KEY";"PX";"GPOS";"AAAA";"LOC";"NXT";"EID";"NIMLOC";"SRV";"ATMA";"NAPTR"; "KX";"CERT";"A6";"DNAME";"SINK";"OPT";"APL";"DS";"SSHFP";"IPSECKEY";"RRSIG"; -"NSEC";"DNSKEY";"DHCID";"NSEC3";"NSEC3PARAM";"TLSA";[55]="HIP";[56]="NINFO"; -[57]="RKEY";[58]="TALINK";[59]="CDS";[60]="CDNSKEY";[61]="OPENPGPKEY"; -[62]="CSYNC";TLSA=52;NS=2;[249]="TKEY";[251]="IXFR";NSAP=22;UID=101;APL=42; -MG=8;NIMLOC=32;DHCID=49;TALINK=58;HINFO=13;MINFO=14;EID=31;DS=43;CSYNC=62; -RKEY=57;TKEY=249;NID=104;NAPTR=35;RT=21;LP=107;L32=105;KEY=25;MD=3;MX=15; -A6=38;KX=36;PX=26;CAA=257;WKS=11;TSIG=250;MAILA=254;CDS=59;SINK=40;LOC=29; -DLV=32769;[32769]="DLV";TA=32768;[32768]="TA";GID=102;IXFR=251;MAILB=253; -[256]="URI";[250]="TSIG";[252]="AXFR";NSEC=47;HIP=55;[254]="MAILA";[255]="*"; -NSEC3PARAM=51;["*"]=255;URI=256;[253]="MAILB";AXFR=252;SPF=99;NXT=30;AFSDB=18; -EUI48=108;NINFO=56;CDNSKEY=60;ISDN=20;L64=106;SRV=33;DNSKEY=48;X25=19;TXT=16; -RRSIG=46;OPENPGPKEY=61;DNAME=39;CNAME=5;EUI64=109;A=1;MR=9;IPSECKEY=45;OPT=41; -UNSPEC=103;["NSAP-PTR"]=23;[103]="UNSPEC";[257]="CAA";UINFO=100;[99]="SPF"; -MF=4;[101]="UID";[102]="GID";SOA=6;[104]="NID";[105]="L32";[106]="L64"; -[107]="LP";[108]="EUI48";[109]="EUI64";NSEC3=50;RP=17;PTR=12;[100]="UINFO"; -NULL=10;AAAA=28;MB=7;GPOS=27;SSHFP=44;CERT=37;SIG=24;ATMA=34 -}; +"NSEC";"DNSKEY";"DHCID";"NSEC3";"NSEC3PARAM";"TLSA";"SMIMEA";[55]="HIP"; +[56]="NINFO";[57]="RKEY";[58]="TALINK";[59]="CDS";[60]="CDNSKEY";[61]="OPENPGPKEY"; +[62]="CSYNC";[63]="ZONEMD";[99]="SPF";[100]="UINFO";[101]="UID";[102]="GID"; +[103]="UNSPEC";[104]="NID";[105]="L32";[106]="L64";[107]="LP";[108]="EUI48"; +[109]="EUI64";["CSYNC"]=62;["TXT"]=16;["NAPTR"]=35;["A6"]=38;["RP"]=17; +["TALINK"]=58;["NXT"]=30;["MR"]=9;["UINFO"]=100;["X25"]=19;["TKEY"]=249; +["CERT"]=37;["SMIMEA"]=53;[252]="AXFR";[253]="MAILB";["CDS"]=59;[32769]="DLV"; +["RT"]=21;["WKS"]=11;[249]="TKEY";["LP"]=107;[250]="TSIG";["SSHFP"]=44;["DS"]=43; +["ISDN"]=20;["ATMA"]=34;["NS"]=2;[257]="CAA";["PX"]=26;["MX"]=15;["TSIG"]=250; +["EID"]=31;["TLSA"]=52;["GID"]=102;["KX"]=36;["SPF"]=99;["DOA"]=259;["GPOS"]=27; +["IPSECKEY"]=45;["NIMLOC"]=32;["RRSIG"]=46;["UID"]=101;["DNAME"]=39;["NSAP"]=22; +["DNSKEY"]=48;["SINK"]=40;["DHCID"]=49;[32768]="TA";["NSAP-PTR"]=23;["AAAA"]=28; +["PTR"]=12;["MINFO"]=14;["TA"]=32768;["EUI64"]=109;[260]="AMTRELAY"; +["AMTRELAY"]=260;["CDNSKEY"]=60;[259]="DOA";["LOC"]=29;[258]="AVC";["AVC"]=258; +["CAA"]=257;["MB"]=7;["*"]=255;[256]="URI";["URI"]=256;["SRV"]=33;["EUI48"]=108; +[255]="*";[254]="MAILA";["MAILA"]=254;["MAILB"]=253;["CNAME"]=5;[251]="IXFR"; +["APL"]=42;["OPENPGPKEY"]=61;["MD"]=3;["NINFO"]=56;["ZONEMD"]=63;["RKEY"]=57; +["L32"]=105;["NID"]=104;["HIP"]=55;["NSEC"]=47;["DLV"]=32769;["UNSPEC"]=103; +["NSEC3PARAM"]=51;["MF"]=4;["MG"]=8;["AFSDB"]=18;["A"]=1;["SIG"]=24;["NSEC3"]=50; +["HINFO"]=13;["IXFR"]=251;["NULL"]=10;["AXFR"]=252;["KEY"]=25;["OPT"]=41; +["SOA"]=6;["L64"]=106; +} local errors = { NoError = "No Error"; [0] = "NoError"; -- cgit v1.2.3 From 70ab78d9b13e1bc8f2b35ed718091b1c05ce206c Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 26 Jun 2020 16:41:31 +0100 Subject: util.dbuffer: dynamic string buffer Similar to util.ringbuffer (and shares almost identical API). Differences: - size limit is optional and dynamic - does not allocate a fixed buffer of max_size bytes - focus on simply storing references to existing string objects where possible, avoiding unnecessary allocations - references are still stored in a ring buffer to enable use as a fast FIFO Optional second parameter to new() provides the number of ring buffer segments. On Lua 5.2 on my laptop, a segment is ~19 bytes. If the ring buffer fills up, the next write will compact all strings into a single item. --- util/dbuffer.lua | 171 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ util/queue.lua | 7 +++ 2 files changed, 178 insertions(+) create mode 100644 util/dbuffer.lua (limited to 'util') diff --git a/util/dbuffer.lua b/util/dbuffer.lua new file mode 100644 index 00000000..c38a16f5 --- /dev/null +++ b/util/dbuffer.lua @@ -0,0 +1,171 @@ +local queue = require "util.queue"; + +local dbuffer_methods = {}; +local dynamic_buffer_mt = { __index = dbuffer_methods }; + +function dbuffer_methods:write(data) + if self.max_size and #data + self._length > self.max_size then + return nil; + end + local ok = self.items:push(data); + if not ok then + self:collapse(); + ok = self.items:push(data); + end + if not ok then + return nil; + end + self._length = self._length + #data; + return true; +end + +function dbuffer_methods:read_chunk(requested_bytes) + local chunk, consumed = self.items:peek(), self.front_consumed; + if not chunk then return; end + local chunk_length = #chunk; + local remaining_chunk_length = chunk_length - consumed; + if remaining_chunk_length <= requested_bytes then + self.front_consumed = 0; + self._length = self._length - remaining_chunk_length; + self.items:pop(); + assert(#chunk:sub(consumed + 1, -1) == remaining_chunk_length); + return chunk:sub(consumed + 1, -1), remaining_chunk_length; + end + local end_pos = consumed + requested_bytes; + self.front_consumed = end_pos; + self._length = self._length - requested_bytes; + assert(#chunk:sub(consumed + 1, end_pos) == requested_bytes); + return chunk:sub(consumed + 1, end_pos), requested_bytes; +end + +function dbuffer_methods:read(requested_bytes) + local chunks; + + if requested_bytes > self._length then + return nil; + end + + local chunk, read_bytes = self:read_chunk(requested_bytes); + if chunk then + requested_bytes = requested_bytes - read_bytes; + if requested_bytes == 0 then -- Already read everything we need + return chunk; + end + chunks = {}; + else + return nil; + end + + -- Need to keep reading more chunks + while chunk do + table.insert(chunks, chunk); + if requested_bytes > 0 then + chunk, read_bytes = self:read_chunk(requested_bytes); + requested_bytes = requested_bytes - read_bytes; + else + break; + end + end + + return table.concat(chunks); +end + +function dbuffer_methods:discard(requested_bytes) + if requested_bytes > self._length then + return nil; + end + + local chunk, read_bytes = self:read_chunk(requested_bytes); + if chunk then + requested_bytes = requested_bytes - read_bytes; + if requested_bytes == 0 then -- Already read everything we need + return true; + end + else + return nil; + end + + while chunk do + if requested_bytes > 0 then + chunk, read_bytes = self:read_chunk(requested_bytes); + requested_bytes = requested_bytes - read_bytes; + else + break; + end + end + return true; +end + +function dbuffer_methods:sub(i, j) + if j == nil then + j = -1; + end + if j < 0 then + j = self._length + (j+1); + end + if i < 0 then + i = self._length + (i+1); + end + if i < 1 then + i = 1; + end + if j > self._length then + j = self._length; + end + if i > j then + return ""; + end + + self:collapse(j); + + return self.items:peek():sub(i, j); +end + +function dbuffer_methods:byte(i, j) + i = i or 1; + j = j or i; + return string.byte(self:sub(i, j), 1, -1); +end + +function dbuffer_methods:length() + return self._length; +end +dynamic_buffer_mt.__len = dbuffer_methods.length; -- support # operator + +function dbuffer_methods:collapse(bytes) + bytes = bytes or self._length; + + local front_chunk = self.items:peek(); + + if #front_chunk - self.front_consumed >= bytes then + return; + end + + local front_chunks = { front_chunk:sub(self.front_consumed+1) }; + local front_bytes = #front_chunks[1]; + + while front_bytes < bytes do + self.items:pop(); + local chunk = self.items:peek(); + front_bytes = front_bytes + #chunk; + table.insert(front_chunks, chunk); + end + self.items:replace(table.concat(front_chunks)); + self.front_consumed = 0; +end + +local function new(max_size, max_chunks) + if max_size and max_size <= 0 then + return nil; + end + return setmetatable({ + front_consumed = 0; + _length = 0; + max_size = max_size; + items = queue.new(max_chunks or 32); + }, dynamic_buffer_mt); +end + +return { + new = new; +}; diff --git a/util/queue.lua b/util/queue.lua index 66ed098b..c94c62ae 100644 --- a/util/queue.lua +++ b/util/queue.lua @@ -51,6 +51,13 @@ local function new(size, allow_wrapping) end return t[tail]; end; + replace = function (self, data) + if items == 0 then + return self:push(data); + end + t[tail] = data; + return true; + end; items = function (self) return function (_, pos) if pos >= items then -- cgit v1.2.3 From 8ad24d50cb6283dcc9f89b2418b9b5f518af14b1 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 27 Jun 2020 14:25:57 +0200 Subject: util.dependencies: Tone down lua-unbound dependency for now At least until packages are available Wording from MattJ --- util/dependencies.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util') diff --git a/util/dependencies.lua b/util/dependencies.lua index 56a04736..d2f87661 100644 --- a/util/dependencies.lua +++ b/util/dependencies.lua @@ -100,10 +100,12 @@ local function check_dependencies() local unbound, err = softreq"lunbound"; if not unbound then + --[[ TODO Re-enable once packages are available missingdep("lua-unbound", { { "luarocks", "luarocks install luaunbound" }; { "Source", "https://www.zash.se/luaunbound.html" }; }, "Old DNS resolver library will be used", err); + --]] else package.preload["net.adns"] = function () local ub = require "net.unbound"; -- cgit v1.2.3 From 310ca3f76c608944c62031c0e9cf15ddaeae3f7c Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 28 Jun 2020 02:15:25 +0200 Subject: util.dependencies: Quiet luacheck --- util/dependencies.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/dependencies.lua b/util/dependencies.lua index d2f87661..c117bfc2 100644 --- a/util/dependencies.lua +++ b/util/dependencies.lua @@ -98,8 +98,8 @@ local function check_dependencies() }, "WebSocket support will not be available", err); end - local unbound, err = softreq"lunbound"; - if not unbound then + local unbound, err = softreq"lunbound"; -- luacheck: ignore 211/err + if not unbound then -- luacheck: ignore 542 --[[ TODO Re-enable once packages are available missingdep("lua-unbound", { { "luarocks", "luarocks install luaunbound" }; -- cgit v1.2.3 From e39e20f7f7f1a8554032f060b5dcbc6554e10804 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 29 Jun 2020 12:51:28 +0100 Subject: util.dbuffer: If no bytes parameter passed to read, return remainder of frontmost chunk --- util/dbuffer.lua | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/dbuffer.lua b/util/dbuffer.lua index c38a16f5..63dca750 100644 --- a/util/dbuffer.lua +++ b/util/dbuffer.lua @@ -24,6 +24,9 @@ function dbuffer_methods:read_chunk(requested_bytes) if not chunk then return; end local chunk_length = #chunk; local remaining_chunk_length = chunk_length - consumed; + if not requested_bytes then + requested_bytes = remaining_chunk_length; + end if remaining_chunk_length <= requested_bytes then self.front_consumed = 0; self._length = self._length - remaining_chunk_length; @@ -41,12 +44,14 @@ end function dbuffer_methods:read(requested_bytes) local chunks; - if requested_bytes > self._length then + if requested_bytes and requested_bytes > self._length then return nil; end local chunk, read_bytes = self:read_chunk(requested_bytes); - if chunk then + if not requested_bytes then + return chunk; + elseif chunk then requested_bytes = requested_bytes - read_bytes; if requested_bytes == 0 then -- Already read everything we need return chunk; -- cgit v1.2.3 From d8efd81254826f38e3ff47287eb2833cd8121a5d Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 29 Jun 2020 16:37:58 +0200 Subject: util.timer: Defer to selected net.server if it implements this API E.g. net.server_epoll is very close and could easily be adapted to support this. --- util/timer.lua | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'util') diff --git a/util/timer.lua b/util/timer.lua index 4670e196..cbfc1992 100644 --- a/util/timer.lua +++ b/util/timer.lua @@ -16,6 +16,11 @@ local tostring = tostring; local xpcall = require "util.xpcall".xpcall; local math_max = math.max; +if server.timer then + -- The selected net.server implements this API, so defer to that + return server.timer; +end + local _ENV = nil; -- luacheck: std none -- cgit v1.2.3 From 5b16babafbc5f17f8534b71727ee6c0aa2198948 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 16 Aug 2020 12:55:55 +0200 Subject: util.dataforms: Convert media element sizes to avoid error on Lua 5.3 The stanza API does not accept number values and threw an error due to the height and width attributes of the media element (XEP-0221). This part had no test coverage previously, explaining why it was not discovered until now. --- util/dataforms.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/dataforms.lua b/util/dataforms.lua index 052d6a55..449c1a58 100644 --- a/util/dataforms.lua +++ b/util/dataforms.lua @@ -136,7 +136,7 @@ function form_t.form(layout, data, formtype) local media = field.media; if media then - form:tag("media", { xmlns = "urn:xmpp:media-element", height = media.height, width = media.width }); + form:tag("media", { xmlns = "urn:xmpp:media-element", height = ("%g"):format(media.height), width = ("%g"):format(media.width) }); for _, val in ipairs(media) do form:tag("uri", { type = val.type }):text(val.uri):up() end -- cgit v1.2.3 From 41f1cb95d3d18a0203edf3bb2f75458d42d56629 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 20 Aug 2020 15:22:19 +0100 Subject: util.dbuffer: Fix traceback when :collapse() is called on empty buffer --- util/dbuffer.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/dbuffer.lua b/util/dbuffer.lua index 63dca750..9ec40eb9 100644 --- a/util/dbuffer.lua +++ b/util/dbuffer.lua @@ -142,7 +142,7 @@ function dbuffer_methods:collapse(bytes) local front_chunk = self.items:peek(); - if #front_chunk - self.front_consumed >= bytes then + if not front_chunk or #front_chunk - self.front_consumed >= bytes then return; end -- cgit v1.2.3 From 526412d6b80bb24d43ccf537f02fa1d9450f7317 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 24 Aug 2020 16:18:13 +0100 Subject: util.dbuffer: Fix :sub() not working with partially-consumed chunks (thanks Zash for test case) This also appears to fix some bugs with chunk-encoded streams in net.http.parser. --- util/dbuffer.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/dbuffer.lua b/util/dbuffer.lua index 9ec40eb9..a50f3a64 100644 --- a/util/dbuffer.lua +++ b/util/dbuffer.lua @@ -123,7 +123,7 @@ function dbuffer_methods:sub(i, j) self:collapse(j); - return self.items:peek():sub(i, j); + return self.items:peek():sub(self.front_consumed+1):sub(i, j); end function dbuffer_methods:byte(i, j) -- cgit v1.2.3 From 7f4e1073cd28ad524ee7d1b4d414460da0641aaf Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 28 Aug 2020 12:40:59 +0100 Subject: util.error: Allow optional tracebacks to be injected on errors This allows extra debug info to be provided for development purposes. --- util/error.lua | 15 +++++++++++++++ util/startup.lua | 6 ++++++ 2 files changed, 21 insertions(+) (limited to 'util') diff --git a/util/error.lua b/util/error.lua index ca960dd9..b83302fa 100644 --- a/util/error.lua +++ b/util/error.lua @@ -8,6 +8,14 @@ local function is_err(e) return getmetatable(e) == error_mt; end +local auto_inject_traceback = false; + +local function configure(opt) + if opt.auto_inject_traceback ~= nil then + auto_inject_traceback = opt.auto_inject_traceback; + end +end + -- Do we want any more well-known fields? -- Or could we just copy all fields from `e`? -- Sometimes you want variable details in the `text`, how to handle that? @@ -17,6 +25,12 @@ end local function new(e, context, registry) local template = (registry and registry[e]) or e or {}; + context = context or template.context or { _error_id = e }; + + if auto_inject_traceback then + context.traceback = debug.traceback("error stack", 2); + end + return setmetatable({ type = template.type or "cancel"; condition = template.condition or "undefined-condition"; @@ -57,4 +71,5 @@ return { coerce = coerce; is_err = is_err; from_stanza = from_stanza; + configure = configure; } diff --git a/util/startup.lua b/util/startup.lua index 01ca585b..40021981 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -546,6 +546,10 @@ function startup.init_gc() return true; end +function startup.init_errors() + require "util.error".configure(config.get("*", "error_library")); +end + function startup.make_host(hostname) return { type = "local", @@ -577,6 +581,7 @@ function startup.prosodyctl() startup.force_console_logging(); startup.init_logging(); startup.init_gc(); + startup.init_errors(); startup.setup_plugindir(); -- startup.setup_plugin_install_path(); startup.setup_datadir(); @@ -600,6 +605,7 @@ function startup.prosody() startup.read_config(); startup.init_logging(); startup.init_gc(); + startup.init_errors(); startup.sanity_check(); startup.sandbox_require(); startup.set_function_metatable(); -- cgit v1.2.3 From 5134e640373428d5e73172b110a42b3d191471fd Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 28 Aug 2020 12:51:40 +0100 Subject: util.error: Add configuration for including traceback in tostring() --- util/error.lua | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/error.lua b/util/error.lua index b83302fa..ffdc4eec 100644 --- a/util/error.lua +++ b/util/error.lua @@ -1,6 +1,15 @@ + +-- Library configuration (see configure()) +local auto_inject_traceback = false; +local display_tracebacks = false; + + local error_mt = { __name = "error" }; function error_mt:__tostring() + if display_tracebacks and self.context.traceback then + return ("error<%s:%s:%s:%s>"):format(self.type, self.condition, self.text or "", self.context.traceback); + end return ("error<%s:%s:%s>"):format(self.type, self.condition, self.text or ""); end @@ -8,9 +17,10 @@ local function is_err(e) return getmetatable(e) == error_mt; end -local auto_inject_traceback = false; - local function configure(opt) + if opt.display_tracebacks ~= nil then + display_tracebacks = opt.display_tracebacks; + end if opt.auto_inject_traceback ~= nil then auto_inject_traceback = opt.auto_inject_traceback; end -- cgit v1.2.3 From 2a9cdf2538d3fc11685f65e2d137f5e10e511f8d Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 28 Aug 2020 12:54:31 +0100 Subject: util.startup: Init util.error with defaults if none given --- util/startup.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/startup.lua b/util/startup.lua index 40021981..a8d8594c 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -547,7 +547,7 @@ function startup.init_gc() end function startup.init_errors() - require "util.error".configure(config.get("*", "error_library")); + require "util.error".configure(config.get("*", "error_library") or {}); end function startup.make_host(hostname) -- cgit v1.2.3 From 7e563605c92e6b7ee609c33b622a1ca86b6956c4 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 28 Aug 2020 13:54:16 +0200 Subject: util.error: Add a 'source' parameter where origin module can be mentioned --- util/error.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/error.lua b/util/error.lua index ffdc4eec..600c9e5e 100644 --- a/util/error.lua +++ b/util/error.lua @@ -33,7 +33,7 @@ end -- Should the `type` be restricted to the stanza error types or free-form? -- What to set `type` to for stream errors or SASL errors? Those don't have a 'type' attr. -local function new(e, context, registry) +local function new(e, context, registry, source) local template = (registry and registry[e]) or e or {}; context = context or template.context or { _error_id = e }; @@ -48,6 +48,7 @@ local function new(e, context, registry) code = template.code; context = context or template.context or { _error_id = e }; + source = source; }, error_mt); end -- cgit v1.2.3 From 3533f8e1b2e2f12ff1a49fdf2da91a9f50e9bf6c Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 28 Aug 2020 13:55:05 +0200 Subject: util.error: Add a wrapper for common parameters Lets you set up source and registry once per module --- util/error.lua | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'util') diff --git a/util/error.lua b/util/error.lua index 600c9e5e..c46f8790 100644 --- a/util/error.lua +++ b/util/error.lua @@ -52,6 +52,12 @@ local function new(e, context, registry, source) }, error_mt); end +local function init(source, registry) + return function (e, context) + return new(e, context, registry, source); + end +end + local function coerce(ok, err, ...) if ok or is_err(err) then return ok, err, ...; @@ -79,6 +85,7 @@ end return { new = new; + init = init; coerce = coerce; is_err = is_err; from_stanza = from_stanza; -- cgit v1.2.3 From ace2ee1b3b34a9e122367abe945298c4a7e6865f Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 28 Aug 2020 18:44:02 +0200 Subject: util.jid: Fix special escaping of '\' per XEP-0106 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From XEP-0106 §2. Requirements: > in certain circumstances, the escaping character itself ("\") might > also be escaped Later in §4.2 Address Transformation Algorithm it is stated that the backslash would only be escaped if it forms an escape sequence. Thus '\foo' is unaltered but '\20' must be escaped into '\5c20'. Thanks to lovetox and jonas’ for brining up. --- util/jid.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/jid.lua b/util/jid.lua index 1ddf33d4..a1180534 100644 --- a/util/jid.lua +++ b/util/jid.lua @@ -22,7 +22,11 @@ local escapes = { ["@"] = "\\40"; ["\\"] = "\\5c"; }; local unescapes = {}; -for k,v in pairs(escapes) do unescapes[v] = k; end +local backslash_escapes = {}; +for k,v in pairs(escapes) do + unescapes[v] = k; + backslash_escapes[v] = v:gsub("\\", escapes) +end local _ENV = nil; -- luacheck: std none @@ -107,7 +111,7 @@ local function resource(jid) return (select(3, split(jid))); end -local function escape(s) return s and (s:gsub(".", escapes)); end +local function escape(s) return s and (s:gsub("\\%x%x", backslash_escapes):gsub("[\"&'/:<>@ ]", escapes)); end local function unescape(s) return s and (s:gsub("\\%x%x", unescapes)); end return { -- cgit v1.2.3 From 86d220234afd850254f3303d793709db27f3b58e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 3 Sep 2020 12:59:43 +0100 Subject: util.events: Add set_debug_hook() method Hook signature: ret = debug_hook(handler, event_name, event_data) --- util/events.lua | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/events.lua b/util/events.lua index 0bf0ddcb..5205a457 100644 --- a/util/events.lua +++ b/util/events.lua @@ -26,6 +26,8 @@ local function new() local wrappers = {}; -- Event map: event_map[handler_function] = priority_number local event_map = {}; + -- Debug hook, if any + local active_debug_hook = nil; -- Called on-demand to build handlers entries local function _rebuild_index(self, event) local _handlers = event_map[event]; @@ -74,11 +76,16 @@ local function new() end; local function _fire_event(event_name, event_data) local h = handlers[event_name]; - if h then + if h and not active_debug_hook then for i=1,#h do local ret = h[i](event_data); if ret ~= nil then return ret; end end + elseif h and active_debug_hook then + for i=1,#h do + local ret = active_debug_hook(h[i], event_name, event_data); + if ret ~= nil then return ret; end + end end end; local function fire_event(event_name, event_data) @@ -140,6 +147,13 @@ local function new() end end end + + local function set_debug_hook(new_hook) + local old_hook = active_debug_hook; + active_debug_hook = new_hook; + return old_hook; + end + return { add_handler = add_handler; remove_handler = remove_handler; @@ -150,8 +164,12 @@ local function new() add_handler = add_wrapper; remove_handler = remove_wrapper; }; + add_wrapper = add_wrapper; remove_wrapper = remove_wrapper; + + set_debug_hook = set_debug_hook; + fire_event = fire_event; _handlers = handlers; _event_map = event_map; -- cgit v1.2.3 From 5392d207d4b1a0c07245ebdc0ba749edfe5762bb Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 3 Sep 2020 13:00:43 +0100 Subject: util.helpers: when logging events, log individual handler calls --- util/helpers.lua | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'util') diff --git a/util/helpers.lua b/util/helpers.lua index 02257ffa..139b62ec 100644 --- a/util/helpers.lua +++ b/util/helpers.lua @@ -23,12 +23,27 @@ local function log_events(events, name, logger) logger("debug", "%s firing event: %s", name, event); return f(event, ...); end + + local function event_handler_hook(handler, event_name, event_data) + logger("debug", "calling handler for %s: %s", event_name, handler); + local ok, ret = pcall(handler, event_data); + if not ok then + logger("error", "error in event handler %s: %s", handler, ret); + error(ret); + end + if ret ~= nil then + logger("debug", "event chain ended for %s by %s with result: %s", event_name, handler, ret); + end + return ret; + end + events.set_debug_hook(event_handler_hook); events[events.fire_event] = f; return events; end local function revert_log_events(events) events.fire_event, events[events.fire_event] = events[events.fire_event], nil; -- :)) + events.set_debug_hook(nil); end local function log_host_events(host) -- cgit v1.2.3 From c00bd4515da975d5db8004b2b92809e04aeaec32 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 9 Sep 2020 17:10:33 +0100 Subject: util.interpolation: Add '~' as the opposite of '&' (render sub-block if falsy) One more magic character consumed! --- util/interpolation.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util') diff --git a/util/interpolation.lua b/util/interpolation.lua index e0ccf47b..808a45f2 100644 --- a/util/interpolation.lua +++ b/util/interpolation.lua @@ -64,6 +64,9 @@ local function new_render(pat, escape, funcs) elseif opt == '&' then if not value then return ""; end return render(s_sub(block, e), values); + elseif opt == '~' then + if 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 -- cgit v1.2.3 From 7cafc6927809105e4e295e35c909d1e01e8b51cb Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 21 Nov 2019 18:56:43 +0100 Subject: util.dataforms: Add support for validating (integer) ranges --- util/dataforms.lua | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/dataforms.lua b/util/dataforms.lua index 449c1a58..efb13ac9 100644 --- a/util/dataforms.lua +++ b/util/dataforms.lua @@ -10,6 +10,7 @@ local setmetatable = setmetatable; local ipairs = ipairs; local type, next = type, next; local tonumber = tonumber; +local tostring = tostring; local t_concat = table.concat; local st = require "util.stanza"; local jid_prep = require "util.jid".prep; @@ -54,6 +55,12 @@ function form_t.form(layout, data, formtype) if formtype == "form" and field.datatype then form:tag("validate", { xmlns = xmlns_validate, datatype = field.datatype }); + if field.range_min or field.range_max then + form:tag("range", { + min = field.range_min and tostring(field.range_min), + max = field.range_max and tostring(field.range_max), + }):up(); + end -- assumed form:up(); end @@ -290,13 +297,18 @@ field_readers["hidden"] = end data_validators["xs:integer"] = - function (data) + function (data, field) local n = tonumber(data); if not n then return false, "not a number"; elseif n % 1 ~= 0 then return false, "not an integer"; end + if field.range_max and n > field.range_max then + return false, "out of bounds"; + elseif field.range_min and n < field.range_min then + return false, "out of bounds"; + end return true, n; end -- cgit v1.2.3 From b55922a55a4d53d1106c6c2d2da6705a23abe63a Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 25 Sep 2020 12:18:18 +0100 Subject: util.error: Add unique 'instance_id' to error objects --- util/error.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util') diff --git a/util/error.lua b/util/error.lua index c46f8790..2c96bc03 100644 --- a/util/error.lua +++ b/util/error.lua @@ -1,3 +1,4 @@ +local id = require "util.id"; -- Library configuration (see configure()) local auto_inject_traceback = false; @@ -42,6 +43,7 @@ local function new(e, context, registry, source) end return setmetatable({ + instance_id = id.short(); type = template.type or "cancel"; condition = template.condition or "undefined-condition"; text = template.text; -- cgit v1.2.3 From 93500d02d77f1541cbe952bf3b89f2e73f2ad031 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 25 Sep 2020 12:19:30 +0100 Subject: util.error: Simplify error creation - remove ability to set context from templates, and remove default context --- util/error.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/error.lua b/util/error.lua index 2c96bc03..ade1bb3b 100644 --- a/util/error.lua +++ b/util/error.lua @@ -36,7 +36,7 @@ end local function new(e, context, registry, source) local template = (registry and registry[e]) or e or {}; - context = context or template.context or { _error_id = e }; + context = context or {}; if auto_inject_traceback then context.traceback = debug.traceback("error stack", 2); -- cgit v1.2.3 From 4f6f98131e29535594b8e53f97d2f9a8914ad05d Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 25 Sep 2020 12:27:45 +0100 Subject: util.error: Minor tweaks to error creation code to prepare for future changes --- util/error.lua | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/error.lua b/util/error.lua index ade1bb3b..4079a876 100644 --- a/util/error.lua +++ b/util/error.lua @@ -42,16 +42,19 @@ local function new(e, context, registry, source) context.traceback = debug.traceback("error stack", 2); end - return setmetatable({ + local error_instance = setmetatable({ instance_id = id.short(); + type = template.type or "cancel"; condition = template.condition or "undefined-condition"; text = template.text; code = template.code; - context = context or template.context or { _error_id = e }; + context = context; source = source; }, error_mt); + + return error_instance; end local function init(source, registry) -- cgit v1.2.3 From 7411b0a73e941b409a59d288db347efa31fc9542 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 25 Sep 2020 12:32:43 +0100 Subject: util.error: Have init() return an object to allow API extensibility via additional methods --- util/error.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'util') diff --git a/util/error.lua b/util/error.lua index 4079a876..00192273 100644 --- a/util/error.lua +++ b/util/error.lua @@ -58,9 +58,11 @@ local function new(e, context, registry, source) end local function init(source, registry) - return function (e, context) - return new(e, context, registry, source); - end + return { + new = function (e, context) + return new(e, context, registry, source); + end; + }; end local function coerce(ok, err, ...) -- cgit v1.2.3 From bcd629fbc17aab71e0ab7ce04a931626ff5b9df0 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 25 Sep 2020 12:38:58 +0100 Subject: util.error: Switch coerce() to use new() and change 'native' to context field 'wrapped_error' --- util/error.lua | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'util') diff --git a/util/error.lua b/util/error.lua index 00192273..976bc355 100644 --- a/util/error.lua +++ b/util/error.lua @@ -70,12 +70,10 @@ local function coerce(ok, err, ...) return ok, err, ...; end - local new_err = setmetatable({ - native = err; + local new_err = new({ + type = "cancel", condition = "undefined-condition" + }, { wrapped_error = err }); - type = "cancel"; - condition = "undefined-condition"; - }, error_mt); return ok, new_err, ...; end -- cgit v1.2.3 From 3e3ce993e6b3573f96ebbadae405ba753c4a5c8f Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 25 Sep 2020 16:39:22 +0100 Subject: util.error: Simplify error creation flow --- util/error.lua | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/error.lua b/util/error.lua index 976bc355..cfb35350 100644 --- a/util/error.lua +++ b/util/error.lua @@ -35,7 +35,19 @@ end -- What to set `type` to for stream errors or SASL errors? Those don't have a 'type' attr. local function new(e, context, registry, source) - local template = (registry and registry[e]) or e or {}; + local template = registry and registry[e]; + if not template then + if type(e) == "table" then + template = { + code = e.code; + type = e.type; + condition = e.condition; + text = e.text; + }; + else + template = {}; + end + end context = context or {}; if auto_inject_traceback then -- cgit v1.2.3 From cfb933ba988b70d98c8694be2beb7c346e93f947 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 26 Sep 2020 17:16:34 +0200 Subject: util.error: Add well-known field 'extra' A place for various extra fields and edge cases of the stanza error data model, e.g. the URI field of --- util/error.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util') diff --git a/util/error.lua b/util/error.lua index cfb35350..e0b1f9a6 100644 --- a/util/error.lua +++ b/util/error.lua @@ -43,6 +43,7 @@ local function new(e, context, registry, source) type = e.type; condition = e.condition; text = e.text; + extra = e.extra; }; else template = {}; @@ -61,6 +62,7 @@ local function new(e, context, registry, source) condition = template.condition or "undefined-condition"; text = template.text; code = template.code; + extra = template.extra; context = context; source = source; -- cgit v1.2.3 From 05a3d574dce37d28621bfd35703b2a2283cfd1bd Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 26 Sep 2020 17:18:17 +0200 Subject: util.stanza: Reorder code to prepare for extracting 'by' from util.error --- util/stanza.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'util') diff --git a/util/stanza.lua b/util/stanza.lua index a8a417ab..7d45d444 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -455,12 +455,12 @@ local function error_reply(orig, error_type, condition, error_message, error_by) end local t = reply(orig); t.attr.type = "error"; - if t.attr.from == error_by then - error_by = nil; - end if type(error_type) == "table" then -- an util.error or similar object error_type, condition, error_message = error_type.type, error_type.condition, error_type.text; end + if t.attr.from == error_by then + error_by = nil; + end t:tag("error", {type = error_type, by = error_by}) --COMPAT: Some day xmlns:stanzas goes here :tag(condition, xmpp_stanzas_attr):up(); if error_message then t:text_tag("text", error_message, xmpp_stanzas_attr); end -- cgit v1.2.3 From 27273548aec77ac761a23426a31a089283506750 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 26 Sep 2020 17:26:31 +0200 Subject: util.stanza: Support getting 'by' from util.error object --- util/stanza.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util') diff --git a/util/stanza.lua b/util/stanza.lua index 7d45d444..3c415f20 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -456,6 +456,9 @@ local function error_reply(orig, error_type, condition, error_message, error_by) local t = reply(orig); t.attr.type = "error"; if type(error_type) == "table" then -- an util.error or similar object + if type(error_type.extra) == "table" then + if type(error_type.extra.by) == "string" then error_by = error_type.extra.by; end + end error_type, condition, error_message = error_type.type, error_type.condition, error_type.text; end if t.attr.from == error_by then -- cgit v1.2.3 From 10f82d332163c8a3572ee507e32611d56e930ca4 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 26 Sep 2020 17:30:47 +0200 Subject: util.stanza: Support inclusion of URI from util.error object --- util/stanza.lua | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/stanza.lua b/util/stanza.lua index 3c415f20..2c936a40 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -455,9 +455,11 @@ local function error_reply(orig, error_type, condition, error_message, error_by) end local t = reply(orig); t.attr.type = "error"; + local extra; if type(error_type) == "table" then -- an util.error or similar object if type(error_type.extra) == "table" then - if type(error_type.extra.by) == "string" then error_by = error_type.extra.by; end + extra = error_type.extra; + if type(extra.by) == "string" then error_by = extra.by; end end error_type, condition, error_message = error_type.type, error_type.condition, error_type.text; end @@ -465,7 +467,11 @@ local function error_reply(orig, error_type, condition, error_message, error_by) error_by = nil; end t:tag("error", {type = error_type, by = error_by}) --COMPAT: Some day xmlns:stanzas goes here - :tag(condition, xmpp_stanzas_attr):up(); + :tag(condition, xmpp_stanzas_attr); + if extra and condition == "gone" and type(extra.uri) == "string" then + t:text(extra.uri); + end + t:up(); if error_message then t:text_tag("text", error_message, xmpp_stanzas_attr); end return t; -- stanza ready for adding app-specific errors end -- cgit v1.2.3 From 40800ea93bb1dca78c3548de58a09f424f5eef30 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 26 Sep 2020 18:07:33 +0200 Subject: util.stanza: Get 'by' from context instead Zash> should go where? extra.by? context? source? Zash> In Prosody this would usually be module.host or a bare user/room JID MattJ> Zash: context MattJ> context.by, basically the opposite of context.actor --- util/stanza.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/stanza.lua b/util/stanza.lua index 2c936a40..3d0d5002 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -459,8 +459,8 @@ local function error_reply(orig, error_type, condition, error_message, error_by) if type(error_type) == "table" then -- an util.error or similar object if type(error_type.extra) == "table" then extra = error_type.extra; - if type(extra.by) == "string" then error_by = extra.by; end end + if type(error_type.context) == "table" and type(error_type.context.by) == "string" then error_by = error_type.context.by; end error_type, condition, error_message = error_type.type, error_type.condition, error_type.text; end if t.attr.from == error_by then -- cgit v1.2.3 From a2ec5e4e52adc58ef83d5e20f268f2a1005b13ac Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 26 Sep 2020 18:09:10 +0200 Subject: util.stanza: Support Application-Specific Conditions in util.error --- util/stanza.lua | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'util') diff --git a/util/stanza.lua b/util/stanza.lua index 3d0d5002..9bcf3747 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -473,6 +473,11 @@ local function error_reply(orig, error_type, condition, error_message, error_by) end t:up(); if error_message then t:text_tag("text", error_message, xmpp_stanzas_attr); end + if extra and is_stanza(extra.tag) then + t:add_child(extra.tag); + elseif extra and extra.namespace and extra.condition then + t:tag(extra.condition, { xmlns = extra.namespace }):up(); + end return t; -- stanza ready for adding app-specific errors end -- cgit v1.2.3 From 6674d10a2294a7f767fcdd0fbffe01060d522b32 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 26 Sep 2020 18:12:18 +0200 Subject: util.stanza: Extract Application-Specific Condition from errors API change --- util/stanza.lua | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'util') diff --git a/util/stanza.lua b/util/stanza.lua index 9bcf3747..94815346 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -349,11 +349,11 @@ function stanza_mt.get_text(t) end function stanza_mt.get_error(stanza) - local error_type, condition, text; + local error_type, condition, text, extra_tag; local error_tag = stanza:get_child("error"); if not error_tag then - return nil, nil, nil; + return nil, nil, nil, nil; end error_type = error_tag.attr.type; @@ -364,12 +364,14 @@ function stanza_mt.get_error(stanza) elseif not condition then condition = child.name; end - if condition and text then - break; - end + else + extra_tag = child; + end + if condition and text and extra_tag then + break; end end - return error_type, condition or "undefined-condition", text; + return error_type, condition or "undefined-condition", text, extra_tag; end local function preserialize(stanza) -- cgit v1.2.3 From aba41217c99b08a2c64b2177a33c03fa73d0f29e Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 26 Sep 2020 18:13:27 +0200 Subject: util.error: Extract error originator from stanza errors --- util/error.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/error.lua b/util/error.lua index e0b1f9a6..c2b7d149 100644 --- a/util/error.lua +++ b/util/error.lua @@ -93,12 +93,17 @@ end local function from_stanza(stanza, context) local error_type, condition, text = stanza:get_error(); + local error_tag = stanza:get_child("error"); + context = context or {}; + context.stanza = stanza; + context.by = error_tag.attr.by; return setmetatable({ type = error_type or "cancel"; condition = condition or "undefined-condition"; text = text; - context = context or { stanza = stanza }; + context = context; + }, error_mt); end -- cgit v1.2.3 From 8f053aedc2be00ba51477dca694d0ff6eb928185 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 26 Sep 2020 18:14:10 +0200 Subject: util.error: Default error originator to stanza sender The @by attribute is primarily useful for errors caused by intermediate entities. --- util/error.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'util') diff --git a/util/error.lua b/util/error.lua index c2b7d149..0fe6cfa2 100644 --- a/util/error.lua +++ b/util/error.lua @@ -96,7 +96,8 @@ local function from_stanza(stanza, context) local error_tag = stanza:get_child("error"); context = context or {}; context.stanza = stanza; - context.by = error_tag.attr.by; + context.by = error_tag.attr.by or stanza.attr.from; + return setmetatable({ type = error_type or "cancel"; condition = condition or "undefined-condition"; -- cgit v1.2.3 From 049c2437d825dca3f97cc441803986e69d04aed5 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 26 Sep 2020 18:15:27 +0200 Subject: util.error: Add special case handling of with an URI --- util/error.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util') diff --git a/util/error.lua b/util/error.lua index 0fe6cfa2..c0b87d86 100644 --- a/util/error.lua +++ b/util/error.lua @@ -102,6 +102,9 @@ local function from_stanza(stanza, context) type = error_type or "cancel"; condition = condition or "undefined-condition"; text = text; + extra = condition == "gone" and { + uri = error_tag:get_child_text("gone", "urn:ietf:params:xml:ns:xmpp-stanzas"); + } or nil; context = context; -- cgit v1.2.3 From 534435a9d00e7389067ec6654a9afc3dc5c187ed Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 26 Sep 2020 18:15:49 +0200 Subject: util.error: Collect Application-Specific Conditions from stanza errors --- util/error.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/error.lua b/util/error.lua index c0b87d86..92567248 100644 --- a/util/error.lua +++ b/util/error.lua @@ -92,7 +92,7 @@ local function coerce(ok, err, ...) end local function from_stanza(stanza, context) - local error_type, condition, text = stanza:get_error(); + local error_type, condition, text, extra_tag = stanza:get_error(); local error_tag = stanza:get_child("error"); context = context or {}; context.stanza = stanza; @@ -102,8 +102,9 @@ local function from_stanza(stanza, context) type = error_type or "cancel"; condition = condition or "undefined-condition"; text = text; - extra = condition == "gone" and { + extra = (extra_tag or condition == "gone") and { uri = error_tag:get_child_text("gone", "urn:ietf:params:xml:ns:xmpp-stanzas"); + tag = extra_tag; } or nil; context = context; -- cgit v1.2.3 From b93e90ebb3df8770e783318443baa3bafd9652d2 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 26 Sep 2020 23:17:53 +0200 Subject: util.serialization: Let freeze metamethod return a literal string Enables custom serialization, such as creating a datatype that serializes into a variable reference. --- util/serialization.lua | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'util') diff --git a/util/serialization.lua b/util/serialization.lua index d70e92ba..d310a3e8 100644 --- a/util/serialization.lua +++ b/util/serialization.lua @@ -150,6 +150,10 @@ local function new(opt) if type(fr) == "function" then t = fr(t); + if type(t) == "string" then + o[l], l = t, l + 1; + return l; + end if type(tag) == "string" then o[l], l = tag, l + 1; end -- cgit v1.2.3 From 76fbdfbfddc32302884ef33949a82fca17e98479 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 27 Sep 2020 00:17:48 +0200 Subject: util.error: Pass converted stanza errors throguh new() In order to benefit from common processing --- util/error.lua | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'util') diff --git a/util/error.lua b/util/error.lua index 92567248..fd6deb80 100644 --- a/util/error.lua +++ b/util/error.lua @@ -91,14 +91,14 @@ local function coerce(ok, err, ...) return ok, new_err, ...; end -local function from_stanza(stanza, context) +local function from_stanza(stanza, context, source) local error_type, condition, text, extra_tag = stanza:get_error(); local error_tag = stanza:get_child("error"); context = context or {}; context.stanza = stanza; context.by = error_tag.attr.by or stanza.attr.from; - return setmetatable({ + return new({ type = error_type or "cancel"; condition = condition or "undefined-condition"; text = text; @@ -106,10 +106,7 @@ local function from_stanza(stanza, context) uri = error_tag:get_child_text("gone", "urn:ietf:params:xml:ns:xmpp-stanzas"); tag = extra_tag; } or nil; - - context = context; - - }, error_mt); + }, context, nil, source); end return { -- cgit v1.2.3 From 5407d52f84ccb1fc6dd1b04deb659d412ebb6aa8 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 28 Sep 2020 01:55:35 +0200 Subject: util.error: Turns out wasn't alone, there's also --- util/error.lua | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/error.lua b/util/error.lua index fd6deb80..44eb59a2 100644 --- a/util/error.lua +++ b/util/error.lua @@ -98,12 +98,17 @@ local function from_stanza(stanza, context, source) context.stanza = stanza; context.by = error_tag.attr.by or stanza.attr.from; + local uri; + if condition == "gone" or condition == "redirect" then + uri = error_tag:get_child_text(condition, "urn:ietf:params:xml:ns:xmpp-stanzas"); + end + return new({ type = error_type or "cancel"; condition = condition or "undefined-condition"; text = text; - extra = (extra_tag or condition == "gone") and { - uri = error_tag:get_child_text("gone", "urn:ietf:params:xml:ns:xmpp-stanzas"); + extra = (extra_tag or uri) and { + uri = uri; tag = extra_tag; } or nil; }, context, nil, source); -- cgit v1.2.3 From 8d1e8e9ad951787a257ef93dce4a02799280f62a Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 28 Sep 2020 19:26:48 +0200 Subject: util.error: Expose source and registry as fields on the registry object For access, e.g. to identify and compare errors later --- util/error.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'util') diff --git a/util/error.lua b/util/error.lua index 44eb59a2..47d4e7b6 100644 --- a/util/error.lua +++ b/util/error.lua @@ -73,6 +73,8 @@ end local function init(source, registry) return { + source = source; + registry = registry; new = function (e, context) return new(e, context, registry, source); end; -- cgit v1.2.3 From e04db26e8fa4d0af71d79b6dbe2192917d38d379 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 28 Sep 2020 18:39:51 +0200 Subject: util.error: Add a "compact mode" for registries Inspired by the older registry in pubsub.lib.lua --- util/error.lua | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'util') diff --git a/util/error.lua b/util/error.lua index 47d4e7b6..2e4118ec 100644 --- a/util/error.lua +++ b/util/error.lua @@ -58,11 +58,14 @@ local function new(e, context, registry, source) local error_instance = setmetatable({ instance_id = id.short(); - type = template.type or "cancel"; - condition = template.condition or "undefined-condition"; - text = template.text; + type = template.type or template[1] or "cancel"; + condition = template.condition or template[2] or "undefined-condition"; + text = template.text or template[3]; code = template.code; - extra = template.extra; + extra = template.extra or (registry and registry.namespace and template[4] and { + namespace = registry.namespace; + condition = template[4] + }); context = context; source = source; -- cgit v1.2.3 From 315d9ef8d9f43f0cc7929f98a6769f0567048e72 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 28 Sep 2020 22:13:04 +0200 Subject: util.error: Expand compact registries into normal form internally Also the exposed form on the table returned from init() --- util/error.lua | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) (limited to 'util') diff --git a/util/error.lua b/util/error.lua index 2e4118ec..5bd0f80e 100644 --- a/util/error.lua +++ b/util/error.lua @@ -58,14 +58,11 @@ local function new(e, context, registry, source) local error_instance = setmetatable({ instance_id = id.short(); - type = template.type or template[1] or "cancel"; - condition = template.condition or template[2] or "undefined-condition"; - text = template.text or template[3]; + type = template.type or "cancel"; + condition = template.condition or "undefined-condition"; + text = template.text; code = template.code; - extra = template.extra or (registry and registry.namespace and template[4] and { - namespace = registry.namespace; - condition = template[4] - }); + extra = template.extra; context = context; source = source; @@ -74,7 +71,36 @@ local function new(e, context, registry, source) return error_instance; end -local function init(source, registry) +-- compact --> normal form +local function expand_registry(namespace, registry) + local mapped = {} + for err,template in pairs(registry) do + local e = { + type = template[1]; + condition = template[2]; + text = template[3]; + }; + if namespace and template[4] then + e.extra = { namespace = namespace, condition = template[4] }; + end + mapped[err] = e; + end + return mapped; +end + +local function init(source, namespace, registry) + if type(namespace) == "table" then + -- registry can be given as second argument if namespace is either not used + registry, namespace = namespace, nil; + if type(registry.namespace) == "string" then + -- error templates are always type table, so this can't be one + namespace, registry.namespace = registry.namespace, nil; + end + end + local _, protoerr = next(registry, nil); + if protoerr and type(next(protoerr)) == "number" then + registry = expand_registry(namespace, registry); + end return { source = source; registry = registry; -- cgit v1.2.3 From 02b56f2304d65945480ecef156c5cf5c60b5ca9f Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 28 Sep 2020 23:48:02 +0200 Subject: util.error: Drop registry initialization with namespace as key Enough complexity with compact vs normal and with/without namespace --- util/error.lua | 4 ---- 1 file changed, 4 deletions(-) (limited to 'util') diff --git a/util/error.lua b/util/error.lua index 5bd0f80e..132389f7 100644 --- a/util/error.lua +++ b/util/error.lua @@ -92,10 +92,6 @@ local function init(source, namespace, registry) if type(namespace) == "table" then -- registry can be given as second argument if namespace is either not used registry, namespace = namespace, nil; - if type(registry.namespace) == "string" then - -- error templates are always type table, so this can't be one - namespace, registry.namespace = registry.namespace, nil; - end end local _, protoerr = next(registry, nil); if protoerr and type(next(protoerr)) == "number" then -- cgit v1.2.3 From 937336bacd199e30b999831cb71c6255a35cb06f Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 3 Oct 2020 16:22:56 +0200 Subject: util.xml: Fix float formatting of line and columns in error (on Lua 5.3+) --- util/xml.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/xml.lua b/util/xml.lua index dac3f6fe..f826b6bf 100644 --- a/util/xml.lua +++ b/util/xml.lua @@ -71,7 +71,7 @@ local parse_xml = (function() if ok then return stanza.tags[1]; else - return ok, err.." (line "..line..", col "..col..")"; + return ok, ("%s (line %d, col %d))"):format(err, line, col); end end; end)(); -- cgit v1.2.3 From 753ba4a8f2dbb2fe7d25121fafa28180ea3d6972 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 5 Oct 2020 20:56:03 +0200 Subject: util.pluginloader: Extract Lua version once It's not going to change while the module is loaded. --- util/pluginloader.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/pluginloader.lua b/util/pluginloader.lua index af0428c4..1cebe436 100644 --- a/util/pluginloader.lua +++ b/util/pluginloader.lua @@ -8,6 +8,7 @@ -- luacheck: ignore 113/CFG_PLUGINDIR local dir_sep, path_sep = package.config:match("^(%S+)%s(%S+)"); +local lua_version = _VERSION:match(" (.+)$"); local plugin_dir = {}; for path in (CFG_PLUGINDIR or "./plugins/"):gsub("[/\\]", dir_sep):gmatch("[^"..path_sep.."]+") do path = path..dir_sep; -- add path separator to path end @@ -36,7 +37,6 @@ end local function load_resource(plugin, resource) resource = resource or "mod_"..plugin..".lua"; - local lua_version = _VERSION:match(" (.+)$"); local names = { "mod_"..plugin..dir_sep..plugin..dir_sep..resource; -- mod_hello/hello/mod_hello.lua "mod_"..plugin..dir_sep..resource; -- mod_hello/mod_hello.lua -- cgit v1.2.3 From 1e7854e4b53be4a2abd550a265318d5bfbb4240e Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 5 Oct 2020 20:56:53 +0200 Subject: util.pluginloader: Look for top level mod_something.lua in luarocks-style tree --- util/pluginloader.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'util') diff --git a/util/pluginloader.lua b/util/pluginloader.lua index 1cebe436..5af5084d 100644 --- a/util/pluginloader.lua +++ b/util/pluginloader.lua @@ -42,6 +42,7 @@ local function load_resource(plugin, resource) "mod_"..plugin..dir_sep..resource; -- mod_hello/mod_hello.lua plugin..dir_sep..resource; -- hello/mod_hello.lua resource; -- mod_hello.lua + "share"..dir_sep.."lua"..dir_sep..lua_version..dir_sep..resource; "share"..dir_sep.."lua"..dir_sep..lua_version..dir_sep.."mod_"..plugin..dir_sep..resource; }; -- cgit v1.2.3 From 7f390f42dad75c3f4d1cee214ccded9b20e4552a Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 5 Oct 2020 20:58:37 +0200 Subject: util.pluginloader: Look for module libs in mod_plugin/lib.lua Luarocks can't be told to install something as foo.lib.lua AFAIK, so instead let's try mod_bar/foo.lua --- util/pluginloader.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util') diff --git a/util/pluginloader.lua b/util/pluginloader.lua index 5af5084d..8e37ffc4 100644 --- a/util/pluginloader.lua +++ b/util/pluginloader.lua @@ -60,6 +60,9 @@ end local function load_code_ext(plugin, resource, extension, env) local content, err = load_resource(plugin, resource.."."..extension); + if not content and extension == "lib.lua" then + content, err = load_resource(plugin, resource..".lua"); + end if not content then content, err = load_resource(resource, resource.."."..extension); if not content then -- cgit v1.2.3 From 22bcfff5cea846a83397de89b4a78e71245ed95c Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 5 Oct 2020 21:13:23 +0200 Subject: util.prosodyctl: Construct luarocks command line with templates More flexible and safer wrt escaping --- util/prosodyctl.lua | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua index cb86a5a1..6378856a 100644 --- a/util/prosodyctl.lua +++ b/util/prosodyctl.lua @@ -12,6 +12,7 @@ local encodings = require "util.encodings"; local stringprep = encodings.stringprep; local storagemanager = require "core.storagemanager"; local usermanager = require "core.usermanager"; +local interpolation = require "util.interpolation"; local signal = require "util.signal"; local set = require "util.set"; local lfs = require "lfs"; @@ -224,6 +225,8 @@ local function get_path_custom_plugins(plugin_paths) end end +local render_cli = interpolation.new("%b{}", function (s) return "'"..s:gsub("'","'\\''").."'" end) + local function call_luarocks(mod, operation) local dir = get_path_custom_plugins(prosody.paths.plugins); if operation == "install" then @@ -232,9 +235,11 @@ local function call_luarocks(mod, operation) show_message("Removing %s from %s", mod, dir); end if operation == "list" then - os.execute("luarocks list --tree='"..dir.."'") + os.execute(render_cli("luarocks list --tree={dir}", {dir = dir})); else - os.execute("luarocks --tree='"..dir.."' --server='http://localhost/' "..operation.." "..mod); + os.execute(render_cli("luarocks {op} --tree={dir} {server&--server={server}} {mod}", { + dir = dir; op = operation; mod = mod; server = "http://localhost/"; + })); end if operation == "install" then show_module_configuration_help(mod); -- cgit v1.2.3 From 1f4d45ecbca29698ac2c1efdfa553580f095505b Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 5 Oct 2020 21:17:29 +0200 Subject: util.prosodyctl: Flip argument order "verb subject" feels better than "subject verb", especially since the subject (module) is optional. --- util/prosodyctl.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua index 6378856a..28347455 100644 --- a/util/prosodyctl.lua +++ b/util/prosodyctl.lua @@ -227,7 +227,7 @@ end local render_cli = interpolation.new("%b{}", function (s) return "'"..s:gsub("'","'\\''").."'" end) -local function call_luarocks(mod, operation) +local function call_luarocks(operation, mod) local dir = get_path_custom_plugins(prosody.paths.plugins); if operation == "install" then show_message("Installing %s at %s", mod, dir); -- cgit v1.2.3 From 898e99680d37c55d867e29693f49f08ce04a22ed Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 5 Oct 2020 21:24:07 +0200 Subject: util.prosodyctl: Move hardcoded luarocks server into prosodyctl To be replaced with config option in future commit --- util/prosodyctl.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua index 28347455..b80ce46b 100644 --- a/util/prosodyctl.lua +++ b/util/prosodyctl.lua @@ -227,7 +227,7 @@ end local render_cli = interpolation.new("%b{}", function (s) return "'"..s:gsub("'","'\\''").."'" end) -local function call_luarocks(operation, mod) +local function call_luarocks(operation, mod, server) local dir = get_path_custom_plugins(prosody.paths.plugins); if operation == "install" then show_message("Installing %s at %s", mod, dir); @@ -238,7 +238,7 @@ local function call_luarocks(operation, mod) os.execute(render_cli("luarocks list --tree={dir}", {dir = dir})); else os.execute(render_cli("luarocks {op} --tree={dir} {server&--server={server}} {mod}", { - dir = dir; op = operation; mod = mod; server = "http://localhost/"; + dir = dir; op = operation; mod = mod; server = server; })); end if operation == "install" then -- cgit v1.2.3 From 1dba3e8c9d1159ac25639331f58fb5627454a465 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 5 Oct 2020 21:23:39 +0200 Subject: util.prosodyctl: Simplify luarocks invocation --- util/prosodyctl.lua | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'util') diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua index b80ce46b..80052163 100644 --- a/util/prosodyctl.lua +++ b/util/prosodyctl.lua @@ -234,13 +234,9 @@ local function call_luarocks(operation, mod, server) elseif operation == "remove" then show_message("Removing %s from %s", mod, dir); end - if operation == "list" then - os.execute(render_cli("luarocks list --tree={dir}", {dir = dir})); - else - os.execute(render_cli("luarocks {op} --tree={dir} {server&--server={server}} {mod}", { - dir = dir; op = operation; mod = mod; server = server; - })); - end + os.execute(render_cli("luarocks {op} --tree={dir} {server&--server={server}} {mod?}", { + dir = dir; op = operation; mod = mod; server = server; + })); if operation == "install" then show_module_configuration_help(mod); end -- cgit v1.2.3 From 63864afb77fdde6a3a4be80254bab4dbfb66db20 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 5 Oct 2020 21:43:47 +0200 Subject: util.startup: Put 'installer_plugin_path' under data directory by default Fixes issue where it ends up creating this in $PWD, which might be ~prosody, ~you or /, depending on how it's invoked. --- util/startup.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/startup.lua b/util/startup.lua index a8d8594c..339a11dc 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -260,7 +260,7 @@ function startup.setup_plugindir() end function startup.setup_plugin_install_path() - local installer_plugin_path = config.get("*", "installer_plugin_path") or "custom_plugins"; + local installer_plugin_path = config.get("*", "installer_plugin_path") or CFG_DATADIR.."/custom_plugins"; local path_sep = package.config:sub(3,3); -- TODO Figure out what this should be relative to, because CWD could be anywhere installer_plugin_path = config.resolve_relative_path(require "lfs".currentdir(), installer_plugin_path); -- cgit v1.2.3 From 9b24947796e4af6ec340a6267efa2b5a2638152c Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 5 Oct 2020 21:45:02 +0200 Subject: util.startup: Re-enable installer path setup --- util/startup.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/startup.lua b/util/startup.lua index 339a11dc..fefedcbf 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -583,7 +583,7 @@ function startup.prosodyctl() startup.init_gc(); startup.init_errors(); startup.setup_plugindir(); - -- startup.setup_plugin_install_path(); + startup.setup_plugin_install_path(); startup.setup_datadir(); startup.chdir(); startup.read_version(); @@ -612,7 +612,7 @@ function startup.prosody() startup.check_dependencies(); startup.load_libraries(); startup.setup_plugindir(); - -- startup.setup_plugin_install_path(); + startup.setup_plugin_install_path(); startup.setup_datadir(); startup.chdir(); startup.add_global_prosody_functions(); -- cgit v1.2.3 From 37bcd4270edd9923e495bd94fc7e888eade0baca Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 5 Oct 2020 21:54:17 +0200 Subject: util.startup: Fix startup failure if CFG_DATADIR is unset As is normal when running from source --- util/startup.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/startup.lua b/util/startup.lua index fefedcbf..b7d9f6fe 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -260,7 +260,7 @@ function startup.setup_plugindir() end function startup.setup_plugin_install_path() - local installer_plugin_path = config.get("*", "installer_plugin_path") or CFG_DATADIR.."/custom_plugins"; + local installer_plugin_path = config.get("*", "installer_plugin_path") or (CFG_DATADIR or "data").."/custom_plugins"; local path_sep = package.config:sub(3,3); -- TODO Figure out what this should be relative to, because CWD could be anywhere installer_plugin_path = config.resolve_relative_path(require "lfs".currentdir(), installer_plugin_path); -- cgit v1.2.3 From 810de3893ca5993290a9c3ed1525d4b7b14a8d91 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 7 Oct 2020 15:37:15 +0200 Subject: util.startup: Save the path used by the installer to prosody.paths Makes it easier for other parts of the code to use this for things, such as determining whether a certain module is from this path or from elsewhere. --- util/startup.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'util') diff --git a/util/startup.lua b/util/startup.lua index b7d9f6fe..f0b9b2c0 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -269,6 +269,7 @@ function startup.setup_plugin_install_path() require"util.paths".complement_lua_path(installer_plugin_path); -- luacheck: ignore 111 CFG_PLUGINDIR = installer_plugin_path..path_sep..(CFG_PLUGINDIR or "plugins"); + prosody.paths.installer = installer_plugin_path; prosody.paths.plugins = CFG_PLUGINDIR; end -- cgit v1.2.3 From 9a137bf062a1141e22668634c16a627afc77dc50 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 9 Oct 2020 17:34:04 +0200 Subject: util.startup: Retrieve less data for function string representation debug.getinfo(f) collects more info than what is needed here. --- util/startup.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/startup.lua b/util/startup.lua index f0b9b2c0..af126e8c 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -197,7 +197,7 @@ function startup.set_function_metatable() end end function mt.__tostring(f) - local info = debug.getinfo(f); + local info = debug.getinfo(f, "S"); return ("function(%s:%d)"):format(info.short_src:match("[^\\/]*$"), info.linedefined); end debug.setmetatable(function() end, mt); -- cgit v1.2.3 From f7b985822930ca7b6e8605173ac92ae80b28436a Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 9 Oct 2020 17:41:10 +0200 Subject: util.startup: Include arguments in function string representation Improves usability of the console when digging around the internals. No specific rationale for the function(args) format, it looked best of the variants I tried. --- util/startup.lua | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/startup.lua b/util/startup.lua index af126e8c..358c491f 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -197,8 +197,14 @@ function startup.set_function_metatable() end end function mt.__tostring(f) - local info = debug.getinfo(f, "S"); - return ("function(%s:%d)"):format(info.short_src:match("[^\\/]*$"), info.linedefined); + local info = debug.getinfo(f, "Su"); + for i = 1, info.nparams do + info[i] = debug.getlocal(f, i); + end + if info.isvararg then + info[info.nparams+1] = "..."; + end + return ("function<%s:%d>(%s)"):format(info.short_src:match("[^\\/]*$"), info.linedefined, table.concat(info, ", ")); end debug.setmetatable(function() end, mt); end -- cgit v1.2.3 From 1adf1d813b9e1ddb3578d55f22e5e77c45877f49 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 11 Oct 2020 20:25:32 +0100 Subject: util.startup: Handle missing nparams field from debug info (not present in 5.1) --- util/startup.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/startup.lua b/util/startup.lua index 358c491f..27420c57 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -198,11 +198,12 @@ function startup.set_function_metatable() end function mt.__tostring(f) local info = debug.getinfo(f, "Su"); - for i = 1, info.nparams do + local n_params = info.nparams or 0; + for i = 1, n_params do info[i] = debug.getlocal(f, i); end if info.isvararg then - info[info.nparams+1] = "..."; + info[n_params+1] = "..."; end return ("function<%s:%d>(%s)"):format(info.short_src:match("[^\\/]*$"), info.linedefined, table.concat(info, ", ")); end -- cgit v1.2.3 From bbc78f6e851990df4975bea177f182069cd6d7fa Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 15 Oct 2020 13:43:03 +0100 Subject: util.error: Pass through existing error objects passed to new() --- util/error.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'util') diff --git a/util/error.lua b/util/error.lua index 132389f7..85265e47 100644 --- a/util/error.lua +++ b/util/error.lua @@ -35,6 +35,7 @@ end -- What to set `type` to for stream errors or SASL errors? Those don't have a 'type' attr. local function new(e, context, registry, source) + if is_err(e) then return e; end local template = registry and registry[e]; if not template then if type(e) == "table" then -- cgit v1.2.3 From fba7208d2039b9c9074ac9666651dc414427540f Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 7 Sep 2019 13:38:02 +0200 Subject: util.sasl.scram: Use util.strbitop for XOR step --- util/sasl/scram.lua | 30 +----------------------------- 1 file changed, 1 insertion(+), 29 deletions(-) (limited to 'util') diff --git a/util/sasl/scram.lua b/util/sasl/scram.lua index 009a01ce..865f8cf7 100644 --- a/util/sasl/scram.lua +++ b/util/sasl/scram.lua @@ -19,9 +19,7 @@ local generate_uuid = require "util.uuid".generate; local saslprep = require "util.encodings".stringprep.saslprep; local nodeprep = require "util.encodings".stringprep.nodeprep; local log = require "util.logger".init("sasl"); -local t_concat = table.concat; -local char = string.char; -local byte = string.byte; +local binaryXOR = require "util.strbitop".sxor; local _ENV = nil; -- luacheck: std none @@ -45,32 +43,6 @@ Supported Channel Binding Backends local default_i = 4096 -local xor_map = { - 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,1,0,3,2,5,4,7,6,9,8,11,10, - 13,12,15,14,2,3,0,1,6,7,4,5,10,11,8,9,14,15,12,13,3,2,1,0,7,6,5, - 4,11,10,9,8,15,14,13,12,4,5,6,7,0,1,2,3,12,13,14,15,8,9,10,11,5, - 4,7,6,1,0,3,2,13,12,15,14,9,8,11,10,6,7,4,5,2,3,0,1,14,15,12,13, - 10,11,8,9,7,6,5,4,3,2,1,0,15,14,13,12,11,10,9,8,8,9,10,11,12,13, - 14,15,0,1,2,3,4,5,6,7,9,8,11,10,13,12,15,14,1,0,3,2,5,4,7,6,10, - 11,8,9,14,15,12,13,2,3,0,1,6,7,4,5,11,10,9,8,15,14,13,12,3,2,1, - 0,7,6,5,4,12,13,14,15,8,9,10,11,4,5,6,7,0,1,2,3,13,12,15,14,9,8, - 11,10,5,4,7,6,1,0,3,2,14,15,12,13,10,11,8,9,6,7,4,5,2,3,0,1,15, - 14,13,12,11,10,9,8,7,6,5,4,3,2,1,0, -}; - -local result = {}; -local function binaryXOR( a, b ) - for i=1, #a do - local x, y = byte(a, i), byte(b, i); - local lowx, lowy = x % 16, y % 16; - local hix, hiy = (x - lowx) / 16, (y - lowy) / 16; - local lowr, hir = xor_map[lowx * 16 + lowy + 1], xor_map[hix * 16 + hiy + 1]; - local r = hir * 16 + lowr; - result[i] = char(r) - end - return t_concat(result); -end - local function validate_username(username, _nodeprep) -- check for forbidden char sequences for eq in username:gmatch("=(.?.?)") do -- cgit v1.2.3 From b6f4936b57078c02e7bee7ae524f1e3ab32a2af2 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 11 Oct 2020 23:04:13 +0200 Subject: util.paths: Optimize path joining with few arguments A casual search suggests that the majority of paths.join() calls involve only two arguments. This saves the creation of a table for up to 3 arguments. Looks like 3x faster for 3 arguments or less, 5% slower when it uses the array to concatenate. --- util/paths.lua | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'util') diff --git a/util/paths.lua b/util/paths.lua index 036f315a..b75c35e5 100644 --- a/util/paths.lua +++ b/util/paths.lua @@ -37,8 +37,18 @@ function path_util.glob_to_pattern(glob) end).."$"; end -function path_util.join(...) - return t_concat({...}, path_sep); +function path_util.join(a, b, c, ...) -- (... : string) --> string + -- Optimization: Avoid creating table for most uses + if b then + if c then + if ... then + return t_concat({a,b,c,...}, path_sep); + end + return a..path_sep..b..path_sep..c; + end + return a..path_sep..b; + end + return a; end function path_util.complement_lua_path(installer_plugin_path) -- cgit v1.2.3 From f29013fb0c1b161d7f7ddcb335ef3191ce32bf5d Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 30 Oct 2020 13:53:39 +0000 Subject: util.dbuffer: Add __name to metatable --- util/dbuffer.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/dbuffer.lua b/util/dbuffer.lua index 640c1449..54e12266 100644 --- a/util/dbuffer.lua +++ b/util/dbuffer.lua @@ -2,7 +2,7 @@ local queue = require "util.queue"; local s_byte, s_sub = string.byte, string.sub; local dbuffer_methods = {}; -local dynamic_buffer_mt = { __index = dbuffer_methods }; +local dynamic_buffer_mt = { __name = "dbuffer", __index = dbuffer_methods }; function dbuffer_methods:write(data) if self.max_size and #data + self._length > self.max_size then -- cgit v1.2.3 From 4afbfc6854ebc374acc34729fdc6e472b44b07f1 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 30 Oct 2020 14:05:07 +0000 Subject: util.cache: Add __name to metatable --- util/cache.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util') diff --git a/util/cache.lua b/util/cache.lua index a5fd5e6d..cd1b4544 100644 --- a/util/cache.lua +++ b/util/cache.lua @@ -28,7 +28,7 @@ local function _insert(list, m) end local cache_methods = {}; -local cache_mt = { __index = cache_methods }; +local cache_mt = { __name = "cache", __index = cache_methods }; function cache_methods:set(k, v) local m = self._data[k]; -- cgit v1.2.3