diff options
Diffstat (limited to 'util')
-rw-r--r-- | util/array.lua | 16 | ||||
-rw-r--r-- | util/logger.lua | 16 | ||||
-rw-r--r-- | util/openmetrics.lua | 7 | ||||
-rw-r--r-- | util/prosodyctl/shell.lua | 19 | ||||
-rw-r--r-- | util/sslconfig.lua | 73 | ||||
-rw-r--r-- | util/stanza.lua | 32 | ||||
-rw-r--r-- | util/vcard.lua | 574 |
7 files changed, 130 insertions, 607 deletions
diff --git a/util/array.lua b/util/array.lua index c33a5ef1..9d438940 100644 --- a/util/array.lua +++ b/util/array.lua @@ -8,6 +8,7 @@ local t_insert, t_sort, t_remove, t_concat = table.insert, table.sort, table.remove, table.concat; +local t_move = require "util.table".move; local setmetatable = setmetatable; local getmetatable = getmetatable; @@ -137,13 +138,11 @@ function array_base.slice(outa, ina, i, j) return outa; end - for idx = 1, 1+j-i do - outa[idx] = ina[i+(idx-1)]; - end + + t_move(ina, i, j, 1, outa); if ina == outa then - for idx = 2+j-i, #outa do - outa[idx] = nil; - end + -- Clear (nil) remainder of range + t_move(ina, #outa+1, #outa*2, 2+j-i, ina); end return outa; end @@ -209,10 +208,7 @@ function array_methods:shuffle() end function array_methods:append(ina) - local len, len2 = #self, #ina; - for i = 1, len2 do - self[len+i] = ina[i]; - end + t_move(ina, 1, #ina, #self+1, self); return self; end diff --git a/util/logger.lua b/util/logger.lua index 20a5cef2..148b98dc 100644 --- a/util/logger.lua +++ b/util/logger.lua @@ -10,6 +10,7 @@ local pairs = pairs; local ipairs = ipairs; local require = require; +local t_remove = table.remove; local _ENV = nil; -- luacheck: std none @@ -78,6 +79,20 @@ local function add_simple_sink(simple_sink_function, levels) for _, level in ipairs(levels or {"debug", "info", "warn", "error"}) do add_level_sink(level, sink_function); end + return sink_function; +end + +local function remove_sink(sink_function) + local removed; + for level, sinks in pairs(level_sinks) do + for i = #sinks, 1, -1 do + if sinks[i] == sink_function then + t_remove(sinks, i); + removed = true; + end + end + end + return removed; end return { @@ -87,4 +102,5 @@ return { add_level_sink = add_level_sink; add_simple_sink = add_simple_sink; new = make_logger; + remove_sink = remove_sink; }; diff --git a/util/openmetrics.lua b/util/openmetrics.lua index cb7791ec..634f9de1 100644 --- a/util/openmetrics.lua +++ b/util/openmetrics.lua @@ -35,10 +35,11 @@ local t_pack, t_unpack = require "util.table".pack, table.unpack or unpack; --lu -- `with_partial_label` by the moduleapi in order to pre-set the `host` label -- on metrics created in non-global modules. local metric_proxy_mt = {} +metric_proxy_mt.__name = "metric_proxy" metric_proxy_mt.__index = metric_proxy_mt local function new_metric_proxy(metric_family, with_labels_proxy_fun) - return { + return setmetatable({ _family = metric_family, with_labels = function(self, ...) return with_labels_proxy_fun(self._family, ...) @@ -48,7 +49,7 @@ local function new_metric_proxy(metric_family, with_labels_proxy_fun) return family:with_labels(label, ...) end) end - } + }, metric_proxy_mt); end -- END of Utility: "metric proxy" @@ -128,6 +129,7 @@ end -- BEGIN of generic MetricFamily implementation local metric_family_mt = {} +metric_family_mt.__name = "metric_family" metric_family_mt.__index = metric_family_mt local function histogram_metric_ctor(orig_ctor, buckets) @@ -278,6 +280,7 @@ local function compose_name(name, unit) end local metric_registry_mt = {} +metric_registry_mt.__name = "metric_registry" metric_registry_mt.__index = metric_registry_mt local function new_metric_registry(backend) diff --git a/util/prosodyctl/shell.lua b/util/prosodyctl/shell.lua index bce27b94..8f910301 100644 --- a/util/prosodyctl/shell.lua +++ b/util/prosodyctl/shell.lua @@ -4,6 +4,8 @@ local st = require "util.stanza"; local path = require "util.paths"; local parse_args = require "util.argparse".parse; local unpack = table.unpack or _G.unpack; +local tc = require "util.termcolours"; +local isatty = require "util.pposix".isatty; local have_readline, readline = pcall(require, "readline"); @@ -27,7 +29,7 @@ local function read_line(prompt_string) end local function send_line(client, line) - client.send(st.stanza("repl-input"):text(line)); + client.send(st.stanza("repl-input", { width = os.getenv "COLUMNS" }):text(line)); end local function repl(client) @@ -64,6 +66,7 @@ end local function start(arg) --luacheck: ignore 212/arg local client = adminstream.client(); local opts, err, where = parse_args(arg); + local ttyout = isatty(io.stdout); if not opts then if err == "param-not-found" then @@ -89,11 +92,15 @@ local function start(arg) --luacheck: ignore 212/arg local errors = 0; -- TODO This is weird, but works for now. client.events.add_handler("received", function(stanza) if stanza.name == "repl-output" or stanza.name == "repl-result" then + local dest = io.stdout; if stanza.attr.type == "error" then errors = errors + 1; - io.stderr:write(stanza:get_text(), "\n"); + dest = io.stderr; + end + if stanza.attr.eol == "0" then + dest:write(stanza:get_text()); else - print(stanza:get_text()); + dest:write(stanza:get_text(), "\n"); end end if stanza.name == "repl-result" then @@ -118,7 +125,11 @@ local function start(arg) --luacheck: ignore 212/arg client.events.add_handler("received", function (stanza) 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()); + local out = result_prefix.." "..stanza:get_text(); + if ttyout and stanza.attr.type == "error" then + out = tc.getstring(tc.getstyle("red"), out); + end + print(out); end if stanza.name == "repl-result" then repl(client); diff --git a/util/sslconfig.lua b/util/sslconfig.lua index 6074a1fb..0078365b 100644 --- a/util/sslconfig.lua +++ b/util/sslconfig.lua @@ -3,9 +3,12 @@ local type = type; local pairs = pairs; local rawset = rawset; +local rawget = rawget; +local error = error; local t_concat = table.concat; local t_insert = table.insert; local setmetatable = setmetatable; +local resolve_path = require"util.paths".resolve_relative_path; local _ENV = nil; -- luacheck: std none @@ -34,7 +37,7 @@ function handlers.options(config, field, new) options[value] = true; end end - config[field] = options; + rawset(config, field, options) end handlers.verifyext = handlers.options; @@ -70,6 +73,20 @@ finalisers.curveslist = finalisers.ciphers; -- TLS 1.3 ciphers finalisers.ciphersuites = finalisers.ciphers; +-- Path expansion +function finalisers.key(path, config) + if type(path) == "string" then + return resolve_path(config._basedir, path); + else + return nil + end +end +finalisers.certificate = finalisers.key; +finalisers.cafile = finalisers.key; +finalisers.capath = finalisers.key; +-- XXX: copied from core/certmanager.lua, but this seems odd, because it would remove a dhparam function from the config +finalisers.dhparam = finalisers.key; + -- protocol = "x" should enable only that protocol -- protocol = "x+" should enable x and later versions @@ -89,37 +106,81 @@ end -- Merge options from 'new' config into 'config' local function apply(config, new) + rawset(config, "_cache", nil); if type(new) == "table" then for field, value in pairs(new) do - (handlers[field] or rawset)(config, field, value); + -- exclude keys which are internal to the config builder + if field:sub(1, 1) ~= "_" then + (handlers[field] or rawset)(config, field, value); + end end end + return config end -- Finalize the config into the form LuaSec expects local function final(config) local output = { }; for field, value in pairs(config) do - output[field] = (finalisers[field] or id)(value); + -- exclude keys which are internal to the config builder + if field:sub(1, 1) ~= "_" then + output[field] = (finalisers[field] or id)(value, config); + end end -- Need to handle protocols last because it adds to the options list protocol(output); return output; end +local function build(config) + local cached = rawget(config, "_cache"); + if cached then + return cached, nil + end + + local ctx, err = rawget(config, "_context_factory")(config:final(), config); + if ctx then + rawset(config, "_cache", ctx); + end + return ctx, err +end + local sslopts_mt = { __index = { apply = apply; final = final; + build = build; }; + __newindex = function() + error("SSL config objects cannot be modified directly. Use :apply()") + end; }; -local function new() - return setmetatable({options={}}, sslopts_mt); + +-- passing basedir through everything is required to avoid sslconfig depending +-- on prosody.paths.config +local function new(context_factory, basedir) + return setmetatable({ + _context_factory = context_factory, + _basedir = basedir, + options={}, + }, sslopts_mt); end +local function clone(config) + local result = new(); + for k, v in pairs(config) do + -- note that we *do* copy the internal keys on clone -- we have to carry + -- both the factory and the cache with us + rawset(result, k, v); + end + return result +end + +sslopts_mt.__index.clone = clone; + return { apply = apply; final = final; - new = new; + _new = new; }; diff --git a/util/stanza.lua b/util/stanza.lua index a38f80b3..a14be5f0 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -21,6 +21,8 @@ local type = type; local s_gsub = string.gsub; local s_sub = string.sub; local s_find = string.find; +local t_move = table.move or require "util.table".move; +local t_create = require"util.table".create; local valid_utf8 = require "util.encodings".utf8.valid; @@ -275,25 +277,33 @@ function stanza_mt:find(path) end local function _clone(stanza, only_top) - local attr, tags = {}, {}; + local attr = {}; 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 }; + local tags, new; + if only_top then + tags = {}; + new = { name = stanza.name, attr = attr, namespaces = namespaces, tags = tags }; + else + tags = t_create(#stanza.tags, 0); + new = t_create(#stanza, 4); + new.name = stanza.name; + new.attr = attr; + new.namespaces = namespaces; + new.tags = tags; + end + + setmetatable(new, stanza_mt); 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 + t_move(stanza, 1, #stanza, 1, new); + t_move(stanza.tags, 1, #stanza.tags, 1, tags); + new:maptags(_clone); end - return setmetatable(new, stanza_mt); + return new; end local function clone(stanza, only_top) diff --git a/util/vcard.lua b/util/vcard.lua deleted file mode 100644 index e311f73f..00000000 --- a/util/vcard.lua +++ /dev/null @@ -1,574 +0,0 @@ --- Copyright (C) 2011-2014 Kim Alvefur --- --- This project is MIT/X11 licensed. Please see the --- COPYING file in the source package for more information. --- - --- TODO --- Fix folding. - -local st = require "util.stanza"; -local t_insert, t_concat = table.insert, table.concat; -local type = type; -local pairs, ipairs = pairs, ipairs; - -local from_text, to_text, from_xep54, to_xep54; - -local line_sep = "\n"; - -local vCard_dtd; -- See end of file -local vCard4_dtd; - -local function vCard_esc(s) - return s:gsub("[,:;\\]", "\\%1"):gsub("\n","\\n"); -end - -local function vCard_unesc(s) - return s:gsub("\\?[\\nt:;,]", { - ["\\\\"] = "\\", - ["\\n"] = "\n", - ["\\r"] = "\r", - ["\\t"] = "\t", - ["\\:"] = ":", -- FIXME Shouldn't need to escape : in values, just params - ["\\;"] = ";", - ["\\,"] = ",", - [":"] = "\29", - [";"] = "\30", - [","] = "\31", - }); -end - -local function item_to_xep54(item) - local t = st.stanza(item.name, { xmlns = "vcard-temp" }); - - local prop_def = vCard_dtd[item.name]; - if prop_def == "text" then - t:text(item[1]); - elseif type(prop_def) == "table" then - if prop_def.types and item.TYPE then - if type(item.TYPE) == "table" then - for _,v in pairs(prop_def.types) do - for _,typ in pairs(item.TYPE) do - if typ:upper() == v then - t:tag(v):up(); - break; - end - end - end - else - t:tag(item.TYPE:upper()):up(); - end - end - - if prop_def.props then - for _,prop in pairs(prop_def.props) do - if item[prop] then - for _, v in ipairs(item[prop]) do - t:text_tag(prop, v); - end - end - end - end - - if prop_def.value then - t:text_tag(prop_def.value, item[1]); - elseif prop_def.values then - local prop_def_values = prop_def.values; - local repeat_last = prop_def_values.behaviour == "repeat-last" and prop_def_values[#prop_def_values]; - for i=1,#item do - t:text_tag(prop_def.values[i] or repeat_last, item[i]); - end - end - end - - return t; -end - -local function vcard_to_xep54(vCard) - local t = st.stanza("vCard", { xmlns = "vcard-temp" }); - for i=1,#vCard do - t:add_child(item_to_xep54(vCard[i])); - end - return t; -end - -function to_xep54(vCards) - if not vCards[1] or vCards[1].name then - return vcard_to_xep54(vCards) - else - local t = st.stanza("xCard", { xmlns = "vcard-temp" }); - for i=1,#vCards do - t:add_child(vcard_to_xep54(vCards[i])); - end - return t; - end -end - -function from_text(data) - data = data -- unfold and remove empty lines - :gsub("\r\n","\n") - :gsub("\n ", "") - :gsub("\n\n+","\n"); - local vCards = {}; - local current; - for line in data:gmatch("[^\n]+") do - line = vCard_unesc(line); - local name, params, value = line:match("^([-%a]+)(\30?[^\29]*)\29(.*)$"); - value = value:gsub("\29",":"); - if #params > 0 then - local _params = {}; - for k,isval,v in params:gmatch("\30([^=]+)(=?)([^\30]*)") do - k = k:upper(); - local _vt = {}; - for _p in v:gmatch("[^\31]+") do - _vt[#_vt+1]=_p - _vt[_p]=true; - end - if isval == "=" then - _params[k]=_vt; - else - _params[k]=true; - end - end - params = _params; - end - if name == "BEGIN" and value == "VCARD" then - current = {}; - vCards[#vCards+1] = current; - elseif name == "END" and value == "VCARD" then - current = nil; - elseif current and vCard_dtd[name] then - local dtd = vCard_dtd[name]; - local item = { name = name }; - t_insert(current, item); - local up = current; - current = item; - if dtd.types then - for _, t in ipairs(dtd.types) do - t = t:lower(); - if ( params.TYPE and params.TYPE[t] == true) - or params[t] == true then - current.TYPE=t; - end - end - end - if dtd.props then - for _, p in ipairs(dtd.props) do - if params[p] then - if params[p] == true then - current[p]=true; - else - for _, prop in ipairs(params[p]) do - current[p]=prop; - end - end - end - end - end - if dtd == "text" or dtd.value then - t_insert(current, value); - elseif dtd.values then - for p in ("\30"..value):gmatch("\30([^\30]*)") do - t_insert(current, p); - end - end - current = up; - end - end - return vCards; -end - -local function item_to_text(item) - local value = {}; - for i=1,#item do - value[i] = vCard_esc(item[i]); - end - value = t_concat(value, ";"); - - local params = ""; - for k,v in pairs(item) do - if type(k) == "string" and k ~= "name" then - params = params .. (";%s=%s"):format(k, type(v) == "table" and t_concat(v,",") or v); - end - end - - return ("%s%s:%s"):format(item.name, params, value) -end - -local function vcard_to_text(vcard) - local t={}; - t_insert(t, "BEGIN:VCARD") - for i=1,#vcard do - t_insert(t, item_to_text(vcard[i])); - end - t_insert(t, "END:VCARD") - return t_concat(t, line_sep); -end - -function to_text(vCards) - if vCards[1] and vCards[1].name then - return vcard_to_text(vCards) - else - local t = {}; - for i=1,#vCards do - t[i]=vcard_to_text(vCards[i]); - end - return t_concat(t, line_sep); - end -end - -local function from_xep54_item(item) - local prop_name = item.name; - local prop_def = vCard_dtd[prop_name]; - - local prop = { name = prop_name }; - - if prop_def == "text" then - prop[1] = item:get_text(); - elseif type(prop_def) == "table" then - if prop_def.value then --single item - prop[1] = item:get_child_text(prop_def.value) or ""; - elseif prop_def.values then --array - local value_names = prop_def.values; - if value_names.behaviour == "repeat-last" then - for i=1,#item.tags do - t_insert(prop, item.tags[i]:get_text() or ""); - end - else - for i=1,#value_names do - t_insert(prop, item:get_child_text(value_names[i]) or ""); - end - end - elseif prop_def.names then - local names = prop_def.names; - for i=1,#names do - if item:get_child(names[i]) then - prop[1] = names[i]; - break; - end - end - end - - if prop_def.props_verbatim then - for k,v in pairs(prop_def.props_verbatim) do - prop[k] = v; - end - end - - if prop_def.types then - local types = prop_def.types; - prop.TYPE = {}; - for i=1,#types do - if item:get_child(types[i]) then - t_insert(prop.TYPE, types[i]:lower()); - end - end - if #prop.TYPE == 0 then - prop.TYPE = nil; - end - end - - -- A key-value pair, within a key-value pair? - if prop_def.props then - local params = prop_def.props; - for i=1,#params do - local name = params[i] - local data = item:get_child_text(name); - if data then - prop[name] = prop[name] or {}; - t_insert(prop[name], data); - end - end - end - else - return nil - end - - return prop; -end - -local function from_xep54_vCard(vCard) - local tags = vCard.tags; - local t = {}; - for i=1,#tags do - t_insert(t, from_xep54_item(tags[i])); - end - return t -end - -function from_xep54(vCard) - if vCard.attr.xmlns ~= "vcard-temp" then - return nil, "wrong-xmlns"; - end - if vCard.name == "xCard" then -- A collection of vCards - local t = {}; - local vCards = vCard.tags; - for i=1,#vCards do - t[i] = from_xep54_vCard(vCards[i]); - end - return t - elseif vCard.name == "vCard" then -- A single vCard - return from_xep54_vCard(vCard) - end -end - -local vcard4 = { } - -function vcard4:text(node, params, value) -- luacheck: ignore 212/params - self:tag(node:lower()) - -- FIXME params - if type(value) == "string" then - self:text_tag("text", value); - elseif vcard4[node] then - vcard4[node](value); - end - self:up(); -end - -function vcard4.N(value) - for i, k in ipairs(vCard_dtd.N.values) do - value:text_tag(k, value[i]); - end -end - -local xmlns_vcard4 = "urn:ietf:params:xml:ns:vcard-4.0" - -local function item_to_vcard4(item) - local typ = item.name:lower(); - local t = st.stanza(typ, { xmlns = xmlns_vcard4 }); - - local prop_def = vCard4_dtd[typ]; - if prop_def == "text" then - t:text_tag("text", item[1]); - elseif prop_def == "uri" then - if item.ENCODING and item.ENCODING[1] == 'b' then - t:text_tag("uri", "data:;base64," .. item[1]); - else - t:text_tag("uri", item[1]); - end - elseif type(prop_def) == "table" then - if prop_def.values then - for i, v in ipairs(prop_def.values) do - t:text_tag(v:lower(), item[i]); - end - else - t:tag("unsupported",{xmlns="http://zash.se/protocol/vcardlib"}) - end - else - t:tag("unsupported",{xmlns="http://zash.se/protocol/vcardlib"}) - end - return t; -end - -local function vcard_to_vcard4xml(vCard) - local t = st.stanza("vcard", { xmlns = xmlns_vcard4 }); - for i=1,#vCard do - t:add_child(item_to_vcard4(vCard[i])); - end - return t; -end - -local function vcards_to_vcard4xml(vCards) - if not vCards[1] or vCards[1].name then - return vcard_to_vcard4xml(vCards) - else - local t = st.stanza("vcards", { xmlns = xmlns_vcard4 }); - for i=1,#vCards do - t:add_child(vcard_to_vcard4xml(vCards[i])); - end - return t; - end -end - --- This was adapted from http://xmpp.org/extensions/xep-0054.html#dtd -vCard_dtd = { - VERSION = "text", --MUST be 3.0, so parsing is redundant - FN = "text", - N = { - values = { - "FAMILY", - "GIVEN", - "MIDDLE", - "PREFIX", - "SUFFIX", - }, - }, - NICKNAME = "text", - PHOTO = { - props_verbatim = { ENCODING = { "b" } }, - props = { "TYPE" }, - value = "BINVAL", --{ "EXTVAL", }, - }, - BDAY = "text", - ADR = { - types = { - "HOME", - "WORK", - "POSTAL", - "PARCEL", - "DOM", - "INTL", - "PREF", - }, - values = { - "POBOX", - "EXTADD", - "STREET", - "LOCALITY", - "REGION", - "PCODE", - "CTRY", - } - }, - LABEL = { - types = { - "HOME", - "WORK", - "POSTAL", - "PARCEL", - "DOM", - "INTL", - "PREF", - }, - value = "LINE", - }, - TEL = { - types = { - "HOME", - "WORK", - "VOICE", - "FAX", - "PAGER", - "MSG", - "CELL", - "VIDEO", - "BBS", - "MODEM", - "ISDN", - "PCS", - "PREF", - }, - value = "NUMBER", - }, - EMAIL = { - types = { - "HOME", - "WORK", - "INTERNET", - "PREF", - "X400", - }, - value = "USERID", - }, - JABBERID = "text", - MAILER = "text", - TZ = "text", - GEO = { - values = { - "LAT", - "LON", - }, - }, - TITLE = "text", - ROLE = "text", - LOGO = "copy of PHOTO", - AGENT = "text", - ORG = { - values = { - behaviour = "repeat-last", - "ORGNAME", - "ORGUNIT", - } - }, - CATEGORIES = { - values = "KEYWORD", - }, - NOTE = "text", - PRODID = "text", - REV = "text", - SORTSTRING = "text", - SOUND = "copy of PHOTO", - UID = "text", - URL = "text", - CLASS = { - names = { -- The item.name is the value if it's one of these. - "PUBLIC", - "PRIVATE", - "CONFIDENTIAL", - }, - }, - KEY = { - props = { "TYPE" }, - value = "CRED", - }, - DESC = "text", -}; -vCard_dtd.LOGO = vCard_dtd.PHOTO; -vCard_dtd.SOUND = vCard_dtd.PHOTO; - -vCard4_dtd = { - source = "uri", - kind = "text", - xml = "text", - fn = "text", - n = { - values = { - "family", - "given", - "middle", - "prefix", - "suffix", - }, - }, - nickname = "text", - photo = "uri", - bday = "date-and-or-time", - anniversary = "date-and-or-time", - gender = "text", - adr = { - values = { - "pobox", - "ext", - "street", - "locality", - "region", - "code", - "country", - } - }, - tel = "text", - email = "text", - impp = "uri", - lang = "language-tag", - tz = "text", - geo = "uri", - title = "text", - role = "text", - logo = "uri", - org = "text", - member = "uri", - related = "uri", - categories = "text", - note = "text", - prodid = "text", - rev = "timestamp", - sound = "uri", - uid = "uri", - clientpidmap = "number, uuid", - url = "uri", - version = "text", - key = "uri", - fburl = "uri", - caladruri = "uri", - caluri = "uri", -}; - -return { - from_text = from_text; - to_text = to_text; - - from_xep54 = from_xep54; - to_xep54 = to_xep54; - - to_vcard4 = vcards_to_vcard4xml; -}; |