Mercurial > prosody-hg
changeset 13919:6ad72bd9a7ec
Merge 13.0->trunk
| author | Kim Alvefur <zash@zash.se> |
|---|---|
| date | Sun, 27 Jul 2025 01:11:58 +0200 |
| parents | 598359d9f580 (diff) f0d854c63c8d (current diff) |
| children | 33755e085ca5 |
| files | |
| diffstat | 34 files changed, 416 insertions(+), 238 deletions(-) [+] |
line wrap: on
line diff
--- a/GNUmakefile Sun Jul 27 01:08:14 2025 +0200 +++ b/GNUmakefile Sun Jul 27 01:11:58 2025 +0200 @@ -141,9 +141,10 @@ vpath %.tl teal-src/prosody %.lua: %.tl tl -I teal-src/ --gen-compat off --gen-target 5.1 gen $^ -o $@ - -lua-format -i $@ + -lua-format -i --no-keep-simple-control-block-one-line --no-keep-simple-function-one-line $@ + sed -i "1i-- This file is generated from $<" $@ -teal: util/jsonschema.lua util/datamapper.lua util/jsonpointer.lua +teal: util/jsonschema.lua util/datamapper.lua util/jsonpointer.lua plugins/mod_cron.lua util/%.so: $(MAKE) install -C util-src
--- a/core/features.lua Sun Jul 27 01:08:14 2025 +0200 +++ b/core/features.lua Sun Jul 27 01:11:58 2025 +0200 @@ -20,6 +20,8 @@ "loader"; -- "keyval+" store "keyval+"; + -- Support for target filter in net.resolvers.basic + "net-connect-filter"; "s2sout-pre-connect-event";
--- a/core/moduleapi.lua Sun Jul 27 01:08:14 2025 +0200 +++ b/core/moduleapi.lua Sun Jul 27 01:11:58 2025 +0200 @@ -93,7 +93,14 @@ end function api:hook(event, handler, priority) - return self:hook_object_event((hosts[self.host] or prosody).events, event, handler, priority); + local events; + local host = self.host; + if host == "*" then + events = prosody.events; + else + events = hosts[host].events; + end + return self:hook_object_event(events, event, handler, priority); end function api:hook_global(event, handler, priority) @@ -405,7 +412,14 @@ end function api:context(host) - return setmetatable({ host = host or "*", global = "*" == host }, { __index = self, __newindex = self }); + local is_global; + if not host or host == "*" then + host, is_global = "*", true; + end + if not is_global and not hosts[host] then + error("Cannot create context for unknown host: "..host); + end + return setmetatable({ host = host, global = is_global }, { __index = self, __newindex = self }); end function api:add_item(key, value)
--- a/net/resolvers/basic.lua Sun Jul 27 01:08:14 2025 +0200 +++ b/net/resolvers/basic.lua Sun Jul 27 01:11:58 2025 +0200 @@ -66,6 +66,9 @@ return; end local next_target = table.remove(self.targets, 1); + if self.filter and not self.filter(next_target[1], next_target[2], next_target[3], next_target[4], not not self.targets[1]) then + return self:next(cb); + end cb(next_target[1], next_target[2], next_target[3], next_target[4], not not self.targets[1]); return; end @@ -142,6 +145,7 @@ port = port; conn_type = conn_type; extra = extra or {}; + filter = extra and extra.filter; targets = targets; }, resolver_mt); end
--- a/plugins/mod_cron.lua Sun Jul 27 01:08:14 2025 +0200 +++ b/plugins/mod_cron.lua Sun Jul 27 01:11:58 2025 +0200 @@ -1,3 +1,4 @@ +-- This file is generated from teal-src/prosody/plugins/mod_cron.tl module:set_global(); local async = require("prosody.util.async"); @@ -9,7 +10,7 @@ local active_hosts = {} if prosody.process_type == "prosodyctl" then - return; -- Yes, it happens... + return end function module.add_host(host_module) @@ -17,14 +18,24 @@ local last_run_times = host_module:open_store("cron", "map"); active_hosts[host_module.host] = true; - local function save_task(task, started_at) last_run_times:set(nil, task.id, started_at); end + local function save_task(task, started_at) + last_run_times:set(nil, task.id, started_at); + end - local function restore_task(task) if task.last == nil then task.last = last_run_times:get(nil, task.id); end end + local function restore_task(task) + if task.last == nil then + task.last = last_run_times:get(nil, task.id); + end + end local function task_added(event) local task = event.item; - if task.name == nil then task.name = task.when; end - if task.id == nil then task.id = event.source.name .. "/" .. task.name:gsub("%W", "_"):lower(); end + if task.name == nil then + task.name = task.when; + end + if task.id == nil then + task.id = event.source.name .. "/" .. task.name:gsub("%W", "_"):lower(); + end task.period = host_module:get_option_period(task.id:gsub("/", "_") .. "_period", "1" .. task.when, 60, 86400 * 7 * 53); task.restore = restore_task; task.save = save_task; @@ -40,14 +51,20 @@ host_module:handle_items("task", task_added, task_removed, true); - function host_module.unload() active_hosts[host_module.host] = nil; end + function host_module.unload() + active_hosts[host_module.host] = nil; + end end -local function should_run(task, last) return not last or last + task.period * 0.995 <= os.time() end +local function should_run(task, last) + return not last or last + task.period * 0.995 <= os.time() +end local function run_task(task) task:restore(); - if not should_run(task, task.last) then return end + if not should_run(task, task.last) then + return + end local started_at = os.time(); task:run(started_at); task.last = started_at; @@ -55,7 +72,7 @@ end local function spread(t, factor) - return t * (1 - factor + 2*factor*math.random()); + return t * (1 - factor + 2 * factor * math.random()) end local task_runner = async.runner(run_task); @@ -64,7 +81,9 @@ local delay = spread(cron_check_delay, cron_spread_factor); for host in pairs(active_hosts) do module:log("debug", "Running periodic tasks for host %s", host); - for _, task in ipairs(module:context(host):get_host_items("task")) do task_runner:run(task); end + for _, task in ipairs(module:context(host):get_host_items("task")) do + task_runner:run(task); + end end module:log("debug", "Wait %gs", delay); return delay
--- a/plugins/mod_csi_simple.lua Sun Jul 27 01:08:14 2025 +0200 +++ b/plugins/mod_csi_simple.lua Sun Jul 27 01:11:58 2025 +0200 @@ -11,6 +11,7 @@ local dt = require "prosody.util.datetime"; local filters = require "prosody.util.filters"; local timer = require "prosody.util.timer"; +local jid_bare = require "prosody.util.jid".bare; local queue_size = module:get_option_integer("csi_queue_size", 256, 1); local resume_delay = module:get_option_period("csi_resume_inactive_delay", 5); @@ -40,13 +41,18 @@ -- TODO Some MUC awareness, e.g. check for the 'this relates to you' status code return true, "subscription request"; elseif st_name == "message" then + if st_type == "error" then + return true, "delivery failure"; + end + if stanza:get_child("event", "http://jabber.org/protocol/pubsub#event") then + if stanza.attr.from == nil or stanza.attr.from == jid_bare(stanza.attr.to) then + return true, "self-pep-event"; + end + end if st_type == "headline" then -- Headline messages are ephemeral by definition return false, "headline"; end - if st_type == "error" then - return true, "delivery failure"; - end if stanza:get_child("sent", "urn:xmpp:carbons:2") then return true, "carbon"; end
--- a/plugins/mod_external_services.lua Sun Jul 27 01:08:14 2025 +0200 +++ b/plugins/mod_external_services.lua Sun Jul 27 01:11:58 2025 +0200 @@ -101,6 +101,9 @@ srv.restricted = true; end end + if item.extra and st.is_stanza(item.extra) then + srv.extra = item.extra; + end return srv; end @@ -149,7 +152,11 @@ password = srv.password; expires = srv.expires and dt.datetime(srv.expires) or nil; restricted = srv.restricted and "1" or nil; - }):up(); + }); + if srv.extra then + reply:add_child(srv.extra); + end + reply:up(); end return reply; @@ -242,3 +249,43 @@ module:add_feature("urn:xmpp:extdisco:1"); module:hook("iq-get/host/urn:xmpp:extdisco:1:services", handle_services); module:hook("iq-get/host/urn:xmpp:extdisco:1:credentials", handle_credentials); + + +module:add_item("shell-command", { + section = "services"; + section_desc = "Inspect external services"; + name = "list"; + desc = "List all external services for a host"; + args = { + { name = "host", type = "string" }; + { name = "service", type = "string" }; + }; + host_selector = "host"; + handler = function (shell, host, service_type) --luacheck: ignore 212/host + local c = 0; + local print = shell.session.print; + local services = get_services(); + for _, srv in ipairs(services) do + if not service_type or service_type:lower() == srv.type:lower() then + c = c + 1; + print(srv.type.." ("..srv.transport..")", srv.host..(srv.port and (":"..srv.port) or "")); + print(" auth:", srv.restricted and "required" or "none"); + for _, prop in ipairs({ "username", "password", "expires" }) do + local val = srv[prop]; + if val then + if prop == "expires" then + val = dt.datetime(val); + end + print(" "..prop..":", val); + end + end + if srv.extra then + print(" extra:"); + print(" "..tostring(srv.extra:indent(3, " "))); + end + print(""); + end + end + return true, ("%d services"):format(c); + end; +});
--- a/plugins/mod_http_files.lua Sun Jul 27 01:08:14 2025 +0200 +++ b/plugins/mod_http_files.lua Sun Jul 27 01:11:58 2025 +0200 @@ -11,7 +11,7 @@ local open = io.open; local fileserver = require"prosody.net.http.files"; -local base_path = module:get_option_path("http_files_dir", module:get_option_path("http_path")); +local base_path = assert(module:get_option_path("http_files_dir", module:get_option_path("http_path")), "missing required setting 'http_files_dir'"); local cache_size = module:get_option_integer("http_files_cache_size", 128, 1); local cache_max_file_size = module:get_option_integer("http_files_cache_max_file_size", 4096, 1); local dir_indices = module:get_option_array("http_index_files", { "index.html", "index.htm" }); @@ -55,37 +55,12 @@ end -- COMPAT -- TODO deprecate -function serve(opts) - if type(opts) ~= "table" then -- assume path string - opts = { path = opts }; - end - if opts.directory_index == nil then - opts.directory_index = directory_index; - end - if opts.mime_map == nil then - opts.mime_map = mime_map; - end - if opts.cache_size == nil then - opts.cache_size = cache_size; - end - if opts.cache_max_file_size == nil then - opts.cache_max_file_size = cache_max_file_size; - end - if opts.index_files == nil then - opts.index_files = dir_indices; - end - module:log("warn", "%s should be updated to use 'prosody.net.http.files' instead of mod_http_files", get_calling_module()); - return fileserver.serve(opts); +function serve() + error(string.format("%s should be updated to use 'prosody.net.http.files' instead of mod_http_files", get_calling_module())); end -function wrap_route(routes) - module:log("debug", "%s should be updated to use 'prosody.net.http.files' instead of mod_http_files", get_calling_module()); - for route,handler in pairs(routes) do - if type(handler) ~= "function" then - routes[route] = fileserver.serve(handler); - end - end - return routes; +function wrap_route() + error(string.format("%s should be updated to use 'prosody.net.http.files' instead of mod_http_files", get_calling_module())); end module:provides("http", {
--- a/plugins/mod_invites.lua Sun Jul 27 01:08:14 2025 +0200 +++ b/plugins/mod_invites.lua Sun Jul 27 01:11:58 2025 +0200 @@ -260,7 +260,7 @@ flags = { array_params = { role = true, group = have_group_invites }; value_params = { expires_after = true }; - kv_params = { admin = true }; + bool_params = { admin = true }; }; handler = function (self, user_jid, opts) --luacheck: ignore 212/self @@ -326,7 +326,7 @@ host_selector = "user_jid"; flags = { value_params = { expires_after = true }; - kv_params = { allow_registration = true }; + bool_params = { allow_registration = true }; }; handler = function (self, user_jid, opts) --luacheck: ignore 212/self
--- a/plugins/mod_s2s_auth_dane_in.lua Sun Jul 27 01:08:14 2025 +0200 +++ b/plugins/mod_s2s_auth_dane_in.lua Sun Jul 27 01:11:58 2025 +0200 @@ -24,11 +24,6 @@ return r; end -local function ensure_nonempty(r) - assert(r[1], "empty"); - return r; -end - local function flatten(a) local seen = {}; local ret = {}; @@ -95,7 +90,8 @@ return promise.all(tlsas):next(flatten); end - local ret = async.wait_for(resolver:lookup_promise("_xmpp-server." .. dns_domain, "TLSA"):next(ensure_secure):next(ensure_nonempty):catch(function() + local ret = async.wait_for(resolver:lookup_promise("_xmpp-server." .. dns_domain, "TLSA"):next(ensure_secure):next(function(ret) + if ret[1] then return ret; end return promise.all({ resolver:lookup_promise("_xmpps-server._tcp." .. dns_domain, "SRV"):next(ensure_secure):next(fetch_tlsa); resolver:lookup_promise("_xmpp-server._tcp." .. dns_domain, "SRV"):next(ensure_secure):next(fetch_tlsa);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/muc/gc3.lib.lua Sun Jul 27 01:11:58 2025 +0200 @@ -0,0 +1,57 @@ + +local jid_bare = require "prosody.util.jid".bare; + +local st = require "prosody.util.stanza"; + +local muc_util = module:require "muc/util"; +local valid_affiliations = muc_util.valid_affiliations; + +local xmlns_gc3 = "urn:xmpp:gc3:tmp" + +--luacheck: ignore 113/get_room_from_jid + +function fetch_participant_list(room, stanza, origin) + local from_jid = stanza.attr.from; + local from_aff = room:get_affiliation(from_jid); + local from_rank = valid_affiliations[from_aff or "none"]; + + local required_aff_rank = valid_affiliations[room:get_members_only() and "member" or "none"]; + if from_rank < required_aff_rank then + origin.send(st.error_reply(stanza, "auth", "forbidden")); + return true; + end + + local can_see_real_jids = not room:is_anonymous_for(from_jid); + + local reply = st.reply(stanza) + :tag("participants", { xmlns = xmlns_gc3 }); + + for jid, aff in room:each_affiliation() do + local nick = room:get_registered_nick(jid); + local visible_jid = can_see_real_jids and jid or nil; + reply:text_tag("item", nil, { + affiliation = aff; + jid = visible_jid; + nick = nick; + id = room:get_occupant_id_from_jid(jid); + }); + end + + origin.send(reply); + return true; +end + +local function room_event_handler(handler, allow_nonexistent) + return function (event) + local origin, stanza = event.origin, event.stanza; + local room_jid = jid_bare(stanza.attr.to); + local room = get_room_from_jid(room_jid); + if not room and allow_nonexistent ~= true then + origin.send(st.error_reply(stanza, "cancel", "item-not-found")); + return true; + end + return handler(room, stanza, origin); + end; +end + +module:hook("iq-get/bare/"..xmlns_gc3..":participants", room_event_handler(fetch_participant_list));
--- a/plugins/muc/mod_muc.lua Sun Jul 27 01:08:14 2025 +0200 +++ b/plugins/muc/mod_muc.lua Sun Jul 27 01:11:58 2025 +0200 @@ -100,6 +100,11 @@ local occupant_id = module:require "muc/occupant_id"; room_mt.get_salt = occupant_id.get_room_salt; room_mt.get_occupant_id = occupant_id.get_occupant_id; +room_mt.get_occupant_id_from_jid = occupant_id.get_occupant_id_from_jid; + +if module:get_option_boolean("muc_enable_experimental_gc3", false) then + module:require "muc/gc3"; +end local jid_split = require "prosody.util.jid".split; local jid_prep = require "prosody.util.jid".prep;
--- a/plugins/muc/muc.lib.lua Sun Jul 27 01:08:14 2025 +0200 +++ b/plugins/muc/muc.lib.lua Sun Jul 27 01:11:58 2025 +0200 @@ -24,6 +24,7 @@ local base64 = require "prosody.util.encodings".base64; local hmac_sha256 = require "prosody.util.hashes".hmac_sha256; local new_id = require "prosody.util.id".medium; +local time = require "prosody.util.time"; local log = module._log; @@ -133,11 +134,27 @@ return occupant end -function room_mt:route_to_occupant(occupant, stanza) +function room_mt:route_to_occupant(occupant, stanza, session_filter) local to = stanza.attr.to; - for jid in occupant:each_session() do - stanza.attr.to = jid; - self:route_stanza(stanza); + for session_jid, session_pres, session_meta in occupant:each_session() do + if session_filter then + stanza = session_filter(session_jid, session_pres, session_meta, stanza); + end + if stanza then + stanza.attr.to = session_jid; + self:route_stanza(stanza); + end + end + stanza.attr.to = to; +end + +function room_mt:route_presence_to_occupant(occupant, stanza) + local to = stanza.attr.to; + for jid, _, session_meta in occupant:each_session() do + if not session_meta.filter_presence then + stanza.attr.to = jid; + self:route_stanza(stanza); + end end stanza.attr.to = to; end @@ -297,7 +314,7 @@ end if recipient then - return self:route_to_occupant(recipient, get_p(recipient)); + return self:route_presence_to_occupant(recipient, get_p(recipient)); end local broadcast_roles = self:get_presence_broadcast(); @@ -305,11 +322,11 @@ for occupant_nick, n_occupant in self:each_occupant() do if occupant_nick ~= occupant.nick then if broadcast_roles[occupant.role or "none"] or force_unavailable then - self:route_to_occupant(n_occupant, get_p(n_occupant)); + self:route_presence_to_occupant(n_occupant, get_p(n_occupant)); elseif prev_role and broadcast_roles[prev_role] then local pr = get_p(n_occupant); pr.attr.type = 'unavailable'; - self:route_to_occupant(n_occupant, pr); + self:route_presence_to_occupant(n_occupant, pr); end end @@ -332,10 +349,14 @@ end end -function room_mt:send_occupant_list(to, filter) - local to_bare = jid_bare(to); +-- Send the current room list to an occupant (specifically to the real jid 'to', +-- which is one of the occupant's sessions). +function room_mt:send_occupant_list(to_occupant, to_jid, filter) + local to_session = to_occupant:get_session(to_jid); + if to_session.filter_presence then return; end + local to_bare = to_occupant.bare_jid; local broadcast_roles = self:get_presence_broadcast(); - local is_anonymous = self:is_anonymous_for(to); + local is_anonymous = self:is_anonymous_for(to_jid); local broadcast_bare_jids = {}; -- Track which bare JIDs we have sent presence for for occupant_jid, occupant in self:each_occupant() do broadcast_bare_jids[occupant.bare_jid] = true; @@ -343,7 +364,7 @@ local x = st.stanza("x", {xmlns='http://jabber.org/protocol/muc#user'}); self:build_item_list(occupant, x, is_anonymous and to_bare ~= occupant.bare_jid); -- can always see your own jids local pres = st.clone(occupant:get_presence()); - pres.attr.to = to; + pres.attr.to = to_jid; pres:add_child(x); module:fire_event("muc-build-occupant-presence", { room = self, occupant = occupant, stanza = pres }); self:route_stanza(pres); @@ -356,7 +377,7 @@ if (nick or not is_anonymous) and not broadcast_bare_jids[affiliated_jid] and (filter == nil or filter(affiliated_jid, nil)) then local from = nick and (self.jid.."/"..nick) or self.jid; - local pres = st.presence({ to = to, from = from, type = "unavailable" }) + local pres = st.presence({ to = to_jid, from = from, type = "unavailable" }) :tag("x", { xmlns = 'http://jabber.org/protocol/muc#user' }) :tag("item", { affiliation = affiliation; @@ -758,7 +779,7 @@ if orig_occupant == nil or muc_x then -- Send occupant list to newly joined or desynced user - self:send_occupant_list(real_jid, function(nick, occupant) -- luacheck: ignore 212 + self:send_occupant_list(dest_occupant, real_jid, function(nick, occupant) -- luacheck: ignore 212 -- Don't include self return (not occupant) or occupant:get_presence(real_jid) == nil; end) @@ -1437,6 +1458,15 @@ end end + data = data or {}; + local old_data = self._affiliation_data[jid]; + + local current_time = time.now(); + data.affiliation_created_at = old_data and old_data.affiliation_created_at or current_time; + data.affiliation_created_by = old_data and old_data.affiliation_created_by or actor; + data.affiliation_modified_at = current_time; + data.affiliation_modified_by = actor; + local event_data = { room = self; actor = actor; @@ -1444,7 +1474,7 @@ affiliation = affiliation or "none"; reason = reason; previous_affiliation = target_affiliation or "none"; - data = data and data or nil; -- coerce false to nil + data = data; previous_data = self._affiliation_data[jid] or nil; }; @@ -1453,10 +1483,6 @@ local err = event_data.error or { type = "cancel", condition = "not-allowed" }; return nil, err.type, err.condition; end - if affiliation and not data and event_data.data then - -- Allow handlers to add data when none was going to be set - data = event_data.data; - end -- Set in 'database' self._affiliations[jid] = affiliation; @@ -1514,7 +1540,7 @@ (old_role ~= "moderator" and occupant.role == "moderator")) then -- Has gained or lost moderator status -- Send everyone else's presences (as jid visibility has changed) for real_jid in occupant:each_session() do - self:send_occupant_list(real_jid, function(occupant_jid, occupant) --luacheck: ignore 212 433 + self:send_occupant_list(occupant, real_jid, function(occupant_jid, occupant) --luacheck: ignore 212 433 return (not occupant) or occupant.bare_jid ~= jid; end); end
--- a/plugins/muc/occupant.lib.lua Sun Jul 27 01:08:14 2025 +0200 +++ b/plugins/muc/occupant.lib.lua Sun Jul 27 01:11:58 2025 +0200 @@ -23,10 +23,11 @@ -- Deep copy an occupant local function copy_occupant(occupant) local sessions = {}; - for full_jid, presence_stanza in pairs(occupant.sessions) do + for full_jid, session in pairs(occupant.sessions) do + local presence_stanza = session.presence; -- Don't keep unavailable presences, as they'll accumulate; unless they're the primary session if presence_stanza.attr.type ~= "unavailable" or full_jid == occupant.jid then - sessions[full_jid] = presence_stanza; + sessions[full_jid] = session; end end return setmetatable({ @@ -48,12 +49,16 @@ return nil; end +function occupant_mt:get_session(real_jid) + return self.sessions[real_jid]; +end + function occupant_mt:set_session(real_jid, presence_stanza, replace_primary) local pr = get_filtered_presence(presence_stanza); pr.attr.from = self.nick; pr.attr.to = real_jid; - self.sessions[real_jid] = pr; + self.sessions[real_jid] = { presence = pr }; if replace_primary then self.jid = real_jid; elseif self.jid == nil or (pr.attr.type == "unavailable" and self.jid == real_jid) then @@ -70,12 +75,23 @@ end end +-- An iterator over self.sessions that returns (jid, presence, session) +local function _session_iter(s, var) + local k = next(s, var); + if k then + local v = s[k]; + return k, v.presence, v; + end +end + function occupant_mt:each_session() - return pairs(self.sessions) + return _session_iter, self.sessions; end function occupant_mt:get_presence(real_jid) - return self.sessions[real_jid or self.jid] + local session = self.sessions[real_jid or self.jid]; + if not session then return; end + return session.presence; end return {
--- a/plugins/muc/occupant_id.lib.lua Sun Jul 27 01:08:14 2025 +0200 +++ b/plugins/muc/occupant_id.lib.lua Sun Jul 27 01:11:58 2025 +0200 @@ -31,6 +31,11 @@ return occupant.stable_id; end +local function get_occupant_id_from_jid(room, real_bare_jid) + local salt = get_room_salt(room) + return b64encode(hmac_sha256(real_bare_jid, salt)); +end + local function update_occupant(event) local stanza, room, occupant, dest_occupant = event.stanza, event.room, event.occupant, event.dest_occupant; @@ -73,4 +78,5 @@ return { get_room_salt = get_room_salt; get_occupant_id = get_occupant_id; + get_occupant_id_from_jid = get_occupant_id_from_jid; };
--- a/plugins/muc/register.lib.lua Sun Jul 27 01:08:14 2025 +0200 +++ b/plugins/muc/register.lib.lua Sun Jul 27 01:11:58 2025 +0200 @@ -4,9 +4,10 @@ local st = require "prosody.util.stanza"; local dataforms = require "prosody.util.dataforms"; -local allow_unaffiliated = module:get_option_boolean("allow_unaffiliated_register", false); +local gc3_mode = module:get_option_boolean("muc_enable_experimental_gc3", false); -local enforce_nick = module:get_option_boolean("enforce_registered_nickname", false); +local allow_unaffiliated = module:get_option_boolean("allow_unaffiliated_register", gc3_mode); +local enforce_nick = module:get_option_boolean("enforce_registered_nickname", gc3_mode); -- Whether to include the current registration data as a dataform. Disabled -- by default currently as it hasn't been widely tested with clients. @@ -125,9 +126,16 @@ if affiliation == "outcast" then origin.send(st.error_reply(stanza, "auth", "forbidden")); return true; - elseif not (affiliation or allow_unaffiliated) then - origin.send(st.error_reply(stanza, "auth", "registration-required")); - return true; + elseif not affiliation then + if room:get_members_only() then + -- Never allow unaffiliated users to register their way into a members-only room + origin.send(st.error_reply(stanza, "auth", "forbidden")); + return true; + elseif not allow_unaffiliated then + -- Not members-only, but we're not permitting registration due to config + origin.send(st.error_reply(stanza, "auth", "registration-required")); + return true; + end end local reply = st.reply(stanza); local registered_nick = get_registered_nick(room, user_jid);
--- a/spec/util_argparse_spec.lua Sun Jul 27 01:08:14 2025 +0200 +++ b/spec/util_argparse_spec.lua Sun Jul 27 01:11:58 2025 +0200 @@ -42,14 +42,14 @@ do -- Same test with strict mode enabled and all parameters declared - local opts, err = parse({ "--foo"; "-b" }, { kv_params = { foo = true, bar = true }; short_params = { b = "bar" }, strict = true }); + local opts, err = parse({ "--foo"; "-b" }, { bool_params = { foo = true, bar = true }; short_params = { b = "bar" }, strict = true }); assert.falsy(err); assert.same({ foo = true; bar = true }, opts); end end); it("supports value arguments", function() - local opts, err = parse({ "--foo"; "bar"; "--baz=moo" }, { value_params = { foo = true; bar = true } }); + local opts, err = parse({ "--foo"; "bar"; "--baz=moo" }, { value_params = { foo = true; bar = true, baz = true } }); assert.falsy(err); assert.same({ foo = "bar"; baz = "moo" }, opts); end); @@ -96,9 +96,9 @@ assert.same("--foobar", err2); end); - it("accepts known kv parameters in strict mode", function () - local opts, err = parse({ "--item=foo" }, { kv_params = { item = true }, strict = true }); + it("accepts known bool parameters in strict mode", function () + local opts, err = parse({ "--item" }, { bool_params = { item = true }, strict = true }); assert.falsy(err); - assert.same("foo", opts.item); + assert.same(true, opts.item); end); end);
--- a/teal-src/prosody/plugins/mod_cron.tl Sun Jul 27 01:08:14 2025 +0200 +++ b/teal-src/prosody/plugins/mod_cron.tl Sun Jul 27 01:11:58 2025 +0200 @@ -36,6 +36,10 @@ local active_hosts : { string : boolean } = { } +if prosody.process_type == "prosodyctl" then + return -- Yes, it happens... +end + function module.add_host(host_module : moduleapi) local last_run_times = host_module:open_store("cron", "map") as map_store<string,integer>; @@ -63,13 +67,13 @@ task.restore = restore_task; task.save = save_task; module:log("debug", "%s task %s added", task.when, task.id); - return true; + return true end local function task_removed(event : task_event) : boolean local task = event.item; host_module:log("debug", "Task %s removed", task.id); - return true; + return true end host_module:handle_items("task", task_added, task_removed, true); @@ -80,13 +84,13 @@ end local function should_run(task : task_spec, last : integer) : boolean - return not last or last + task.period * 0.995 <= os.time(); + return not last or last + task.period * 0.995 <= os.time() end local function run_task(task : task_spec) task:restore(); if not should_run(task, task.last) then - return; + return end local started_at = os.time(); task:run(started_at); @@ -95,7 +99,7 @@ end local function spread(t : number, factor : number) : number - return t * (1 - factor + 2*factor*math.random()); + return t * (1 - factor + 2*factor*math.random()) end local task_runner : async.runner_t<task_spec> = async.runner(run_task); @@ -109,7 +113,7 @@ end end module:log("debug", "Wait %gs", delay); - return delay; + return delay end); -- TODO measure load, pick a good time to do stuff @@ -122,7 +126,7 @@ args = {}; handler = function (self, filter_host : string) local format_table = require "prosody.util.human.io".table; - local it = require "util.iterators"; + local it = require "prosody.util.iterators"; local row = format_table({ { title = "Host", width = "2p" }; { title = "Task", width = "3p" };
--- a/teal-src/prosody/util/array.d.tl Sun Jul 27 01:08:14 2025 +0200 +++ b/teal-src/prosody/util/array.d.tl Sun Jul 27 01:11:58 2025 +0200 @@ -1,9 +1,10 @@ -local record array_t<T> - { T } +local record array_t<T> is { T } + -- TODO methods end local record lib metamethod __call : function () : array_t + -- TODO library functions end return lib
--- a/teal-src/prosody/util/crypto.d.tl Sun Jul 27 01:08:14 2025 +0200 +++ b/teal-src/prosody/util/crypto.d.tl Sun Jul 27 01:11:58 2025 +0200 @@ -2,7 +2,9 @@ record key private_pem : function (key) : string public_pem : function (key) : string + public_raw : function (key) : string get_type : function (key) : string + derive : function (key, key) : string end type base_evp_sign = function (key, message : string) : string @@ -44,9 +46,11 @@ aes_256_ctr_decrypt : Levp_decrypt generate_ed25519_keypair : function () : key + generate_p256_keypair : function () : key import_private_pem : function (string) : key import_public_pem : function (string) : key + import_public_ec_raw : function (string, string) : key parse_ecdsa_signature : function (string, integer) : string, string build_ecdsa_signature : function (r : string, s : string) : string
--- a/teal-src/prosody/util/dataforms.d.tl Sun Jul 27 01:08:14 2025 +0200 +++ b/teal-src/prosody/util/dataforms.d.tl Sun Jul 27 01:11:58 2025 +0200 @@ -5,7 +5,7 @@ title : string instructions : string - record form_field + record form_field is { form_field } enum field_type "boolean" @@ -35,7 +35,6 @@ options : table end - { form_field } enum form_type "form"
--- a/teal-src/prosody/util/datamapper.tl Sun Jul 27 01:08:14 2025 +0200 +++ b/teal-src/prosody/util/datamapper.tl Sun Jul 27 01:11:58 2025 +0200 @@ -25,7 +25,7 @@ local json = require "prosody.util.json" local pointer = require "prosody.util.jsonpointer"; -local json_type_name = json.json_type_name; +local type json_type_name = json.json_type_name; local json_schema_object = require "prosody.util.jsonschema" local type schema_t = boolean | json_schema_object @@ -42,7 +42,7 @@ local function totype(t : json_type_name, s : string) : any if not s then return nil end if t == "string" then - return s; + return s elseif t == "boolean" then return toboolean(s) elseif t == "number" or t == "integer" then @@ -63,10 +63,10 @@ local function resolve_schema(schema : schema_t, root : json_schema_object) : schema_t if schema is json_schema_object then if schema["$ref"] and schema["$ref"]:sub(1, 1) == "#" then - return pointer.resolve(root as table, schema["$ref"]:sub(2)) as schema_t; + return pointer.resolve(root as table, schema["$ref"]:sub(2)) as schema_t end end - return schema; + return schema end local function guess_schema_type(schema : json_schema_object) : json_type_name @@ -95,8 +95,6 @@ if propschema is json_schema_object then proptype = guess_schema_type(propschema); - elseif propschema is string then -- Teal says this can never be a string, but it could before so best be sure - error("schema as string is not supported: "..propschema.." {"..current_ns.."}"..propname) end if proptype == "object" or proptype == "array" then @@ -158,7 +156,7 @@ c = s:get_child(nil, namespace); end if c then - return c.name; + return c.name end elseif value_where == "in_attribute" then local attr = name @@ -250,7 +248,7 @@ table.insert(out, totype(proptype, value)); end end - return out; + return out end local function parse (schema : json_schema_object, s : st.stanza_t) : table @@ -269,7 +267,7 @@ return v elseif proptype == "number" and v is number then return string.format("%g", v) - elseif proptype == "integer" and v is number then -- TODO is integer + elseif proptype == "integer" and v is integer then return string.format("%d", v) elseif proptype == "boolean" then return v and "1" or "0" @@ -361,7 +359,7 @@ unparse_property(out, v, proptype, propschema, value_where, name, namespace, current_ns, prefix, single_attribute, root) end end - return out; + return out elseif s_type == "array" then local itemschema = resolve_schema(schema.items, root) @@ -369,7 +367,7 @@ for _, item in ipairs(t as { string }) do unparse_property(out, item, proptype, itemschema, value_where, name, namespace, current_ns, prefix, single_attribute, root) end - return out; + return out end end
--- a/teal-src/prosody/util/jsonschema.tl Sun Jul 27 01:08:14 2025 +0200 +++ b/teal-src/prosody/util/jsonschema.tl Sun Jul 27 01:11:58 2025 +0200 @@ -10,12 +10,8 @@ if not math.type then require "prosody.util.mathcompat" end - -local utf8_enc = rawget(_G, "utf8") or require"prosody.util.encodings".utf8; -local utf8_len = utf8_enc.len or function(s : string) : integer - local _, count = s:gsub("[%z\001-\127\194-\253][\128-\191]*", ""); - return count; -end; +-- XXX util.encodings seems to count differently from the Lua builtin +local utf8_len = rawget(_G, "utf8") and utf8.len or require"prosody.util.encodings".utf8.length; local json = require "prosody.util.json" local null = json.null; @@ -166,7 +162,7 @@ end local type errors = { validation_error } local function mkerr(sloc:string,iloc:string,err:string) : validation_error - return { schemaLocation = sloc; instanceLocation = iloc; error = err }; + return { schemaLocation = sloc; instanceLocation = iloc; error = err } end local function validate (schema : schema_t, data : any, root : json_schema_object, sloc : string, iloc : string, errs:errors) : boolean, errors @@ -186,14 +182,14 @@ if referenced ~= nil and referenced ~= root and referenced ~= schema then if not validate(referenced, data, root, schema["$ref"], iloc, errs) then table.insert(errs, mkerr(sloc.."/$ref", iloc, "Subschema failed validation")) - return false, errs; + return false, errs end end end if not simple_validate(schema.type, data) then table.insert(errs, mkerr(sloc.."/type", iloc, "unexpected type")); - return false, errs; + return false, errs end if schema.type == "object" then @@ -202,7 +198,7 @@ for k in pairs(data) do if not k is string then table.insert(errs, mkerr(sloc.."/type", iloc, "'object' had non-string keys")); - return false, errs; + return false, errs end end end @@ -214,7 +210,7 @@ for i in pairs(data) do if not i is integer then table.insert(errs, mkerr(sloc.."/type", iloc, "'array' had non-integer keys")); - return false, errs; + return false, errs end end end @@ -231,7 +227,7 @@ end if not match then table.insert(errs, mkerr(sloc.."/enum", iloc, "not one of the enumerated values")); - return false, errs; + return false, errs end end @@ -240,42 +236,42 @@ if data is string then if schema.maxLength and utf8_len(data) > schema.maxLength then table.insert(errs, mkerr(sloc.."/maxLength", iloc, "string too long")) - return false, errs; + return false, errs end if schema.minLength and utf8_len(data) < schema.minLength then table.insert(errs, mkerr(sloc.."/maxLength", iloc, "string too short")) - return false, errs; + return false, errs end if schema.luaPattern and not data:match(schema.luaPattern) then table.insert(errs, mkerr(sloc.."/luaPattern", iloc, "string does not match pattern")) - return false, errs; + return false, errs end end if data is number then if schema.multipleOf and (data == 0 or data % schema.multipleOf ~= 0) then table.insert(errs, mkerr(sloc.."/luaPattern", iloc, "not a multiple")) - return false, errs; + return false, errs end if schema.maximum and not ( data <= schema.maximum ) then table.insert(errs, mkerr(sloc.."/maximum", iloc, "number exceeds maximum")) - return false, errs; + return false, errs end if schema.exclusiveMaximum and not ( data < schema.exclusiveMaximum ) then table.insert(errs, mkerr(sloc.."/exclusiveMaximum", iloc, "number exceeds exclusive maximum")) - return false, errs; + return false, errs end if schema.minimum and not ( data >= schema.minimum ) then table.insert(errs, mkerr(sloc.."/minimum", iloc, "number below minimum")) - return false, errs; + return false, errs end if schema.exclusiveMinimum and not ( data > schema.exclusiveMinimum ) then table.insert(errs, mkerr(sloc.."/exclusiveMinimum", iloc, "number below exclusive minimum")) - return false, errs; + return false, errs end end @@ -283,7 +279,7 @@ for i, sub in ipairs(schema.allOf) do if not validate(sub, data, root, sloc.."/allOf/"..i, iloc, errs) then table.insert(errs, mkerr(sloc.."/allOf", iloc, "did not match all subschemas")) - return false, errs; + return false, errs end end end @@ -297,7 +293,7 @@ end if valid ~= 1 then table.insert(errs, mkerr(sloc.."/oneOf", iloc, "did not match exactly one subschema")) - return false, errs; + return false, errs end end @@ -311,14 +307,14 @@ end if not match then table.insert(errs, mkerr(sloc.."/anyOf", iloc, "did not match any subschema")) - return false, errs; + return false, errs end end if schema["not"] ~= nil then if validate(schema["not"], data, root, sloc.."/not", iloc, errs) then table.insert(errs, mkerr(sloc.."/not", iloc, "did match subschema")) - return false, errs; + return false, errs end end @@ -327,14 +323,14 @@ if schema["then"] ~= nil then if not validate(schema["then"], data, root, sloc.."/then", iloc, errs) then table.insert(errs, mkerr(sloc.."/then", iloc, "did not match subschema")) - return false, errs; + return false, errs end end else if schema["else"] ~= nil then if not validate(schema["else"], data, root, sloc.."/else", iloc, errs) then table.insert(errs, mkerr(sloc.."/else", iloc, "did not match subschema")) - return false, errs; + return false, errs end end end @@ -342,7 +338,7 @@ if schema.const ~= nil and schema.const ~= data then table.insert(errs, mkerr(sloc.."/const", iloc, "did not match constant value")) - return false, errs; + return false, errs end if data is table then @@ -352,19 +348,19 @@ if schema.maxItems and #(data as {any}) > schema.maxItems then table.insert(errs, mkerr(sloc.."/maxItems", iloc, "too many items")) - return false, errs; + return false, errs end if schema.minItems and #(data as {any}) < schema.minItems then table.insert(errs, mkerr(sloc.."/minItems", iloc, "too few items")) - return false, errs; + return false, errs end if schema.required then for _, k in ipairs(schema.required) do if data[k] == nil then table.insert(errs, mkerr(sloc.."/required", iloc.."/"..tostring(k), "missing required property")) - return false, errs; + return false, errs end end end @@ -375,7 +371,7 @@ for _, req in ipairs(reqs) do if data[req] == nil then table.insert(errs, mkerr(sloc.."/dependentRequired", iloc, "missing dependent required property")) - return false, errs; + return false, errs end end end @@ -387,7 +383,7 @@ for k in pairs(data) do if not validate(schema.propertyNames, k, root, sloc.."/propertyNames", iloc.."/"..tostring(k), errs) then table.insert(errs, mkerr(sloc.."/propertyNames", iloc.."/"..tostring(k), "a property name did not match subschema")) - return false, errs; + return false, errs end end end @@ -401,7 +397,7 @@ for k, sub in pairs(schema.properties) do if data[k] ~= nil and not validate(sub, data[k], root, sloc.."/"..tostring(k), iloc.."/"..tostring(k), errs) then table.insert(errs, mkerr(sloc.."/"..tostring(k), iloc.."/"..tostring(k), "a property did not match subschema")) - return false, errs; + return false, errs end seen_properties[k] = true end @@ -414,7 +410,7 @@ if k is string and k:match(pattern) then if not validate(sub, data[k], root, sloc.."/luaPatternProperties", iloc, errs) then table.insert(errs, mkerr(sloc.."/luaPatternProperties/"..pattern, iloc.."/"..tostring(k), "a property did not match subschema")) - return false, errs; + return false, errs end seen_properties[k] = true end @@ -427,7 +423,7 @@ if not seen_properties[k as string] then if not validate(schema.additionalProperties, v, root, sloc.."/additionalProperties", iloc.."/"..tostring(k), errs) then table.insert(errs, mkerr(sloc.."/additionalProperties", iloc.."/"..tostring(k), "additional property did not match subschema")) - return false, errs; + return false, errs end end end @@ -437,7 +433,7 @@ for k, sub in pairs(schema.dependentSchemas) do if data[k] ~= nil and not validate(sub, data, root, sloc.."/dependentSchemas/"..k, iloc, errs) then table.insert(errs, mkerr(sloc.."/dependentSchemas", iloc.."/"..tostring(k), "did not match dependent subschema")) - return false, errs; + return false, errs end end end @@ -448,7 +444,7 @@ for _, v in pairs(data) do if values[v] then table.insert(errs, mkerr(sloc.."/uniqueItems", iloc, "had duplicate items")) - return false, errs; + return false, errs end values[v] = true end @@ -463,7 +459,7 @@ p = i else table.insert(errs, mkerr(sloc.."/prefixItems/"..i, iloc.."/"..tostring(i), "did not match subschema")) - return false, errs; + return false, errs end end end @@ -472,7 +468,7 @@ for i = p+1, #(data as {any}) do if not validate(schema.items, data[i], root, sloc, iloc.."/"..i, errs) then table.insert(errs, mkerr(sloc.."/prefixItems/"..i, iloc.."/"..i, "did not match subschema")) - return false, errs; + return false, errs end end end @@ -488,18 +484,18 @@ end if found < (schema.minContains or 1) then table.insert(errs, mkerr(sloc.."/minContains", iloc, "too few matches")) - return false, errs; + return false, errs elseif found > (schema.maxContains or math.huge) then table.insert(errs, mkerr(sloc.."/maxContains", iloc, "too many matches")) - return false, errs; + return false, errs end end end - return true; + return true end json_schema_object.validate = validate; -return json_schema_object; +return json_schema_object
--- a/teal-src/prosody/util/pposix.d.tl Sun Jul 27 01:08:14 2025 +0200 +++ b/teal-src/prosody/util/pposix.d.tl Sun Jul 27 01:11:58 2025 +0200 @@ -86,6 +86,14 @@ mkdir : function (dir : string) : boolean, string + enum pipe_flag_names + "cloexec" + "direct" + "nonblock" + end + pipe : function (... : pipe_flag_names) : integer, integer + fdopen : function (integer, string) : FILE, string + setrlimit : function (resource : ulimit_resource, soft : ulimit_limit, hard : ulimit_limit) : boolean, string getrlimit : function (resource : ulimit_resource) : boolean, ulimit_limit, ulimit_limit getrlimit : function (resource : ulimit_resource) : boolean, string @@ -103,7 +111,7 @@ ENOENT : integer _NAME : string - _VESRION : string + _VERSION : string end return pposix
--- a/teal-src/prosody/util/queue.d.tl Sun Jul 27 01:08:14 2025 +0200 +++ b/teal-src/prosody/util/queue.d.tl Sun Jul 27 01:11:58 2025 +0200 @@ -18,4 +18,4 @@ new : function<T> (size:integer, allow_wrapping:boolean) : queue<T> end -return lib; +return lib
--- a/teal-src/prosody/util/set.d.tl Sun Jul 27 01:08:14 2025 +0200 +++ b/teal-src/prosody/util/set.d.tl Sun Jul 27 01:11:58 2025 +0200 @@ -1,22 +1,22 @@ local record lib record Set<T> - add : function<T> (Set<T>, T) - contains : function<T> (Set<T>, T) : boolean - contains_set : function<T> (Set<T>, Set<T>) : boolean - items : function<T> (Set<T>) : function<T> (Set<T>, T) : T - remove : function<T> (Set<T>, T) - add_list : function<T> (Set<T>, { T }) - include : function<T> (Set<T>, Set<T>) - exclude : function<T> (Set<T>, Set<T>) - empty : function<T> (Set<T>) : boolean + add : function (Set, T) + contains : function (Set, T) : boolean + contains_set : function (Set, Set) : boolean + items : function (Set) : function (Set, T) : T + remove : function (Set, T) + add_list : function (Set, { T }) + include : function (Set, Set) + exclude : function (Set, Set) + empty : function (Set) : boolean end new : function<T> ({ T }) : Set<T> is_set : function (any) : boolean - union : function<T> (Set<T>, Set<T>) : Set <T> - difference : function<T> (Set<T>, Set<T>) : Set <T> - intersection : function<T> (Set<T>, Set<T>) : Set <T> - xor : function<T> (Set<T>, Set<T>) : Set <T> + union : function (Set, Set) : Set + difference : function (Set, Set) : Set + intersection : function (Set, Set) : Set + xor : function (Set, Set) : Set end return lib
--- a/teal-src/prosody/util/signal.d.tl Sun Jul 27 01:08:14 2025 +0200 +++ b/teal-src/prosody/util/signal.d.tl Sun Jul 27 01:11:58 2025 +0200 @@ -36,6 +36,7 @@ signal : function (integer | Signal, function, boolean) : boolean raise : function (integer | Signal) kill : function (integer, integer | Signal) + signalfd : function (integer) : FILE -- enum : integer end return lib
--- a/teal-src/prosody/util/smqueue.tl Sun Jul 27 01:08:14 2025 +0200 +++ b/teal-src/prosody/util/smqueue.tl Sun Jul 27 01:11:58 2025 +0200 @@ -12,12 +12,11 @@ "head" "pop" end - push : function (smqueue, T) - ack : function (smqueue, integer) : { T }, ack_errors + push : function (smqueue<T>, T) + ack : function (smqueue<T>, integer) : { T }, ack_errors resumable : function (smqueue<T>) : boolean resume : function (smqueue<T>) : queue.queue.iterator, any, integer - type consume_iter = function (smqueue<T>) : T - consume : function (smqueue<T>) : consume_iter + consume : function (smqueue<T>) : function() : T table : function (smqueue<T>) : { T } end @@ -26,17 +25,17 @@ local type smqueue = lib.smqueue; -function smqueue:push(v) +function smqueue:push(v : T) self._head = self._head + 1; -- Wraps instead of errors assert(self._queue:push(v)); end -function smqueue:ack(h : integer) : { any }, smqueue.ack_errors +function smqueue:ack(h : integer) : { T }, smqueue.ack_errors if h < self._tail then - return nil, "tail"; + return nil, "tail" elseif h > self._head then - return nil, "head"; + return nil, "head" end -- TODO optimize? cache table fields local acked = {}; @@ -44,39 +43,41 @@ local expect = self._head - self._tail; while expect < self._queue:count() do local v = self._queue:pop(); - if not v then return nil, "pop"; end + if not v then + return nil, "pop" + end table.insert(acked, v); end - return acked; + return acked end function smqueue:count_unacked() : integer - return self._head - self._tail; + return self._head - self._tail end function smqueue:count_acked() : integer - return self._tail; + return self._tail end function smqueue:resumable() : boolean - return self._queue:count() >= (self._head - self._tail); + return self._queue:count() >= (self._head - self._tail) end function smqueue:resume() : queue.queue.iterator, any, integer - return self._queue:items(); + return self._queue:items() end -function smqueue:consume() : queue.queue.consume_iter - return self._queue:consume() +function smqueue:consume() : (function() : T) + return self._queue:consume() as (function() : T) end -- Compatibility layer, plain ol' table -function smqueue:table() : { any } - local t : { any } = {}; +function smqueue:table() : { T } + local t : { T } = {}; for i, v in self:resume() do t[i] = v; end - return t; + return t end local function freeze(q : smqueue<any>) : { string:integer } @@ -91,9 +92,9 @@ __freeze = freeze; } -function lib.new<T>(size : integer) : queue.queue<T> +function lib.new<T>(size : integer) : smqueue<T> assert(size>0); - return setmetatable({ _head = 0; _tail = 0; _queue = queue.new(size, true) }, queue_mt); + return setmetatable({ _head = 0; _tail = 0; _queue = queue.new(size, true) }, queue_mt) end -return lib; +return lib
--- a/teal-src/prosody/util/stanza.d.tl Sun Jul 27 01:08:14 2025 +0200 +++ b/teal-src/prosody/util/stanza.d.tl Sun Jul 27 01:11:58 2025 +0200 @@ -37,10 +37,9 @@ "unexpected-request" end - record stanza_t + record stanza_t is { stanza_t | string } name : string attr : { string : string } - { stanza_t | string } tags : { stanza_t } query : function ( stanza_t, string ) : stanza_t @@ -77,10 +76,9 @@ indent : function ( stanza_t, integer, string ) : stanza_t end - record serialized_stanza_t + record serialized_stanza_t is { serialized_stanza_t | string } name : string attr : { string : string } - { serialized_stanza_t | string } end record message_attr
--- a/teal-src/prosody/util/xtemplate.tl Sun Jul 27 01:08:14 2025 +0200 +++ b/teal-src/prosody/util/xtemplate.tl Sun Jul 27 01:11:58 2025 +0200 @@ -56,7 +56,7 @@ if func == "each" and tmpl then if not st.is_stanza(value) then - return pre_blank..post_blank; + return pre_blank..post_blank end if not args then value, args = root, path; end local ns, name = s_match(args, "^(%b{})(.*)$"); @@ -94,15 +94,15 @@ if value is string then if not is_escaped then value = escape(value); end - return pre_blank .. value .. post_blank; + return pre_blank .. (value as string) .. post_blank elseif st.is_stanza(value) then value = value:get_text(); if value then - return pre_blank .. escape(value) .. post_blank; + return pre_blank .. escape(value) .. post_blank end end - return pre_blank .. post_blank; - end)); + return pre_blank .. post_blank + end)) end -return { render = render }; +return { render = render }
--- a/util/argparse.lua Sun Jul 27 01:08:14 2025 +0200 +++ b/util/argparse.lua Sun Jul 27 01:11:58 2025 +0200 @@ -2,7 +2,7 @@ local short_params = config and config.short_params or {}; local value_params = config and config.value_params or {}; local array_params = config and config.array_params or {}; - local kv_params = config and config.kv_params or {}; + local bool_params = config and (config.bool_params or config.kv_params) or {}; -- COMPAT: kv_params in 13.0 and earlier local strict = config and config.strict; local stop_on_positional = not config or config.stop_on_positional ~= false; @@ -48,16 +48,13 @@ end 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 + if param:match("^no%-") then + param_k, param_v = param:sub(4), false; + else + param_k, param_v = param, true; end param_k = param_k:gsub("%-", "_"); - if strict and not kv_params[param_k] then + if strict and not bool_params[param_k] then return nil, "param-not-found", raw_param; end end
--- a/util/datamapper.lua Sun Jul 27 01:08:14 2025 +0200 +++ b/util/datamapper.lua Sun Jul 27 01:11:58 2025 +0200 @@ -1,13 +1,13 @@ --- This file is generated from teal-src/util/datamapper.lua - +-- This file is generated from teal-src/prosody/util/datamapper.tl if not math.type then require("prosody.util.mathcompat") end local st = require("prosody.util.stanza"); +local json = require("prosody.util.json") local pointer = require("prosody.util.jsonpointer"); -local schema_t = {} +local json_schema_object = require("prosody.util.jsonschema") local function toboolean(s) if s == "true" or s == "1" then @@ -32,8 +32,6 @@ end end -local value_goes = {} - local function resolve_schema(schema, root) if type(schema) == "table" then if schema["$ref"] and schema["$ref"]:sub(1, 1) == "#" then @@ -69,8 +67,6 @@ if type(propschema) == "table" then proptype = guess_schema_type(propschema); - elseif type(propschema) == "string" then - error("schema as string is not supported: " .. propschema .. " {" .. current_ns .. "}" .. propname) end if proptype == "object" or proptype == "array" then @@ -103,7 +99,7 @@ end end if propschema["const"] then - enums = {propschema["const"]} + enums = { propschema["const"] } elseif propschema["enum"] then enums = propschema["enum"] end @@ -137,7 +133,7 @@ elseif value_where == "in_attribute" then local attr = name if prefix then - attr = prefix .. ":" .. name + attr = prefix .. ':' .. name elseif namespace and namespace ~= s.attr.xmlns then attr = namespace .. "\1" .. name end @@ -243,7 +239,7 @@ return v elseif proptype == "number" and type(v) == "number" then return string.format("%g", v) - elseif proptype == "integer" and type(v) == "number" then + elseif proptype == "integer" and math.type(v) == "integer" then return string.format("%d", v) elseif proptype == "boolean" then return v and "1" or "0" @@ -252,13 +248,12 @@ local unparse -local function unparse_property(out, v, proptype, propschema, value_where, name, namespace, current_ns, prefix, - single_attribute, root) +local function unparse_property(out, v, proptype, propschema, value_where, name, namespace, current_ns, prefix, single_attribute, root) if value_where == "in_attribute" then local attr = name if prefix then - attr = prefix .. ":" .. name + attr = prefix .. ':' .. name elseif namespace and namespace ~= current_ns then attr = namespace .. "\1" .. name end @@ -280,7 +275,7 @@ else local propattr if namespace ~= current_ns then - propattr = {xmlns = namespace} + propattr = { xmlns = namespace } end if value_where == "in_tag_name" then if proptype == "string" and type(v) == "string" then @@ -324,7 +319,7 @@ end - local out = ctx or st.stanza(current_name, {xmlns = current_ns}) + local out = ctx or st.stanza(current_name, { xmlns = current_ns }) local s_type = guess_schema_type(schema) if s_type == "object" then @@ -350,4 +345,4 @@ end end -return {parse = parse; unparse = unparse} +return { parse = parse; unparse = unparse }
--- a/util/jsonpointer.lua Sun Jul 27 01:08:14 2025 +0200 +++ b/util/jsonpointer.lua Sun Jul 27 01:11:58 2025 +0200 @@ -1,5 +1,4 @@ -local m_type = math.type; - +-- This file is generated from teal-src/prosody/util/jsonpointer.tl local function unescape_token(escaped_token) local unescaped = escaped_token:gsub("~1", "/"):gsub("~0", "~") return unescaped @@ -17,10 +16,10 @@ if type(idx) == "string" then new_ref = ref[token] - elseif m_type(idx) == "integer" then + elseif math.type(idx) == "integer" then local i = tonumber(token) if token == "-" then - i = #ref + 1 + i = #(ref) + 1 end new_ref = ref[i + 1] else
--- a/util/jsonschema.lua Sun Jul 27 01:08:14 2025 +0200 +++ b/util/jsonschema.lua Sun Jul 27 01:11:58 2025 +0200 @@ -1,14 +1,9 @@ --- This file is generated from teal-src/util/jsonschema.lua - +-- This file is generated from teal-src/prosody/util/jsonschema.tl if not math.type then require("prosody.util.mathcompat") end -local utf8_enc = rawget(_G, "utf8") or require("prosody.util.encodings").utf8; -local utf8_len = utf8_enc.len or function(s) - local _, count = s:gsub("[%z\001-\127\194-\253][\128-\191]*", ""); - return count -end; +local utf8_len = rawget(_G, "utf8") and utf8.len or require("prosody.util.encodings").utf8.length; local json = require("prosody.util.json") local null = json.null;
