From 396e4d0cc2c5d824c09fd21316fabe755265c6b0 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 28 Mar 2019 12:52:55 +0100 Subject: net.server_epoll: Skip delayed continuation read on paused connections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should prevent #1333 in cases where LuaSockets buffer is "dirty", i.e. contains more data after a read, where it gets resumed with a short delay. --- net/server_epoll.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/server_epoll.lua b/net/server_epoll.lua index cffd3a84..c41266e1 100644 --- a/net/server_epoll.lua +++ b/net/server_epoll.lua @@ -373,7 +373,7 @@ function interface:onreadable() end end if not self.conn then return; end - if self.conn:dirty() then + if self._wantread and self.conn:dirty() then self:setreadtimeout(false); self:pausefor(cfg.read_retry_delay); else -- cgit v1.2.3 From 6b6dd549e8573d87e431d02237fe9272a09bf2a3 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 13 Apr 2019 18:16:28 +0200 Subject: net.dns: Close resolv.conf handle when done (fixes #1342) --- net/dns.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/net/dns.lua b/net/dns.lua index af5f1216..3902f95c 100644 --- a/net/dns.lua +++ b/net/dns.lua @@ -704,6 +704,7 @@ function resolver:adddefaultnameservers() -- - - - - adddefaultnameservers end end end + resolv_conf:close(); end if not self.server or #self.server == 0 then -- TODO log warning about no nameservers, adding localhost as the default nameserver -- cgit v1.2.3 From 6e9f3cf6f3a2a0852dbf919b8a77245a38611140 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 14 Apr 2019 02:06:20 +0200 Subject: util.ip: Add missing netmask for 192.168/16 range (fixes #1343) --- util/ip.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/ip.lua b/util/ip.lua index 0ec9e297..d1808225 100644 --- a/util/ip.lua +++ b/util/ip.lua @@ -203,7 +203,7 @@ local rfc6598 = new_ip("100.64.0.0"); function ip_methods:private() local private = self.scope ~= 0xE; if not private and self.proto == "IPv4" then - return match(self, rfc1918_8, 8) or match(self, rfc1918_12, 12) or match(self, rfc1918_16) or match(self, rfc6598, 10); + return match(self, rfc1918_8, 8) or match(self, rfc1918_12, 12) or match(self, rfc1918_16, 16) or match(self, rfc6598, 10); end return private; end -- cgit v1.2.3 From 3d14e00260469a7d2ba439ec5c3aad9cdb7238d1 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 19 Apr 2019 13:12:00 +0200 Subject: util.hashes: Use HMAC function provided by OpenSSL (fixes #1345) --- util-src/hashes.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/util-src/hashes.c b/util-src/hashes.c index 82f5876e..903ecb6e 100644 --- a/util-src/hashes.c +++ b/util-src/hashes.c @@ -25,6 +25,7 @@ typedef unsigned __int32 uint32_t; #include "lauxlib.h" #include #include +#include #if (LUA_VERSION_NUM == 501) #define luaL_setfuncs(L, R, N) luaL_register(L, NULL, R) @@ -112,35 +113,28 @@ static void hmac(struct hash_desc *desc, const char *key, size_t key_len, desc->Final(result, desc->ctxo); } -#define MAKE_HMAC_FUNCTION(myFunc, func, size, type) \ +#define MAKE_HMAC_FUNCTION(myFunc, evp, size, type) \ static int myFunc(lua_State *L) { \ - type ctx, ctxo; \ unsigned char hash[size], result[2*size]; \ size_t key_len, msg_len; \ + unsigned int out_len; \ const char *key = luaL_checklstring(L, 1, &key_len); \ const char *msg = luaL_checklstring(L, 2, &msg_len); \ const int hex_out = lua_toboolean(L, 3); \ - struct hash_desc desc; \ - desc.Init = (int (*)(void*))func##_Init; \ - desc.Update = (int (*)(void*, const void *, size_t))func##_Update; \ - desc.Final = (int (*)(unsigned char*, void*))func##_Final; \ - desc.digestLength = size; \ - desc.ctx = &ctx; \ - desc.ctxo = &ctxo; \ - hmac(&desc, key, key_len, msg, msg_len, hash); \ + HMAC(evp(), key, key_len, (const unsigned char*)msg, msg_len, (unsigned char*)hash, &out_len); \ if (hex_out) { \ - toHex(hash, size, result); \ - lua_pushlstring(L, (char*)result, size*2); \ + toHex(hash, out_len, result); \ + lua_pushlstring(L, (char*)result, out_len*2); \ } else { \ - lua_pushlstring(L, (char*)hash, size); \ + lua_pushlstring(L, (char*)hash, out_len); \ } \ return 1; \ } -MAKE_HMAC_FUNCTION(Lhmac_sha1, SHA1, SHA_DIGEST_LENGTH, SHA_CTX) -MAKE_HMAC_FUNCTION(Lhmac_sha256, SHA256, SHA256_DIGEST_LENGTH, SHA256_CTX) -MAKE_HMAC_FUNCTION(Lhmac_sha512, SHA512, SHA512_DIGEST_LENGTH, SHA512_CTX) -MAKE_HMAC_FUNCTION(Lhmac_md5, MD5, MD5_DIGEST_LENGTH, MD5_CTX) +MAKE_HMAC_FUNCTION(Lhmac_sha1, EVP_sha1, SHA_DIGEST_LENGTH, SHA_CTX) +MAKE_HMAC_FUNCTION(Lhmac_sha256, EVP_sha256, SHA256_DIGEST_LENGTH, SHA256_CTX) +MAKE_HMAC_FUNCTION(Lhmac_sha512, EVP_sha512, SHA512_DIGEST_LENGTH, SHA512_CTX) +MAKE_HMAC_FUNCTION(Lhmac_md5, EVP_md5, MD5_DIGEST_LENGTH, MD5_CTX) static int LscramHi(lua_State *L) { union xory { -- cgit v1.2.3 From eca1ddea1e620a6200c4999b82d1c793b354c481 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 23 Apr 2019 19:13:50 +0200 Subject: mod_tls: Log debug message for each kind of TLS context created Creating TLS contexts triggers a lot of messages from certmanager that don't really describe their purpose. This is meant to provide hints about that. --- plugins/mod_tls.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/mod_tls.lua b/plugins/mod_tls.lua index 029ddd1d..eb208e28 100644 --- a/plugins/mod_tls.lua +++ b/plugins/mod_tls.lua @@ -52,12 +52,15 @@ function module.load() local parent_s2s = rawgetopt(parent, "s2s_ssl") or NULL; local host_s2s = rawgetopt(modhost, "s2s_ssl") or parent_s2s; + module:log("debug", "Creating context for c2s"); ssl_ctx_c2s, err, ssl_cfg_c2s = create_context(host.host, "server", host_c2s, host_ssl, global_c2s); -- for incoming client connections if not ssl_ctx_c2s then module:log("error", "Error creating context for c2s: %s", err); end + module:log("debug", "Creating context for s2sout"); ssl_ctx_s2sout, err, ssl_cfg_s2sout = create_context(host.host, "client", host_s2s, host_ssl, global_s2s); -- for outgoing server connections if not ssl_ctx_s2sout then module:log("error", "Error creating contexts for s2sout: %s", err); end + module:log("debug", "Creating context for s2sin"); ssl_ctx_s2sin, err, ssl_cfg_s2sin = create_context(host.host, "server", host_s2s, host_ssl, global_s2s); -- for incoming server connections if not ssl_ctx_s2sin then module:log("error", "Error creating contexts for s2sin: %s", err); end end -- cgit v1.2.3 From b623c6cb41f91964faf97e2b9bb6fab9993ab658 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 23 Apr 2019 19:21:06 +0200 Subject: core.portmanager: Log debug message for each kind of TLS context created --- core/portmanager.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/core/portmanager.lua b/core/portmanager.lua index 1ed37da0..bed5eca5 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -112,6 +112,7 @@ local function activate(service_name) if service_info.encryption == "ssl" then local global_ssl_config = config.get("*", "ssl") or {}; local prefix_ssl_config = config.get("*", config_prefix.."ssl") or global_ssl_config; + log("debug", "Creating context for direct TLS service %s on port %d", service_info.name, port); ssl, err = certmanager.create_context(service_info.name.." port "..port, "server", prefix_ssl_config[interface], prefix_ssl_config[port], -- cgit v1.2.3 From bd81be8593e4f529af26978b02abafba37d59bae Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 24 Apr 2019 16:16:19 +0200 Subject: util.encodings: Allow unassigned code points in ICU mode to match libidn behavior (fixes #1348) --- util-src/encodings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util-src/encodings.c b/util-src/encodings.c index f7e8131f..e55a3f44 100644 --- a/util-src/encodings.c +++ b/util-src/encodings.c @@ -299,7 +299,7 @@ static int icu_stringprep_prep(lua_State *L, const UStringPrepProfile *profile) return 1; } - prepped_len = usprep_prepare(profile, unprepped, unprepped_len, prepped, 1024, 0, NULL, &err); + prepped_len = usprep_prepare(profile, unprepped, unprepped_len, prepped, 1024, USPREP_ALLOW_UNASSIGNED, NULL, &err); if(U_FAILURE(err)) { lua_pushnil(L); -- cgit v1.2.3 From 2abb1bdc3c550e45f943cd37f1e834419bd63605 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 1 May 2019 22:34:15 +0200 Subject: prosodyctl: Include version of LuaDBI in 'about' --- prosodyctl | 1 + 1 file changed, 1 insertion(+) diff --git a/prosodyctl b/prosodyctl index b1c6fffd..e580446b 100755 --- a/prosodyctl +++ b/prosodyctl @@ -395,6 +395,7 @@ function commands.about(arg) local module_versions, longest_name = {}, 8; local luaevent =dependencies.softreq"luaevent"; dependencies.softreq"ssl"; + dependencies.softreq"DBI"; for name, module in pairs(package.loaded) do if type(module) == "table" and rawget(module, "_VERSION") and name ~= "_G" and not name:match("%.") then -- cgit v1.2.3 From df1246244109fdbbeb805d8c03ce20bbe4dadc94 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 4 May 2019 04:23:35 +0200 Subject: net.server_epoll: Restore wantread flag after pause (fixes #1354) If a chunk of data has been received that is larger than the amount read at a time, then the connection is paused for a short time after which it tries to read some more. If, after that, there is still more data to read, it should do the same thing. However, because the "want read" flag is removed and was restored after the delayed reading, it would not schedule another delayed read. --- net/server_epoll.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/server_epoll.lua b/net/server_epoll.lua index c41266e1..c279c579 100644 --- a/net/server_epoll.lua +++ b/net/server_epoll.lua @@ -600,10 +600,10 @@ function interface:pausefor(t) self:set(false); self._pausefor = addtimer(t, function () self._pausefor = nil; + self:set(true); if self.conn and self.conn:dirty() then self:onreadable(); end - self:set(true); end); end -- cgit v1.2.3 From 36ec6ce8ab02e62c83ca39dfd6616a1bfa753968 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 4 May 2019 22:48:59 +0200 Subject: configure: Handle lua being found in /bin (workaround for #1353) --- configure | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/configure b/configure index 4307997c..0fe186b5 100755 --- a/configure +++ b/configure @@ -419,6 +419,13 @@ else } fi +# See #1353 +if [ "$LUA_DIR_SET" != "yes" ] && [ "$LUA_DIR" = "/" ] +then + LUA_DIR="/usr" +fi + + if [ "$lua_interp_found" != "yes" ] && [ "$RUNWITH_SET" != "yes" ] then if [ "$LUA_VERSION_SET" ]; then -- cgit v1.2.3 From 6bf96583fa7a3b1919815408dc9e1fecfe90af77 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 13 May 2019 14:47:41 +0200 Subject: mod_storage_sql: Move code out of if-else chain --- plugins/mod_storage_sql.lua | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/plugins/mod_storage_sql.lua b/plugins/mod_storage_sql.lua index 56cef569..dbf5e2da 100644 --- a/plugins/mod_storage_sql.lua +++ b/plugins/mod_storage_sql.lua @@ -390,6 +390,14 @@ function archive_store:delete(username, query) else args[#args+1] = query.truncate; local unlimited = "ALL"; + sql_query = [[ + DELETE FROM "prosodyarchive" + WHERE "sort_id" IN ( + SELECT "sort_id" FROM "prosodyarchive" + WHERE %s + ORDER BY "sort_id" %s + LIMIT %s OFFSET ? + );]]; if engine.params.driver == "SQLite3" then sql_query = [[ DELETE FROM "prosodyarchive" @@ -407,15 +415,6 @@ function archive_store:delete(username, query) LIMIT %s OFFSET ? ) AS limiter on result.sort_id = limiter.sort_id;]]; unlimited = "18446744073709551615"; - else - sql_query = [[ - DELETE FROM "prosodyarchive" - WHERE "sort_id" IN ( - SELECT "sort_id" FROM "prosodyarchive" - WHERE %s - ORDER BY "sort_id" %s - LIMIT %s OFFSET ? - );]]; end sql_query = string.format(sql_query, t_concat(where, " AND "), query.reverse and "ASC" or "DESC", unlimited); -- cgit v1.2.3 From 663a4c3933139a06e70e9c163fc848eceb9b4442 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 13 May 2019 14:39:38 +0200 Subject: mod_storage_sql: Handle SQLite DELETE with LIMIT being optional (fixes #1359) --- plugins/mod_storage_sql.lua | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/plugins/mod_storage_sql.lua b/plugins/mod_storage_sql.lua index dbf5e2da..a449091e 100644 --- a/plugins/mod_storage_sql.lua +++ b/plugins/mod_storage_sql.lua @@ -399,12 +399,14 @@ function archive_store:delete(username, query) LIMIT %s OFFSET ? );]]; if engine.params.driver == "SQLite3" then - sql_query = [[ - DELETE FROM "prosodyarchive" - WHERE %s - ORDER BY "sort_id" %s - LIMIT %s OFFSET ?; - ]]; + if engine._have_delete_limit then + sql_query = [[ + DELETE FROM "prosodyarchive" + WHERE %s + ORDER BY "sort_id" %s + LIMIT %s OFFSET ?; + ]]; + end unlimited = "-1"; elseif engine.params.driver == "MySQL" then sql_query = [[ @@ -620,6 +622,13 @@ function module.load() module:log("error", "Old database format detected. Please run: prosodyctl mod_%s upgrade", module.name); return false, "database upgrade needed"; end + if engine.params.driver == "SQLite3" then + for row in engine:select("PRAGMA compile_options") do + if row[1] == "ENABLE_UPDATE_DELETE_LIMIT" then + engine._have_delete_limit = true; + end + end + end end end); engines[sql.db2uri(params)] = engine; -- cgit v1.2.3 From 32b4030f4327148e2edf83ac4354e4d62039b3c0 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 18 May 2019 17:45:20 +0200 Subject: mod_c2s: Associate connection with session last (fixes #1313) This way, any fatal error in the callback will not leave a half-established session. --- plugins/mod_c2s.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/mod_c2s.lua b/plugins/mod_c2s.lua index 8e31a968..15d3a9be 100644 --- a/plugins/mod_c2s.lua +++ b/plugins/mod_c2s.lua @@ -239,7 +239,6 @@ end --- Port listener function listener.onconnect(conn) local session = sm_new_session(conn); - sessions[conn] = session; session.log("info", "Client connected"); @@ -300,6 +299,8 @@ function listener.onconnect(conn) end session.dispatch_stanza = stream_callbacks.handlestanza; + + sessions[conn] = session; end function listener.onincoming(conn, data) -- cgit v1.2.3 From 23da49fba0397a2c8f2701efb22634f0a3f68bd8 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 18 May 2019 17:28:21 +0200 Subject: util.random: Handle unlikely read errors from /dev/urandom (see #1313) --- util/random.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/util/random.lua b/util/random.lua index d8a84514..8ae06b49 100644 --- a/util/random.lua +++ b/util/random.lua @@ -12,7 +12,11 @@ if ok then return crand; end local urandom, urandom_err = io.open("/dev/urandom", "r"); local function bytes(n) - return urandom:read(n); + local data, err = urandom:read(n); + if not data then + error("Unable to retrieve data from secure random number generator (/dev/urandom): "..err); + end + return data; end if not urandom then -- cgit v1.2.3 From f51f1f1375c3b1ff58aaa1f8b9082b69b1ef95d7 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 18 May 2019 18:51:25 +0200 Subject: util.random: Coerce error to string (thanks waqas) In theory this could happen in an EOF condition, which should be impossible with a read from /dev/urandom. --- util/random.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/random.lua b/util/random.lua index 8ae06b49..51f2747d 100644 --- a/util/random.lua +++ b/util/random.lua @@ -14,7 +14,7 @@ local urandom, urandom_err = io.open("/dev/urandom", "r"); local function bytes(n) local data, err = urandom:read(n); if not data then - error("Unable to retrieve data from secure random number generator (/dev/urandom): "..err); + error("Unable to retrieve data from secure random number generator (/dev/urandom): "..tostring(err)); end return data; end -- cgit v1.2.3 From 8ceccabe00d91193fa3b940b3c9b9d65727b8a0d Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 21 May 2019 08:52:21 +0200 Subject: util.random: Throw different error for EOL condition --- util/random.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/util/random.lua b/util/random.lua index 51f2747d..6782d7fa 100644 --- a/util/random.lua +++ b/util/random.lua @@ -14,7 +14,11 @@ local urandom, urandom_err = io.open("/dev/urandom", "r"); local function bytes(n) local data, err = urandom:read(n); if not data then - error("Unable to retrieve data from secure random number generator (/dev/urandom): "..tostring(err)); + if err then + error("Unable to retrieve data from secure random number generator (/dev/urandom): "..tostring(err)); + else + error("Secure random number generator (/dev/urandom) returned an end-of-file condition"); + end end return data; end -- cgit v1.2.3 From cdd8ba5308e3d987fdd66559da734ab4cb8a169f Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 27 May 2019 12:05:35 +0200 Subject: mod_announce: Check for admin on current virtualhost instead of global (fixes #1365) (thanks yc) --- plugins/mod_announce.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_announce.lua b/plugins/mod_announce.lua index 14bb9f3d..970a273a 100644 --- a/plugins/mod_announce.lua +++ b/plugins/mod_announce.lua @@ -44,7 +44,7 @@ function handle_announcement(event) return; -- Not an announcement end - if not is_admin(stanza.attr.from) then + if not is_admin(stanza.attr.from, host) then -- Not an admin? Not allowed! module:log("warn", "Non-admin '%s' tried to send server announcement", stanza.attr.from); return; -- cgit v1.2.3 From fddbd6ed134ebde535c574da5e8a7a0d4e007767 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 27 May 2019 19:00:32 +0200 Subject: mod_mam: Cache last date that archive owner has messages to reduce writes (fixes #1368) --- plugins/mod_mam/mod_mam.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/mod_mam/mod_mam.lua b/plugins/mod_mam/mod_mam.lua index a8c2689d..8900a2be 100644 --- a/plugins/mod_mam/mod_mam.lua +++ b/plugins/mod_mam/mod_mam.lua @@ -348,8 +348,14 @@ if cleanup_after ~= "never" then -- messages, we collect the union of sets of users from dates that fall -- outside the cleanup range. + local last_date = require "util.cache".new(module:get_option_number("archive_cleanup_date_cache_size", 1000)); function schedule_cleanup(username, date) - cleanup_map:set(date or datestamp(), username, true); + date = date or datestamp(); + if last_date:get(username) == date then return end + local ok = cleanup_map:set(date, username, true); + if ok then + last_date:set(username, date); + end end cleanup_runner = require "util.async".runner(function () -- cgit v1.2.3 From 41c625f6a9cc611342a7a964888952c08e9201c6 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 27 May 2019 19:00:34 +0200 Subject: mod_muc_mam: Cache last date that archive owner has messages to reduce writes (fixes #1368) --- plugins/mod_muc_mam.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/mod_muc_mam.lua b/plugins/mod_muc_mam.lua index 1fbc8b2a..a2e3f81b 100644 --- a/plugins/mod_muc_mam.lua +++ b/plugins/mod_muc_mam.lua @@ -422,8 +422,14 @@ if cleanup_after ~= "never" then -- messages, we collect the union of sets of rooms from dates that fall -- outside the cleanup range. + local last_date = require "util.cache".new(module:get_option_number("muc_log_cleanup_date_cache_size", 1000)); function schedule_cleanup(roomname, date) - cleanup_map:set(date or datestamp(), roomname, true); + date = date or datestamp(); + if last_date:get(roomname) == date then return end + local ok = cleanup_map:set(date, roomname, true); + if ok then + last_date:set(roomname, date); + end end cleanup_runner = require "util.async".runner(function () -- cgit v1.2.3 From 71bdfa72d8ce993ede0ac595c8e78404758e3546 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 30 May 2019 15:16:56 +0200 Subject: mod_pep: Handle presence based subscription outside of util.pubsub (fixes #1372) Subscriptions were updated for each incoming presence stanza from contacts. Each subscription change triggered a configuration save, which would filter out the presence based subscriptions and usually end up replacing the existing data with identical data. With many subscribed nodes this adds up to a fair bit of IO bound work that is avoided by keeping them separate. --- plugins/mod_pep.lua | 54 +++++++++++++++++------------------------------------ 1 file changed, 17 insertions(+), 37 deletions(-) diff --git a/plugins/mod_pep.lua b/plugins/mod_pep.lua index 7a4aac2b..f60f84c9 100644 --- a/plugins/mod_pep.lua +++ b/plugins/mod_pep.lua @@ -90,21 +90,6 @@ local function nodestore(username) return data, err; end function store:set(node, data) - if data then - -- Save the data without subscriptions - local subscribers = {}; - for jid, sub in pairs(data.subscribers) do - if type(sub) ~= "table" or not sub.presence then - subscribers[jid] = sub; - end - end - data = { - name = data.name; - config = data.config; - affiliations = data.affiliations; - subscribers = subscribers; - }; - end return node_config:set(username, node, data); end function store:users() @@ -151,7 +136,23 @@ local function get_broadcaster(username) end message:add_child(item); end + + local broadcast_to = {}; for jid in pairs(jids) do + broadcast_to[jid] = true; + end + + local service_recipients = recipients[username]; + if service_recipients then + local service = services[username]; + for recipient, nodes in pairs(service_recipients) do + if nodes:contains(node) and service:may(node, recipient, "subscribe") then + broadcast_to[recipient] = true; + end + end + end + + for jid in pairs(broadcast_to) do module:log("debug", "Sending notification to %s from %s: %s", jid, user_bare, tostring(item)); message.attr.to = jid; module:send(message); @@ -160,20 +161,6 @@ local function get_broadcaster(username) return simple_broadcast; end -local function on_node_creation(event) - local service = event.service; - local node = event.node; - local username = service.config.pep_username; - - local service_recipients = recipients[username]; - if not service_recipients then return; end - - for recipient, nodes in pairs(service_recipients) do - if nodes:contains(node) then - service:add_subscription(node, recipient, recipient, { presence = true }); - end - end -end function get_pep_service(username) module:log("debug", "get_pep_service(%q)", username); @@ -233,10 +220,6 @@ function get_pep_service(username) return service; end -module:hook("item-added/pep-service", function (event) - local service = event.item.service; - module:hook_object_event(service.events, "node-created", on_node_creation); -end); function handle_pubsub_iq(event) local origin, stanza = event.origin, event.stanza; @@ -303,12 +286,9 @@ local function update_subscriptions(recipient, service_name, nodes) end local service = get_pep_service(service_name); - for node in current - nodes do - service:remove_subscription(node, recipient, recipient); - end for node in nodes - current do - if service:add_subscription(node, recipient, recipient, { presence = true }) then + if service:may(node, recipient, "subscribe") then resend_last_item(recipient, node, service); end end -- cgit v1.2.3 From d324c430553c7470211b44cb5f3e9cded7c53228 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 30 May 2019 23:50:28 +0200 Subject: util.sql: Ignore if tables and indices already exist on creation (fixes #1064) Tested with SQLite3 3.16.2 and 3.27.2 and Postgres 11. MySQL does not support IF NOT EXISTS for indices so not handled here. --- util/sql.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/util/sql.lua b/util/sql.lua index 47900102..00c7b57f 100644 --- a/util/sql.lua +++ b/util/sql.lua @@ -238,6 +238,9 @@ function engine:transaction(...) end function engine:_create_index(index) local sql = "CREATE INDEX \""..index.name.."\" ON \""..index.table.."\" ("; + if self.params.driver ~= "MySQL" then + sql = sql:gsub("^CREATE INDEX", "%1 IF NOT EXISTS"); + end for i=1,#index do sql = sql.."\""..index[i].."\""; if i ~= #index then sql = sql..", "; end @@ -256,6 +259,9 @@ function engine:_create_index(index) end function engine:_create_table(table) local sql = "CREATE TABLE \""..table.name.."\" ("; + do + sql = sql:gsub("^CREATE TABLE", "%1 IF NOT EXISTS"); + end for i,col in ipairs(table.c) do local col_type = col.type; if col_type == "MEDIUMTEXT" and self.params.driver ~= "MySQL" then -- cgit v1.2.3 From efcd7d69026afe4a90e27c94a697644d57c5f764 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 10 Jun 2019 12:58:00 +0200 Subject: mod_pep: Revert 045209b41b3a, caused a regression Adding in all presence based subscriptions in the broadcaster caused resend_last_item() to unintentionally send out more notifications than it should have. --- plugins/mod_pep.lua | 54 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/plugins/mod_pep.lua b/plugins/mod_pep.lua index f60f84c9..7a4aac2b 100644 --- a/plugins/mod_pep.lua +++ b/plugins/mod_pep.lua @@ -90,6 +90,21 @@ local function nodestore(username) return data, err; end function store:set(node, data) + if data then + -- Save the data without subscriptions + local subscribers = {}; + for jid, sub in pairs(data.subscribers) do + if type(sub) ~= "table" or not sub.presence then + subscribers[jid] = sub; + end + end + data = { + name = data.name; + config = data.config; + affiliations = data.affiliations; + subscribers = subscribers; + }; + end return node_config:set(username, node, data); end function store:users() @@ -136,23 +151,7 @@ local function get_broadcaster(username) end message:add_child(item); end - - local broadcast_to = {}; for jid in pairs(jids) do - broadcast_to[jid] = true; - end - - local service_recipients = recipients[username]; - if service_recipients then - local service = services[username]; - for recipient, nodes in pairs(service_recipients) do - if nodes:contains(node) and service:may(node, recipient, "subscribe") then - broadcast_to[recipient] = true; - end - end - end - - for jid in pairs(broadcast_to) do module:log("debug", "Sending notification to %s from %s: %s", jid, user_bare, tostring(item)); message.attr.to = jid; module:send(message); @@ -161,6 +160,20 @@ local function get_broadcaster(username) return simple_broadcast; end +local function on_node_creation(event) + local service = event.service; + local node = event.node; + local username = service.config.pep_username; + + local service_recipients = recipients[username]; + if not service_recipients then return; end + + for recipient, nodes in pairs(service_recipients) do + if nodes:contains(node) then + service:add_subscription(node, recipient, recipient, { presence = true }); + end + end +end function get_pep_service(username) module:log("debug", "get_pep_service(%q)", username); @@ -220,6 +233,10 @@ function get_pep_service(username) return service; end +module:hook("item-added/pep-service", function (event) + local service = event.item.service; + module:hook_object_event(service.events, "node-created", on_node_creation); +end); function handle_pubsub_iq(event) local origin, stanza = event.origin, event.stanza; @@ -286,9 +303,12 @@ local function update_subscriptions(recipient, service_name, nodes) end local service = get_pep_service(service_name); + for node in current - nodes do + service:remove_subscription(node, recipient, recipient); + end for node in nodes - current do - if service:may(node, recipient, "subscribe") then + if service:add_subscription(node, recipient, recipient, { presence = true }) then resend_last_item(recipient, node, service); end end -- cgit v1.2.3 From 8e381dbb1e0041b30908e37e0db5acbe87c21de4 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 20 Jun 2019 21:08:12 +0200 Subject: mod_blocklist: Add test case capturing current behavior --- spec/scansion/blocking.scs | 162 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 spec/scansion/blocking.scs diff --git a/spec/scansion/blocking.scs b/spec/scansion/blocking.scs new file mode 100644 index 00000000..5f4c8cba --- /dev/null +++ b/spec/scansion/blocking.scs @@ -0,0 +1,162 @@ +# XEP-0191: Blocking Command + +[Client] Romeo + jid: blocker@localhost + password: password + +[Client] Juliet + jid: blockee@localhost + password: password + +----- + +# The parties connect +Romeo connects + +Romeo sends: + + +Romeo receives: + + + + +Juliet connects + +Juliet sends: + + +Juliet receives: + + + + +# They add each other +Romeo sends: + + +Romeo receives: + + +Juliet receives: + + +Juliet sends: + + +Romeo receives: + + + + + +Juliet sends: + + +Juliet receives: + + +Romeo receives: + + +Romeo sends: + + +Juliet receives: + + + + + +Romeo receives: + + + + + +# They can now talk +Juliet sends: + + ohai + + +Romeo receives: + + ohai + + +# And now to the blockining + +Romeo sends: + + + + + + +Romeo receives: + + +Juliet receives: + + +# Can"t talk anymore +Romeo sends: + + hello? + + +Romeo receives: + + + + You have blocked this JID + + + + +Juliet sends: + + +Juliet receives: + + + + + + +Romeo sends: + + + + + + +Romeo receives: + + +# Can talk again +Romeo sends: + + hello! + + +Juliet receives: + + hello! + + +# Bye +Juliet disconnects + +Juliet sends: + + +Romeo receives: + + +Romeo disconnects + +Romeo sends: + + -- cgit v1.2.3 From 4a997af768df029e1b5f350c41e13887f679701e Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 20 Jun 2019 21:18:46 +0200 Subject: mod_blocklist: Update test case with correct behavior (see #1380) Expect failure --- spec/scansion/blocking.scs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spec/scansion/blocking.scs b/spec/scansion/blocking.scs index 5f4c8cba..809bbcb5 100644 --- a/spec/scansion/blocking.scs +++ b/spec/scansion/blocking.scs @@ -132,6 +132,12 @@ Romeo sends: +Juliet receives: + + + + + Romeo receives: -- cgit v1.2.3 From f1c10dbc910f8d98ad7379e50a6f8a183e60019a Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 20 Jun 2019 21:20:00 +0200 Subject: mod_blocklist: Trigger resend of presence when unblocking a contact (fixes #1380) --- plugins/mod_blocklist.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugins/mod_blocklist.lua b/plugins/mod_blocklist.lua index 8aca7332..ee48ffad 100644 --- a/plugins/mod_blocklist.lua +++ b/plugins/mod_blocklist.lua @@ -128,6 +128,7 @@ local function edit_blocklist(event) -- > only if the contact is allowed to receive presence notifications [...] -- So contacts we need to do that for are added to the set below. local send_unavailable = is_blocking and {}; + local send_available = not is_blocking and {}; -- Because blocking someone currently also blocks the ability to reject -- subscription requests, we'll preemptively reject such @@ -147,6 +148,8 @@ local function edit_blocklist(event) elseif is_contact_pending_in(username, module.host, jid) then remove_pending[jid] = true; end + elseif is_contact_subscribed(username, module.host, jid) then + send_available[jid] = true; end end @@ -203,6 +206,11 @@ local function edit_blocklist(event) save_roster(username, module.host, roster); -- Not much we can do about save failing here end + else + local user_bare = username .. "@" .. module.host; + for jid in pairs(send_available) do + module:send(st.presence({ type = "probe", to = user_bare, from = jid })); + end end local blocklist_push = st.iq({ type = "set", id = "blocklist-push" }) -- cgit v1.2.3 From a4f539d600ec4a3e88f545f80abd0d1349e95024 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 20 Jun 2019 22:10:19 +0200 Subject: mod_blocklist: Remove unrelated tags from test case --- spec/scansion/blocking.scs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/spec/scansion/blocking.scs b/spec/scansion/blocking.scs index 809bbcb5..7b46167f 100644 --- a/spec/scansion/blocking.scs +++ b/spec/scansion/blocking.scs @@ -17,9 +17,7 @@ Romeo sends: Romeo receives: - - - + Juliet connects @@ -27,9 +25,7 @@ Juliet sends: Juliet receives: - - - + # They add each other Romeo sends: @@ -47,7 +43,6 @@ Juliet sends: Romeo receives: - Juliet sends: @@ -65,13 +60,11 @@ Romeo sends: Juliet receives: - Romeo receives: - # They can now talk @@ -134,7 +127,6 @@ Romeo sends: Juliet receives: - -- cgit v1.2.3 From 794356d5130ed275db3132b8eb5205080c8b9c91 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 20 Jun 2019 22:25:46 +0200 Subject: scansion/blocking: Remove the right irrelevant thing --- spec/scansion/blocking.scs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/scansion/blocking.scs b/spec/scansion/blocking.scs index 7b46167f..6a9f199e 100644 --- a/spec/scansion/blocking.scs +++ b/spec/scansion/blocking.scs @@ -127,7 +127,7 @@ Romeo sends: Juliet receives: - + Romeo receives: -- cgit v1.2.3 From 005aa17e7a8a7e16a41113def642d8aabb3507fd Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 8 Jul 2019 01:17:34 +0200 Subject: net.server_epoll: Backport timer optimization 6c2370f17027 from trunk (see #1388) The previous timer handling did not scale well and led to high CPU usage with many connections (each with at least an read timeout). --- net/server_epoll.lua | 77 ++++++++++++++++++++-------------------------------- 1 file changed, 29 insertions(+), 48 deletions(-) diff --git a/net/server_epoll.lua b/net/server_epoll.lua index c279c579..0c03ae15 100644 --- a/net/server_epoll.lua +++ b/net/server_epoll.lua @@ -6,9 +6,7 @@ -- -local t_sort = table.sort; local t_insert = table.insert; -local t_remove = table.remove; local t_concat = table.concat; local setmetatable = setmetatable; local tostring = tostring; @@ -20,6 +18,7 @@ local log = require "util.logger".init("server_epoll"); local socket = require "socket"; local luasec = require "ssl"; local gettime = require "util.time".now; +local indexedbheap = require "util.indexedbheap"; local createtable = require "util.table".create; local inet = require "util.net"; local inet_pton = inet.pton; @@ -66,22 +65,24 @@ local fds = createtable(10, 0); -- FD -> conn -- Timer and scheduling -- -local timers = {}; +local timers = indexedbheap.create(); local function noop() end local function closetimer(t) t[1] = 0; t[2] = noop; + timers:remove(t.id); end --- Set to true when timers have changed -local resort_timers = false; +local function reschedule(t, time) + t[1] = time; + timers:reprioritize(t.id, time); +end -- Add absolute timer local function at(time, f) - local timer = { time, f, close = closetimer }; - t_insert(timers, timer); - resort_timers = true; + local timer = { time, f, close = closetimer, reschedule = reschedule, id = nil }; + timer.id = timers:insert(timer, time); return timer; end @@ -94,50 +95,32 @@ end -- Return time until next timeout local function runtimers(next_delay, min_wait) -- Any timers at all? - if not timers[1] then - return next_delay; + local now = gettime(); + local peek = timers:peek(); + while peek do + + if peek > now then + next_delay = peek - now; + break; end - if resort_timers then - -- Sort earliest timers to the end - t_sort(timers, function (a, b) return a[1] > b[1]; end); - resort_timers = false; + local _, timer, id = timers:pop(); + local ok, ret = pcall(timer[2], now); + if ok and type(ret) == "number" then + local next_time = now+ret; + timer[1] = next_time; + timers:insert(timer, next_time); end - -- Iterate from the end and remove completed timers - for i = #timers, 1, -1 do - local timer = timers[i]; - local t, f = timer[1], timer[2]; - -- Get time for every iteration to increase accuracy - local now = gettime(); - if t > now then - -- This timer should not fire yet - local diff = t - now; - if diff < next_delay then - next_delay = diff; + peek = timers:peek(); end - break; - end - local new_timeout = f(now); - if new_timeout then - -- Schedule for 'delay' from the time actually scheduled, - -- not from now, in order to prevent timer drift. - timer[1] = t + new_timeout; - resort_timers = true; - else - t_remove(timers, i); - end + if peek == nil then + return next_delay; end - if resort_timers or next_delay < min_wait then - -- Timers may be added from within a timer callback. - -- Those would not be considered for next_delay, - -- and we might sleep for too long, so instead - -- we return a shorter timeout so we can - -- properly sort all new timers. - next_delay = min_wait; + if next_delay < min_wait then + return min_wait; end - return next_delay; end @@ -243,8 +226,7 @@ function interface:setreadtimeout(t) end t = t or cfg.read_timeout; if self._readtimeout then - self._readtimeout[1] = gettime() + t; - resort_timers = true; + self._readtimeout:reschedule(gettime() + t); else self._readtimeout = addtimer(t, function () if self:on("readtimeout") then @@ -268,8 +250,7 @@ function interface:setwritetimeout(t) end t = t or cfg.send_timeout; if self._writetimeout then - self._writetimeout[1] = gettime() + t; - resort_timers = true; + self._writetimeout:reschedule(gettime() + t); else self._writetimeout = addtimer(t, function () self:on("disconnect", "write timeout"); -- cgit v1.2.3 From e22a26259927efe96a2808964e98f1dd42a573e5 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 8 Jul 2019 02:46:27 +0200 Subject: util.serialization: Cache default serialization instance (fixes #1389) Most serialization uses still use the default serialize() and thus duplicate much of the setup, which negates some of the performance improvements of the rewrite. --- util/serialization.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/util/serialization.lua b/util/serialization.lua index dd6a2a2b..5121a9f9 100644 --- a/util/serialization.lua +++ b/util/serialization.lua @@ -272,10 +272,15 @@ local function deserialize(str) return ret; end +local default = new(); return { new = new; serialize = function (x, opt) - return new(opt)(x); + if opt == nil then + return default(x); + else + return new(opt)(x); + end end; deserialize = deserialize; }; -- cgit v1.2.3 From 0d6f101a496bf5aa5bb827432270030192c1e5c8 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 20 Jul 2019 04:19:58 +0200 Subject: mod_websocket: Clone stanza before mutating (fixes #1398) Checking for `stanza.attr.xmlns == nil` to determine if the stanza object is an actual stanza (``, `` or `` in the `jabber:client` or `jabbber:server` namespace) or some other stream element. Since this mutation is not reverted, it may leak to other places and cause them to mistreat stanzas as stream elements. Especially in cases like MUC where a single stanza is broadcast to many recipients. --- plugins/mod_websocket.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/mod_websocket.lua b/plugins/mod_websocket.lua index b4aba338..686a8981 100644 --- a/plugins/mod_websocket.lua +++ b/plugins/mod_websocket.lua @@ -285,6 +285,7 @@ function handle_request(event) end); add_filter(session, "stanzas/out", function(stanza) + stanza = st.clone(stanza); local attr = stanza.attr; attr.xmlns = attr.xmlns or xmlns_client; if stanza.name:find("^stream:") then -- cgit v1.2.3 From 1e99785e748e680b10849ab116e692d9a193ac17 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 30 Jul 2019 08:01:22 +0200 Subject: MUC: Advertise XEP-0410 support Unsure if the feature was in the XEP at the time of 7c1cdf5f9f83 --- plugins/muc/muc.lib.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 9648ea78..a942182d 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -358,6 +358,7 @@ end module:hook("muc-disco#info", function(event) event.reply:tag("feature", {var = "http://jabber.org/protocol/muc"}):up(); event.reply:tag("feature", {var = "http://jabber.org/protocol/muc#stable_id"}):up(); + event.reply:tag("feature", {var = "http://jabber.org/protocol/muc#self-ping-optimization"}):up(); end); module:hook("muc-disco#info", function(event) table.insert(event.form, { name = "muc#roominfo_occupants", label = "Number of occupants" }); -- cgit v1.2.3 From 2f87bc1e336ac432fee87a92a7f6ea17e68338fc Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 31 Aug 2019 16:08:45 +0200 Subject: MUC: Fix delay@from to be room JID (fixes #1416) --- plugins/muc/history.lib.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/muc/history.lib.lua b/plugins/muc/history.lib.lua index 445aacb9..0d69c97d 100644 --- a/plugins/muc/history.lib.lua +++ b/plugins/muc/history.lib.lua @@ -178,10 +178,10 @@ module:hook("muc-add-history", function(event) local ts = gettime(); local stamp = datetime.datetime(ts); stanza:tag("delay", { -- XEP-0203 - xmlns = "urn:xmpp:delay", from = module.host, stamp = stamp + xmlns = "urn:xmpp:delay", from = room.jid, stamp = stamp }):up(); stanza:tag("x", { -- XEP-0091 (deprecated) - xmlns = "jabber:x:delay", from = module.host, stamp = datetime.legacy() + xmlns = "jabber:x:delay", from = room.jid, stamp = datetime.legacy() }):up(); local entry = { stanza = stanza, timestamp = ts }; table.insert(history, entry); -- cgit v1.2.3 -- cgit v1.2.3 From c3d0a0a3289319bedcbc74d8c7eba75d381e2017 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 11 Sep 2019 15:03:54 +0100 Subject: prosodyctl: Fix traceback on incorrect proxy65_address type --- prosodyctl | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/prosodyctl b/prosodyctl index e580446b..405ef1f8 100755 --- a/prosodyctl +++ b/prosodyctl @@ -1103,17 +1103,21 @@ function commands.check(arg) if modules:contains("proxy65") then local proxy65_target = configmanager.get(host, "proxy65_address") or host; - 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."); + 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 -- cgit v1.2.3 From 44f502499a55638aae559a238f4eb760431581e0 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 21 Sep 2019 15:20:50 +0200 Subject: mod_vcard_legacy: Advertise lack of avatar correctly (fixes #1431) (thanks lovetox) --- plugins/mod_vcard_legacy.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/mod_vcard_legacy.lua b/plugins/mod_vcard_legacy.lua index ab2c4490..6229b931 100644 --- a/plugins/mod_vcard_legacy.lua +++ b/plugins/mod_vcard_legacy.lua @@ -301,10 +301,10 @@ local function inject_xep153(event) local pep_service = mod_pep.get_pep_service(username); stanza:remove_children("x", "vcard-temp:x:update"); - local x_update = st.stanza("x", { xmlns = "vcard-temp:x:update" }); + local x_update = st.stanza("x", { xmlns = "vcard-temp:x:update" }):tag("photo"); local ok, avatar_hash = pep_service:get_last_item("urn:xmpp:avatar:metadata", true); if ok and avatar_hash then - x_update:text_tag("photo", avatar_hash); + x_update:text(avatar_hash); end stanza:add_direct_child(x_update); end -- cgit v1.2.3 From 67b37537bb92eb5c2c2dcc7af3498dbe22cf8355 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 21 Sep 2019 16:29:16 +0200 Subject: mod_vcard_legacy: Don't owerwrite empty photo elements (fixes #1432) --- plugins/mod_vcard_legacy.lua | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/plugins/mod_vcard_legacy.lua b/plugins/mod_vcard_legacy.lua index 6229b931..5e75947a 100644 --- a/plugins/mod_vcard_legacy.lua +++ b/plugins/mod_vcard_legacy.lua @@ -300,13 +300,19 @@ local function inject_xep153(event) if stanza.attr.type then return end local pep_service = mod_pep.get_pep_service(username); - stanza:remove_children("x", "vcard-temp:x:update"); - local x_update = st.stanza("x", { xmlns = "vcard-temp:x:update" }):tag("photo"); + local x_update = stanza:get_child("x", "vcard-temp:x:update"); + if not x_update then + x_update = st.stanza("x", { xmlns = "vcard-temp:x:update" }):tag("photo"); + stanza:add_direct_child(x_update); + elseif x_update:get_child("photo") then + return; -- XEP implies that these should be left alone + else + x_update:tag("photo"); + end local ok, avatar_hash = pep_service:get_last_item("urn:xmpp:avatar:metadata", true); if ok and avatar_hash then x_update:text(avatar_hash); end - stanza:add_direct_child(x_update); end module:hook("pre-presence/full", inject_xep153, 1); -- cgit v1.2.3 From 8426a1b88e2a12239affdc970c845adfbda7bfdb Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 29 Sep 2019 20:48:00 +0200 Subject: MUC: Strip tags with MUC-related namespaces from private messages (fixes #1427) Prevents duplication since it adds another <{muc#user}x> here --- plugins/muc/muc.lib.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index a942182d..cf9b46f7 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -779,6 +779,7 @@ function room_mt:handle_message_to_occupant(origin, stanza) return true; end log("debug", "%s sent private message stanza to %s (%s)", from, to, o_data.jid); + stanza = muc_util.filter_muc_x(st.clone(stanza)); stanza:tag("x", { xmlns = "http://jabber.org/protocol/muc#user" }):up(); stanza.attr.from = current_nick; self:route_to_occupant(o_data, stanza) -- cgit v1.2.3 From 4d9a571128c08173315407e243a2b1badb5d9f8c Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 3 Oct 2019 16:38:15 +0100 Subject: mod_mam: Add flag to session when it performs a MAM query --- plugins/mod_mam/mod_mam.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/mod_mam/mod_mam.lua b/plugins/mod_mam/mod_mam.lua index 8900a2be..b500770f 100644 --- a/plugins/mod_mam/mod_mam.lua +++ b/plugins/mod_mam/mod_mam.lua @@ -90,6 +90,8 @@ module:hook("iq-set/self/"..xmlns_mam..":query", function(event) local query = stanza.tags[1]; local qid = query.attr.queryid; + origin.mam_requested = true; + get_prefs(origin.username, true); -- Search query parameters -- cgit v1.2.3 From cd2d1e87d66d6a014dec2a6e6da199fc2fd3398d Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 20 Oct 2019 01:20:11 +0200 Subject: MUC: Don't advertise registration feature on host JID (fixes #1451) There is currently no mention in XEP-0045 of how or where to advertise support for registration. Advertising on the host JID may be confusable with service-wide registration, as implemented in ejabberd. A common and sensible pattern in XMPP is that a feature is advertised on the JID where the service is available. --- plugins/muc/register.lib.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/muc/register.lib.lua b/plugins/muc/register.lib.lua index 21cb3f2f..95ed1a84 100644 --- a/plugins/muc/register.lib.lua +++ b/plugins/muc/register.lib.lua @@ -48,8 +48,6 @@ module:hook("muc-set-affiliation", function (event) event.room._reserved_nicks = nil; end); -module:add_feature("jabber:iq:register"); - module:hook("muc-disco#info", function (event) event.reply:tag("feature", { var = "jabber:iq:register" }):up(); end); -- cgit v1.2.3 From 2a7b5fee5b95d8b1f4ce0f597dae658411ffb303 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 20 Oct 2019 20:52:14 +0200 Subject: util.interpolation: Support unescaped variables with more modifiers (fixes #1452) Tests will be added in trunk. --- util/interpolation.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/util/interpolation.lua b/util/interpolation.lua index 315cc203..e0ccf47b 100644 --- a/util/interpolation.lua +++ b/util/interpolation.lua @@ -32,7 +32,7 @@ local function new_render(pat, escape, funcs) -- assert(type(values) == "table", "bad argument #2 to 'render' (table expected)"); return (s_gsub(template, pat, function (block) block = s_sub(block, 2, -2); - local name, opt, e = s_match(block, "^([%a_][%w_.]*)(%p?)()"); + local name, raw, opt, e = s_match(block, "^([%a_][%w_.]*)(!?)(%p?)()"); if not name then return end local value = values[name]; if not value and name:find(".", 2, true) then @@ -45,7 +45,7 @@ local function new_render(pat, escape, funcs) if funcs then while value ~= nil and opt == '|' do local f; - f, opt, e = s_match(block, "^([%a_][%w_.]*)(%p?)()", e); + f, raw, opt, e = s_match(block, "^([%a_][%w_.]*)(!?)(%p?)()", e); f = funcs[f]; if f then value = f(value); end end @@ -70,7 +70,7 @@ local function new_render(pat, escape, funcs) if type(value) ~= "string" then value = tostring(value); end - if opt ~= '!' then + if raw ~= '!' then return escape(value); end return value; -- cgit v1.2.3 From ce96d43cab1c757fd68ee6cfc19ad71c90c9e298 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 2 Nov 2019 19:24:26 +0100 Subject: net.resolvers: Apply IDNA conversion to ascii for DNS lookups (fixes #1426) --- net/resolvers/basic.lua | 3 ++- net/resolvers/service.lua | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/net/resolvers/basic.lua b/net/resolvers/basic.lua index 9a3c9952..d8031a6b 100644 --- a/net/resolvers/basic.lua +++ b/net/resolvers/basic.lua @@ -1,5 +1,6 @@ local adns = require "net.adns"; local inet_pton = require "util.net".pton; +local idna_to_ascii = require "util.encodings".idna.to_ascii; local methods = {}; local resolver_mt = { __index = methods }; @@ -59,7 +60,7 @@ end local function new(hostname, port, conn_type, extra) return setmetatable({ - hostname = hostname; + hostname = idna_to_ascii(hostname); port = port; conn_type = conn_type or "tcp"; extra = extra; diff --git a/net/resolvers/service.lua b/net/resolvers/service.lua index b5a2d821..a76adb73 100644 --- a/net/resolvers/service.lua +++ b/net/resolvers/service.lua @@ -1,5 +1,6 @@ local adns = require "net.adns"; local basic = require "net.resolvers.basic"; +local idna_to_ascii = require "util.encodings".idna.to_ascii; local methods = {}; local resolver_mt = { __index = methods }; @@ -58,7 +59,7 @@ end local function new(hostname, service, conn_type, extra) return setmetatable({ - hostname = hostname; + hostname = idna_to_ascii(hostname); service = service; conn_type = conn_type or "tcp"; extra = extra; -- cgit v1.2.3 From 6834e9cd6078109b32a4606710979a5b502903ed Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 2 Nov 2019 19:36:19 +0100 Subject: net.resolvers: Abort on hostnames not passing IDNA validation Prevents error on trying to use nil. Needs better error reporting in the future. --- net/resolvers/basic.lua | 5 +++++ net/resolvers/service.lua | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/net/resolvers/basic.lua b/net/resolvers/basic.lua index d8031a6b..cafef58b 100644 --- a/net/resolvers/basic.lua +++ b/net/resolvers/basic.lua @@ -18,6 +18,11 @@ function methods:next(cb) return; end + if not self.hostname then + -- FIXME report IDNA error + cb(nil); + end + local targets = {}; local n = 2; local function ready() diff --git a/net/resolvers/service.lua b/net/resolvers/service.lua index a76adb73..bb255c33 100644 --- a/net/resolvers/service.lua +++ b/net/resolvers/service.lua @@ -25,6 +25,11 @@ function methods:next(cb) return; end + if not self.hostname then + -- FIXME report IDNA error + cb(nil); + end + local targets = {}; local function ready() self.targets = targets; -- cgit v1.2.3 From d3ec2e6fef757bdf9d40a4412945d9daae3142b4 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 3 Nov 2019 22:19:09 +0100 Subject: util.startup: Update config path (fixes #1430) --- util/startup.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/util/startup.lua b/util/startup.lua index c101c290..82d0804d 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -33,7 +33,8 @@ function startup.read_config() if file then file:close(); prosody.config_file = filename; - CFG_CONFIGDIR = filename:match("^(.*)[\\/][^\\/]*$"); -- luacheck: ignore 111 + prosody.paths.config = filename:match("^(.*)[\\/][^\\/]*$"); + CFG_CONFIGDIR = prosody.paths.config; -- luacheck: ignore 111 break; end end -- cgit v1.2.3 From 9b5d4cf8750c90b6eb90d985d98807b63910c7d3 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 4 Nov 2019 00:29:49 +0100 Subject: util.startup: Ensure prosody.paths are absolute (see #1430) Normally these paths are injected into the installed 'prosody' executable as absolute paths, but it is possible to override at least the config path via environment variable or command line argument. This makes sure a path relative to pwd stays relative to that instead of the data directory. --- util/startup.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/util/startup.lua b/util/startup.lua index 82d0804d..e88ed709 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -231,8 +231,14 @@ end function startup.chdir() if prosody.installed then + local lfs = require "lfs"; + -- Ensure paths are absolute, not relative to the working directory which we're about to change + local cwd = lfs.currentdir(); + prosody.paths.source = config.resolve_relative_path(cwd, prosody.paths.source); + prosody.paths.config = config.resolve_relative_path(cwd, prosody.paths.config); + prosody.paths.data = config.resolve_relative_path(cwd, prosody.paths.data); -- Change working directory to data path. - require "lfs".chdir(prosody.paths.data); + lfs.chdir(prosody.paths.data); end end -- cgit v1.2.3 From f86ea56d52295dea0867707cf163d9f3d8f4acc5 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 8 Nov 2019 00:54:56 +0100 Subject: net.resolvers: Fix traceback from hostname failing IDNA Related to #1426 --- net/resolvers/basic.lua | 1 + net/resolvers/service.lua | 1 + 2 files changed, 2 insertions(+) diff --git a/net/resolvers/basic.lua b/net/resolvers/basic.lua index cafef58b..f37e74a2 100644 --- a/net/resolvers/basic.lua +++ b/net/resolvers/basic.lua @@ -21,6 +21,7 @@ function methods:next(cb) if not self.hostname then -- FIXME report IDNA error cb(nil); + return; end local targets = {}; diff --git a/net/resolvers/service.lua b/net/resolvers/service.lua index bb255c33..34f14cba 100644 --- a/net/resolvers/service.lua +++ b/net/resolvers/service.lua @@ -28,6 +28,7 @@ function methods:next(cb) if not self.hostname then -- FIXME report IDNA error cb(nil); + return; end local targets = {}; -- cgit v1.2.3 From c62c176abe6ca5e33c25cdebcb7d1457486ce7ca Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 16 Nov 2019 16:45:33 +0100 Subject: util.pposix: Avoid overflow of malloc info at 2GB (fixes #1445 until 4GB) --- util-src/pposix.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/util-src/pposix.c b/util-src/pposix.c index 5c926603..004f61a6 100644 --- a/util-src/pposix.c +++ b/util-src/pposix.c @@ -721,20 +721,20 @@ int lc_meminfo(lua_State *L) { struct mallinfo info = mallinfo(); lua_createtable(L, 0, 5); /* This is the total size of memory allocated with sbrk by malloc, in bytes. */ - lua_pushinteger(L, info.arena); + lua_pushinteger(L, (unsigned)info.arena); lua_setfield(L, -2, "allocated"); /* This is the total size of memory allocated with mmap, in bytes. */ - lua_pushinteger(L, info.hblkhd); + lua_pushinteger(L, (unsigned)info.hblkhd); lua_setfield(L, -2, "allocated_mmap"); /* This is the total size of memory occupied by chunks handed out by malloc. */ - lua_pushinteger(L, info.uordblks); + lua_pushinteger(L, (unsigned)info.uordblks); lua_setfield(L, -2, "used"); /* This is the total size of memory occupied by free (not in use) chunks. */ - lua_pushinteger(L, info.fordblks); + lua_pushinteger(L, (unsigned)info.fordblks); lua_setfield(L, -2, "unused"); /* This is the size of the top-most releasable chunk that normally borders the end of the heap (i.e., the high end of the virtual address space's data segment). */ - lua_pushinteger(L, info.keepcost); + lua_pushinteger(L, (unsigned)info.keepcost); lua_setfield(L, -2, "returnable"); return 1; } -- cgit v1.2.3 From 94f8d57fe4b3428973f0ba7b990915aa3abe3f7a Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 23 Nov 2019 01:05:28 +0100 Subject: MUC: Keep role across nickname change (fixes #1466) --- plugins/muc/muc.lib.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index cf9b46f7..639ecc38 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -524,6 +524,9 @@ function room_mt:handle_normal_presence(origin, stanza) log("debug", "no occupant found for %s; creating new occupant object for %s", dest_jid, real_jid); is_first_dest_session = true; dest_occupant = self:new_occupant(bare_jid, dest_jid); + if orig_occupant then + dest_occupant.role = orig_occupant.role; + end else is_first_dest_session = false; end -- cgit v1.2.3 From 5c649dff87ecc21140bf07ed511e10b5f93417d1 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 23 Nov 2019 23:10:39 +0100 Subject: prosodyctl check: Warn about conflict between mod_vcard and mod_vcard_legacy (#1469) --- prosodyctl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/prosodyctl b/prosodyctl index 405ef1f8..a732a73e 100755 --- a/prosodyctl +++ b/prosodyctl @@ -933,6 +933,10 @@ function commands.check(arg) 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(" Both mod_vcard_legacy and mod_vcard 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(""); -- cgit v1.2.3 From 7115b16bc42606dc1e33cfe313fa0592edc40637 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 23 Nov 2019 23:11:03 +0100 Subject: core.modulemanager: Disable mod_vcard if mod_vcard_legacy is enabled to prevent conflict (#1469) --- core/modulemanager.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/modulemanager.lua b/core/modulemanager.lua index 17602459..a824d36a 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -63,6 +63,11 @@ local function get_modules_for_host(host) modules:add("admin_telnet"); end + if modules:contains("vcard") and modules:contains("vcard_legacy") then + log("error", "The mod_vcard_legacy plugin replaces mod_vcard but both are enabled. Please update your config."); + modules:remove("vcard"); + end + return modules, component; end -- cgit v1.2.3 From a286ed0a57ae9b2f0cceb0fc0756a8db95708cc4 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 24 Nov 2019 04:23:51 +0100 Subject: net.resolvers.basic: Move IP literal check to constructor This is to prepare for fixing #1459. An IPv6 literal in [ ] brackets does not pass IDNA and resolving it fails there. --- net/resolvers/basic.lua | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/net/resolvers/basic.lua b/net/resolvers/basic.lua index f37e74a2..e3a94382 100644 --- a/net/resolvers/basic.lua +++ b/net/resolvers/basic.lua @@ -33,16 +33,6 @@ function methods:next(cb) self:next(cb); end - local is_ip = inet_pton(self.hostname); - if is_ip then - if #is_ip == 16 then - cb(self.conn_type.."6", self.hostname, self.port, self.extra); - elseif #is_ip == 4 then - cb(self.conn_type.."4", self.hostname, self.port, self.extra); - end - return; - end - -- Resolve DNS to target list local dns_resolver = adns.resolver(); dns_resolver:lookup(function (answer) @@ -65,11 +55,24 @@ function methods:next(cb) end local function new(hostname, port, conn_type, extra) + local ascii_host = idna_to_ascii(hostname); + local targets = nil; + + local is_ip = inet_pton(hostname); + if is_ip then + if #is_ip == 16 then + targets = { { conn_type.."6", hostname, port, extra } }; + elseif #is_ip == 4 then + targets = { { conn_type.."4", hostname, port, extra } }; + end + end + return setmetatable({ - hostname = idna_to_ascii(hostname); + hostname = ascii_host; port = port; conn_type = conn_type or "tcp"; extra = extra; + targets = targets; }, resolver_mt); end -- cgit v1.2.3 From 50137dbaf592e69bf9d06ae5398c6c0ff14479fe Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 24 Nov 2019 04:26:44 +0100 Subject: net.resolvers.basic: Fix resolution of IPv6 literals (in brackets) (fixes #1459) --- net/resolvers/basic.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/resolvers/basic.lua b/net/resolvers/basic.lua index e3a94382..93182fda 100644 --- a/net/resolvers/basic.lua +++ b/net/resolvers/basic.lua @@ -59,6 +59,9 @@ local function new(hostname, port, conn_type, extra) local targets = nil; local is_ip = inet_pton(hostname); + if not is_ip and hostname:sub(1,1) == '[' then + is_ip = inet_pton(hostname:sub(2,-2)); + end if is_ip then if #is_ip == 16 then targets = { { conn_type.."6", hostname, port, extra } }; -- cgit v1.2.3 From 1c60c932b45c6d2866249ba047df4c73aa4992af Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 24 Nov 2019 04:41:07 +0100 Subject: net.resolvers.basic: Normalise IP literals, ensures net.server is happy --- net/resolvers/basic.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/resolvers/basic.lua b/net/resolvers/basic.lua index 93182fda..08c71ef5 100644 --- a/net/resolvers/basic.lua +++ b/net/resolvers/basic.lua @@ -1,5 +1,6 @@ local adns = require "net.adns"; local inet_pton = require "util.net".pton; +local inet_ntop = require "util.net".ntop; local idna_to_ascii = require "util.encodings".idna.to_ascii; local methods = {}; @@ -63,6 +64,7 @@ local function new(hostname, port, conn_type, extra) is_ip = inet_pton(hostname:sub(2,-2)); end if is_ip then + hostname = inet_ntop(is_ip); if #is_ip == 16 then targets = { { conn_type.."6", hostname, port, extra } }; elseif #is_ip == 4 then -- cgit v1.2.3 From 333526a6858a4076568bb134269c9c09c7211fe1 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 6 Jun 2019 17:37:15 +0200 Subject: util.pubsub: Factor out calling of broadcaster This will simplify doing things along with broadcasting. --- util/pubsub.lua | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/util/pubsub.lua b/util/pubsub.lua index e5e0cb7c..24f1f41d 100644 --- a/util/pubsub.lua +++ b/util/pubsub.lua @@ -499,7 +499,7 @@ function service:delete(node, actor) --> ok, err end self.events.fire_event("node-deleted", { service = self, node = node, actor = actor }); - self.config.broadcaster("delete", node, node_obj.subscribers, nil, actor, node_obj, self); + self:broadcast("delete", node, node_obj.subscribers, nil, actor, node_obj); return true; end @@ -562,10 +562,14 @@ function service:publish(node, actor, id, item, requested_config) --> ok, err local event_data = { service = self, node = node, actor = actor, id = id, item = item }; self.events.fire_event("item-published/"..node, event_data); self.events.fire_event("item-published", event_data); - self.config.broadcaster("items", node, node_obj.subscribers, item, actor, node_obj, self); + self:broadcast("items", node, node_obj.subscribers, item, actor, node_obj); return true; end +function service:broadcast(event, node, subscribers, item, actor, node_obj) + return self.config.broadcaster(event, node, subscribers, item, actor, node_obj, self); +end + function service:retract(node, actor, id, retract) --> ok, err -- Access checking if not self:may(node, actor, "retract") then @@ -582,7 +586,7 @@ function service:retract(node, actor, id, retract) --> ok, err end self.events.fire_event("item-retracted", { service = self, node = node, actor = actor, id = id }); if retract then - self.config.broadcaster("retract", node, node_obj.subscribers, retract, actor, node_obj, self); + self:broadcast("retract", node, node_obj.subscribers, retract, actor, node_obj); end return true end @@ -604,7 +608,7 @@ function service:purge(node, actor, notify) --> ok, err end self.events.fire_event("node-purged", { service = self, node = node, actor = actor }); if notify then - self.config.broadcaster("purge", node, node_obj.subscribers, nil, actor, node_obj, self); + self:broadcast("purge", node, node_obj.subscribers, nil, actor, node_obj); end return true end -- cgit v1.2.3 From 1bb215571dedbbc3acebca304a8055b7d6514f41 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 6 Jun 2019 18:13:46 +0200 Subject: util.pubsub: Pass subscribers trough a filter callback --- util/pubsub.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/pubsub.lua b/util/pubsub.lua index 24f1f41d..1674b9a7 100644 --- a/util/pubsub.lua +++ b/util/pubsub.lua @@ -6,6 +6,7 @@ local service_mt = {}; local default_config = { itemstore = function (config, _) return cache.new(config["max_items"]) end; broadcaster = function () end; + subscriber_filter = function (subs) return subs end; itemcheck = function () return true; end; get_affiliation = function () end; normalize_jid = function (jid) return jid; end; @@ -567,6 +568,7 @@ function service:publish(node, actor, id, item, requested_config) --> ok, err end function service:broadcast(event, node, subscribers, item, actor, node_obj) + subscribers = self.config.subscriber_filter(subscribers, node, event); return self.config.broadcaster(event, node, subscribers, item, actor, node_obj, self); end -- cgit v1.2.3 From 4bdf3f20a96aa1734faafacb17328853956cacbd Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 10 Jun 2019 13:57:09 +0200 Subject: mod_pep: Handle presence subscriptions in filter (fixes #1372) Take two on 045209b41b3a --- plugins/mod_pep.lua | 51 +++++++++++++++++---------------------------------- 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/plugins/mod_pep.lua b/plugins/mod_pep.lua index 7a4aac2b..12be41a2 100644 --- a/plugins/mod_pep.lua +++ b/plugins/mod_pep.lua @@ -90,21 +90,6 @@ local function nodestore(username) return data, err; end function store:set(node, data) - if data then - -- Save the data without subscriptions - local subscribers = {}; - for jid, sub in pairs(data.subscribers) do - if type(sub) ~= "table" or not sub.presence then - subscribers[jid] = sub; - end - end - data = { - name = data.name; - config = data.config; - affiliations = data.affiliations; - subscribers = subscribers; - }; - end return node_config:set(username, node, data); end function store:users() @@ -160,18 +145,23 @@ local function get_broadcaster(username) return simple_broadcast; end -local function on_node_creation(event) - local service = event.service; - local node = event.node; - local username = service.config.pep_username; - - local service_recipients = recipients[username]; - if not service_recipients then return; end +local function get_subscriber_filter(username) + return function (jids, node) + local broadcast_to = {}; + for jid, opts in pairs(jids) do + broadcast_to[jid] = opts; + end - for recipient, nodes in pairs(service_recipients) do - if nodes:contains(node) then - service:add_subscription(node, recipient, recipient, { presence = true }); + local service_recipients = recipients[username]; + if service_recipients then + local service = services[username]; + for recipient, nodes in pairs(service_recipients) do + if nodes:contains(node) and service:may(node, recipient, "subscribe") then + broadcast_to[recipient] = true; + end + end end + return broadcast_to; end end @@ -196,6 +186,7 @@ function get_pep_service(username) nodestore = nodestore(username); itemstore = simple_itemstore(username); broadcaster = get_broadcaster(username); + subscriber_filter = get_subscriber_filter(username); itemcheck = is_item_stanza; get_affiliation = function (jid) if jid_bare(jid) == user_bare then @@ -233,11 +224,6 @@ function get_pep_service(username) return service; end -module:hook("item-added/pep-service", function (event) - local service = event.item.service; - module:hook_object_event(service.events, "node-created", on_node_creation); -end); - function handle_pubsub_iq(event) local origin, stanza = event.origin, event.stanza; local service_name = origin.username; @@ -303,12 +289,9 @@ local function update_subscriptions(recipient, service_name, nodes) end local service = get_pep_service(service_name); - for node in current - nodes do - service:remove_subscription(node, recipient, recipient); - end for node in nodes - current do - if service:add_subscription(node, recipient, recipient, { presence = true }) then + if service:may(node, recipient, "subscribe") then resend_last_item(recipient, node, service); end end -- cgit v1.2.3 From 097d7ca6638721a97bae4a92d1cdb3a41a05acdd Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 23 Dec 2019 22:42:39 +0100 Subject: mod_auth_internal_hashed: Pass on errors from password hash function (fixes #1477) --- plugins/mod_auth_internal_hashed.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/mod_auth_internal_hashed.lua b/plugins/mod_auth_internal_hashed.lua index 35764afb..083f648b 100644 --- a/plugins/mod_auth_internal_hashed.lua +++ b/plugins/mod_auth_internal_hashed.lua @@ -68,6 +68,9 @@ function provider.set_password(username, password) account.salt = generate_uuid(); account.iteration_count = max(account.iteration_count or 0, default_iteration_count); local valid, stored_key, server_key = getAuthenticationDatabaseSHA1(password, account.salt, account.iteration_count); + if not valid then + return valid, stored_key; + end local stored_key_hex = to_hex(stored_key); local server_key_hex = to_hex(server_key); @@ -99,6 +102,9 @@ function provider.create_user(username, password) end local salt = generate_uuid(); local valid, stored_key, server_key = getAuthenticationDatabaseSHA1(password, salt, default_iteration_count); + if not valid then + return valid, stored_key; + end local stored_key_hex = to_hex(stored_key); local server_key_hex = to_hex(server_key); return accounts:set(username, { -- cgit v1.2.3 From fa6f88cf3e2a5dc5ce939fdff4e7d1b1d74cf763 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 23 Dec 2019 23:18:39 +0100 Subject: mod_mam: Log error when unable to store stanza (fix #1478) Unclear what else to do here. Caught by luacheck --- plugins/mod_mam/mod_mam.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/mod_mam/mod_mam.lua b/plugins/mod_mam/mod_mam.lua index b500770f..2443270f 100644 --- a/plugins/mod_mam/mod_mam.lua +++ b/plugins/mod_mam/mod_mam.lua @@ -305,6 +305,8 @@ local function message_handler(event, c2s) event.stanza = clone_for_other_handlers; schedule_cleanup(store_user); module:fire_event("archive-message-added", { origin = origin, stanza = clone_for_storage, for_user = store_user, id = id }); + else + log("error", "Could not archive stanza: %s", err); end else log("debug", "Not archiving stanza: %s (prefs)", stanza:top_tag()); -- cgit v1.2.3 From 04965a84f1315221395442e70f8692158ffe2327 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 24 Dec 2019 00:27:41 +0100 Subject: mod_mam: Log error when unable to delete old messages (fix #1479) [luacheck] --- plugins/mod_mam/mod_mam.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/mod_mam/mod_mam.lua b/plugins/mod_mam/mod_mam.lua index 2443270f..df5d925a 100644 --- a/plugins/mod_mam/mod_mam.lua +++ b/plugins/mod_mam/mod_mam.lua @@ -387,6 +387,8 @@ if cleanup_after ~= "never" then if ok then num_users = num_users + 1; sum = sum + (tonumber(ok) or 0); + else + module:log("error", "Could not delete messages for user '%s': %s", user, err); end end module:log("info", "Deleted %d expired messages for %d users", sum, num_users); -- cgit v1.2.3 From 62d5620fdb5686e2fb2ae6a2adb4ec7ef3c2b49e Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 24 Dec 2019 00:27:44 +0100 Subject: mod_mam: Schedule cleanup again if unable to delete messages --- plugins/mod_mam/mod_mam.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/mod_mam/mod_mam.lua b/plugins/mod_mam/mod_mam.lua index df5d925a..fd319486 100644 --- a/plugins/mod_mam/mod_mam.lua +++ b/plugins/mod_mam/mod_mam.lua @@ -388,6 +388,7 @@ if cleanup_after ~= "never" then num_users = num_users + 1; sum = sum + (tonumber(ok) or 0); else + cleanup_map:set(cut_off, user, true); module:log("error", "Could not delete messages for user '%s': %s", user, err); end end -- cgit v1.2.3 From 4e230b95efa6414813830e1371da5bff3de5c169 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 23 Dec 2019 23:32:49 +0100 Subject: mod_muc_mam: Log error when unable to store stanza (fix #1480) [luacheck] --- plugins/mod_muc_mam.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/mod_muc_mam.lua b/plugins/mod_muc_mam.lua index a2e3f81b..37e41157 100644 --- a/plugins/mod_muc_mam.lua +++ b/plugins/mod_muc_mam.lua @@ -357,6 +357,8 @@ local function save_to_history(self, stanza) if id then schedule_cleanup(room_node); stanza:add_direct_child(st.stanza("stanza-id", { xmlns = xmlns_st_id, by = self.jid, id = id })); + else + module:log("error", "Could not archive stanza: %s", err); end end -- cgit v1.2.3 From 20d4a9b614d36aa41a446784cf40d08e965498b2 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 24 Dec 2019 00:26:37 +0100 Subject: mod_muc_mam: Log error when unable to delete old messages (fix #1481) [luacheck] --- plugins/mod_muc_mam.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/mod_muc_mam.lua b/plugins/mod_muc_mam.lua index 37e41157..c6104757 100644 --- a/plugins/mod_muc_mam.lua +++ b/plugins/mod_muc_mam.lua @@ -459,6 +459,8 @@ if cleanup_after ~= "never" then if ok then num_rooms = num_rooms + 1; sum = sum + (tonumber(ok) or 0); + else + module:log("error", "Could not delete messages for room '%s': %s", room, err); end end module:log("info", "Deleted %d expired messages for %d rooms", sum, num_rooms); -- cgit v1.2.3 From ef1a12de07bec2a01242dd53f6157ec75b3f9637 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 24 Dec 2019 00:26:40 +0100 Subject: mod_muc_mam: Schedule cleanup again on failure --- plugins/mod_muc_mam.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/mod_muc_mam.lua b/plugins/mod_muc_mam.lua index c6104757..a9287224 100644 --- a/plugins/mod_muc_mam.lua +++ b/plugins/mod_muc_mam.lua @@ -460,6 +460,7 @@ if cleanup_after ~= "never" then num_rooms = num_rooms + 1; sum = sum + (tonumber(ok) or 0); else + cleanup_map:set(cut_off, room, true); module:log("error", "Could not delete messages for room '%s': %s", room, err); end end -- cgit v1.2.3 From 271036a6a84cc469c8aa37dddedad47e101c67bc Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 24 Dec 2019 01:00:35 +0100 Subject: mod_muc_mam: Fix rebase mistake [luacheck] The 'err' was there in trunk --- plugins/mod_muc_mam.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_muc_mam.lua b/plugins/mod_muc_mam.lua index a9287224..2ce5e1b5 100644 --- a/plugins/mod_muc_mam.lua +++ b/plugins/mod_muc_mam.lua @@ -352,7 +352,7 @@ local function save_to_history(self, stanza) end -- And stash it - local id = archive:append(room_node, nil, stored_stanza, time_now(), with); + local id, err = archive:append(room_node, nil, stored_stanza, time_now(), with); if id then schedule_cleanup(room_node); -- cgit v1.2.3 From 453e74c0602cff3007ec0837c02ca1245e94236b Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 24 Dec 2019 01:01:05 +0100 Subject: mod_mam: Fix rebase mistake [luacheck] The 'err' was there in trunk --- plugins/mod_mam/mod_mam.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_mam/mod_mam.lua b/plugins/mod_mam/mod_mam.lua index fd319486..295d90e1 100644 --- a/plugins/mod_mam/mod_mam.lua +++ b/plugins/mod_mam/mod_mam.lua @@ -297,7 +297,7 @@ local function message_handler(event, c2s) log("debug", "Archiving stanza: %s", stanza:top_tag()); -- And stash it - local ok = archive:append(store_user, nil, clone_for_storage, time_now(), with); + local ok, err = archive:append(store_user, nil, clone_for_storage, time_now(), with); if ok then local clone_for_other_handlers = st.clone(stanza); local id = ok; -- cgit v1.2.3 From 9bb36ab1f100f4f43307eccb030fca839dc115f0 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 16 Dec 2018 02:56:11 +0100 Subject: core.rostermanager: Cache rosters of offline users for faster access (fixes #1233) (grafted from 42a3e3a2824822cef7640ac56d182c59bdd4224e) --- core/rostermanager.lua | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/core/rostermanager.lua b/core/rostermanager.lua index 61b08002..7bfad0a0 100644 --- a/core/rostermanager.lua +++ b/core/rostermanager.lua @@ -12,6 +12,7 @@ local log = require "util.logger".init("rostermanager"); local new_id = require "util.id".short; +local new_cache = require "util.cache".new; local pairs = pairs; local tostring = tostring; @@ -111,6 +112,23 @@ local function load_roster(username, host) else -- Attempt to load roster for non-loaded user log("debug", "load_roster: loading for offline user: %s", jid); end + local roster_cache = hosts[host] and hosts[host].roster_cache; + if not roster_cache then + if hosts[host] then + roster_cache = new_cache(1024); + hosts[host].roster_cache = roster_cache; + end + else + roster = roster_cache:get(jid); + if roster then + log("debug", "load_roster: cache hit"); + roster_cache:set(jid, roster); + if user then user.roster = roster; end + return roster; + else + log("debug", "load_roster: cache miss, loading from storage"); + end + end local roster_store = storagemanager.open(host, "roster", "keyval"); local data, err = roster_store:get(username); roster = data or {}; @@ -134,6 +152,10 @@ local function load_roster(username, host) if not err then hosts[host].events.fire_event("roster-load", { username = username, host = host, roster = roster }); end + if roster_cache and not user then + log("debug", "load_roster: caching loaded roster"); + roster_cache:set(jid, roster); + end return roster, err; end -- cgit v1.2.3 From cd4fcca5aa16fedd7a5cbe42cd6e78ccade1ec54 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 23 Jan 2020 21:43:36 +0000 Subject: Log warning when using prosodyctl start/stop/restart --- prosodyctl | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/prosodyctl b/prosodyctl index 982b102e..b16d9230 100755 --- a/prosodyctl +++ b/prosodyctl @@ -198,11 +198,25 @@ function commands.deluser(arg) return 1; end +local function service_command_warning(command) + if prosody.installed and configmanager.get("*", "prosodyctl_service_warnings") ~= false then + show_warning("WARNING: Use of prosodyctl start/stop/restart/reload is not recommended"); + show_warning(" if Prosody is managed by an init system - use that directly instead."); + if lfs.attributes("/etc/systemd") then + show_warning(" e.g. systemctl %s prosody", command); + elseif lfs.attributes("/etc/init.d/prosody") then + show_warning(" e.g. /etc/init.d/prosody %s", command); + end + show_warning(""); + end +end + function commands.start(arg) if arg[1] == "--help" then show_usage([[start]], [[Start Prosody]]); return 1; end + service_command_warning("start"); local ok, ret = prosodyctl.isrunning(); if not ok then show_message(error_messages[ret]); @@ -293,6 +307,8 @@ function commands.stop(arg) return 1; end + service_command_warning("stop"); + if not prosodyctl.isrunning() then show_message("Prosody is not running"); return 1; @@ -328,6 +344,8 @@ function commands.restart(arg) return 1; end + service_command_warning("restart"); + commands.stop(arg); return commands.start(arg); end @@ -421,6 +439,8 @@ function commands.reload(arg) return 1; end + service_command_warning("reload"); + if not prosodyctl.isrunning() then show_message("Prosody is not running"); return 1; -- cgit v1.2.3 From 8a032412450f7dabaed77359dac45d4450662951 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 23 Jan 2020 21:59:13 +0000 Subject: prosodyctl: Fix some luacheck warnings --- prosodyctl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/prosodyctl b/prosodyctl index b16d9230..028e08d8 100755 --- a/prosodyctl +++ b/prosodyctl @@ -69,6 +69,7 @@ local modulemanager = require "core.modulemanager" local prosodyctl = require "util.prosodyctl" local socket = require "socket" local dependencies = require "util.dependencies"; +local lfs = dependencies.softreq "lfs"; ----------------------- @@ -198,14 +199,15 @@ function commands.deluser(arg) return 1; end -local function service_command_warning(command) +local function service_command_warning(service_command) if prosody.installed and configmanager.get("*", "prosodyctl_service_warnings") ~= false then show_warning("WARNING: Use of prosodyctl start/stop/restart/reload is not recommended"); show_warning(" if Prosody is managed by an init system - use that directly instead."); + lfs = lfs or require if lfs.attributes("/etc/systemd") then - show_warning(" e.g. systemctl %s prosody", command); + show_warning(" e.g. systemctl %s prosody", service_command); elseif lfs.attributes("/etc/init.d/prosody") then - show_warning(" e.g. /etc/init.d/prosody %s", command); + show_warning(" e.g. /etc/init.d/prosody %s", service_command); end show_warning(""); end @@ -512,7 +514,6 @@ function commands.unregister(arg) end local openssl; -local lfs; local cert_commands = {}; -- cgit v1.2.3 From 70e9ff3887f78b7ce8ea5487c5c7b9bdc128a7d9 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 24 Jan 2020 16:21:30 +0000 Subject: mod_websocket: Fire event on session creation (thanks Aaron van Meerten) --- plugins/mod_websocket.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/mod_websocket.lua b/plugins/mod_websocket.lua index 6625c676..177259e6 100644 --- a/plugins/mod_websocket.lua +++ b/plugins/mod_websocket.lua @@ -305,6 +305,8 @@ function handle_request(event) response.headers.sec_webSocket_accept = base64(sha1(request.headers.sec_websocket_key .. "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")); response.headers.sec_webSocket_protocol = "xmpp"; + module:fire_event("websocket-session", { session = session, request = request }); + session.log("debug", "Sending WebSocket handshake"); return ""; -- cgit v1.2.3 From c17d65758ed1b357c1b3cf2a8e4af773f301c2cd Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 24 Jan 2020 23:27:49 +0100 Subject: mod_admin_telnet: Create a DNS resolver per console session (fixes #1492) This is now the common pattern, eg see mod_s2s. --- plugins/mod_admin_telnet.lua | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/plugins/mod_admin_telnet.lua b/plugins/mod_admin_telnet.lua index 1cbe27a4..59eca28b 100644 --- a/plugins/mod_admin_telnet.lua +++ b/plugins/mod_admin_telnet.lua @@ -1074,36 +1074,49 @@ end def_env.dns = {}; local adns = require"net.adns"; -local dns = require"net.dns"; + +local function get_resolver(session) + local resolver = session.dns_resolver; + if not resolver then + resolver = adns.resolver(); + session.dns_resolver = resolver; + end + return resolver; +end function def_env.dns:lookup(name, typ, class) + local resolver = get_resolver(self.session); local ret = "Query sent"; local print = self.session.print; local function handler(...) ret = "Got response"; print(...); end - adns.lookup(handler, name, typ, class); + resolver:lookup(handler, name, typ, class); return true, ret; end function def_env.dns:addnameserver(...) - dns._resolver:addnameserver(...) + local resolver = get_resolver(self.session); + resolver._resolver:addnameserver(...) return true end function def_env.dns:setnameserver(...) - dns._resolver:setnameserver(...) + local resolver = get_resolver(self.session); + resolver._resolver:setnameserver(...) return true end function def_env.dns:purge() - dns.purge() + local resolver = get_resolver(self.session); + resolver._resolver:purge() return true end function def_env.dns:cache() - return true, "Cache:\n"..tostring(dns.cache()) + local resolver = get_resolver(self.session); + return true, "Cache:\n"..tostring(resolver._resolver.cache) end def_env.http = {}; -- cgit v1.2.3 From 239bd598467f2e4b2768a71a2b2fe708f34181a3 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 13 Feb 2020 14:27:12 +0000 Subject: mod_muc: Allow control over the server-admins-are-room-owners feature (see #1174) --- plugins/muc/mod_muc.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/muc/mod_muc.lua b/plugins/muc/mod_muc.lua index 954bae92..b9c7c859 100644 --- a/plugins/muc/mod_muc.lua +++ b/plugins/muc/mod_muc.lua @@ -104,7 +104,8 @@ local function is_admin(jid) return um_is_admin(jid, module.host); end -do -- Monkey patch to make server admins room owners +if module:get_option_boolean("component_admins_as_room_owners", true) then + -- Monkey patch to make server admins room owners local _get_affiliation = room_mt.get_affiliation; function room_mt:get_affiliation(jid) if is_admin(jid) then return "owner"; end -- cgit v1.2.3 From a8812b45ad23d58ba7a9da7d3b341d46bb006df4 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 21 Feb 2020 23:00:44 +0100 Subject: mod_storage_sql: Fix check for deletion limits (fixes #1494) The check was only performed if sql_manage_tables was set to true (the default) It should always be performed --- plugins/mod_storage_sql.lua | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/mod_storage_sql.lua b/plugins/mod_storage_sql.lua index a449091e..a9298138 100644 --- a/plugins/mod_storage_sql.lua +++ b/plugins/mod_storage_sql.lua @@ -622,15 +622,15 @@ function module.load() module:log("error", "Old database format detected. Please run: prosodyctl mod_%s upgrade", module.name); return false, "database upgrade needed"; end - if engine.params.driver == "SQLite3" then - for row in engine:select("PRAGMA compile_options") do - if row[1] == "ENABLE_UPDATE_DELETE_LIMIT" then - engine._have_delete_limit = true; - end - end - end end end); + if engine.params.driver == "SQLite3" then + for row in engine:select("PRAGMA compile_options") do + if row[1] == "ENABLE_UPDATE_DELETE_LIMIT" then + engine._have_delete_limit = true; + end + end + end engines[sql.db2uri(params)] = engine; end -- cgit v1.2.3 From e69d6a21b3a1e0af470eb7a88239b20d463e123c Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 22 Feb 2020 16:23:43 +0100 Subject: mod_storage_sql: Move update limit check back one step, fixes traceback 0c00274528a4 moved it one step too far, so the check was performed before connecting to the database instead of after. --- plugins/mod_storage_sql.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/mod_storage_sql.lua b/plugins/mod_storage_sql.lua index a9298138..73207073 100644 --- a/plugins/mod_storage_sql.lua +++ b/plugins/mod_storage_sql.lua @@ -623,14 +623,14 @@ function module.load() return false, "database upgrade needed"; end end - end); - if engine.params.driver == "SQLite3" then - for row in engine:select("PRAGMA compile_options") do - if row[1] == "ENABLE_UPDATE_DELETE_LIMIT" then - engine._have_delete_limit = true; + if engine.params.driver == "SQLite3" then + for row in engine:select("PRAGMA compile_options") do + if row[1] == "ENABLE_UPDATE_DELETE_LIMIT" then + engine._have_delete_limit = true; + end end end - end + end); engines[sql.db2uri(params)] = engine; end -- cgit v1.2.3 From 81325abb7a06ca38e273eef6b0db7f4750c82bcb Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 24 Feb 2020 23:16:08 +0100 Subject: util.datamanager: Fix iterating over "users" (thanks marc0s) The 'store' path componend used to be unescaped until 756a2a00e7e7 added escaping to address issues with characters like '/' used in PEP, but with a special case for '_' which was already in common use in 'store' path components. Missed adding this escaping here. --- util/datamanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/datamanager.lua b/util/datamanager.lua index cf96887b..0d7060b7 100644 --- a/util/datamanager.lua +++ b/util/datamanager.lua @@ -320,7 +320,7 @@ local type_map = { local function users(host, store, typ) -- luacheck: ignore 431/store typ = type_map[typ or "keyval"]; - local store_dir = format("%s/%s/%s", data_path, encode(host), store); + local store_dir = format("%s/%s/%s", data_path, encode(host), store_encode(store)); local mode, err = lfs.attributes(store_dir, "mode"); if not mode then -- cgit v1.2.3 From ea1ca4fd42978b1606f64dd3d32818f3ee8166a6 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 27 Feb 2020 19:42:51 +0100 Subject: mod_muc_mam: Fix traceback saving message from non-occupant (fixes #1497) Caused by a plugin sending a message from the room itself. --- plugins/mod_muc_mam.lua | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/plugins/mod_muc_mam.lua b/plugins/mod_muc_mam.lua index 2ce5e1b5..f7ea3136 100644 --- a/plugins/mod_muc_mam.lua +++ b/plugins/mod_muc_mam.lua @@ -335,11 +335,14 @@ local function save_to_history(self, stanza) if stanza.name == "message" and self:get_whois() == "anyone" then stored_stanza = st.clone(stanza); stored_stanza.attr.to = nil; - local actor = jid_bare(self._occupants[stanza.attr.from].jid); - local affiliation = self:get_affiliation(actor) or "none"; - local role = self:get_role(actor) or self:get_default_role(affiliation); - stored_stanza:add_direct_child(st.stanza("x", { xmlns = xmlns_muc_user }) - :tag("item", { affiliation = affiliation; role = role; jid = actor })); + local occupant = self._occupants[stanza.attr.from]; + if occupant then + local actor = jid_bare(occupant.jid); + local affiliation = self:get_affiliation(actor) or "none"; + local role = self:get_role(actor) or self:get_default_role(affiliation); + stored_stanza:add_direct_child(st.stanza("x", { xmlns = xmlns_muc_user }) + :tag("item", { affiliation = affiliation; role = role; jid = actor })); + end end -- Policy check -- cgit v1.2.3 From 3ab4eb0e103c29ab4e0f82ba6c28002052e5a0b8 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 27 Feb 2020 20:01:54 +0100 Subject: mod_pubsub, mod_pep: Ensure correct number of children of (fixes #1496) --- plugins/mod_pep.lua | 2 +- plugins/mod_pubsub/mod_pubsub.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/mod_pep.lua b/plugins/mod_pep.lua index 12be41a2..f8047a92 100644 --- a/plugins/mod_pep.lua +++ b/plugins/mod_pep.lua @@ -51,7 +51,7 @@ function module.restore(data) end function is_item_stanza(item) - return st.is_stanza(item) and item.attr.xmlns == xmlns_pubsub and item.name == "item"; + return st.is_stanza(item) and item.attr.xmlns == xmlns_pubsub and item.name == "item" and #item.tags == 1; end function check_node_config(node, actor, new_config) -- luacheck: ignore 212/node 212/actor diff --git a/plugins/mod_pubsub/mod_pubsub.lua b/plugins/mod_pubsub/mod_pubsub.lua index 855c5fd2..4276bc5e 100644 --- a/plugins/mod_pubsub/mod_pubsub.lua +++ b/plugins/mod_pubsub/mod_pubsub.lua @@ -112,7 +112,7 @@ function check_node_config(node, actor, new_config) -- luacheck: ignore 212/acto end function is_item_stanza(item) - return st.is_stanza(item) and item.attr.xmlns == xmlns_pubsub and item.name == "item"; + return st.is_stanza(item) and item.attr.xmlns == xmlns_pubsub and item.name == "item" and #item.tags == 1; end module:hook("pubsub-summary/http://www.w3.org/2005/Atom", function (event) -- cgit v1.2.3 From 66b1c21e0a25adb032f579a4334fe78c5aed29fe Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 11 Mar 2020 18:07:03 +0100 Subject: net.server_epoll: Fix indentation Some lines seem to have gotten the wrong indentation, possibly caused by Meld which often ignores lines with only whitespace changes and leaves their previous indentation. --- net/server_epoll.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/server_epoll.lua b/net/server_epoll.lua index 0c03ae15..2182d56a 100644 --- a/net/server_epoll.lua +++ b/net/server_epoll.lua @@ -102,7 +102,7 @@ local function runtimers(next_delay, min_wait) if peek > now then next_delay = peek - now; break; - end + end local _, timer, id = timers:pop(); local ok, ret = pcall(timer[2], now); @@ -110,10 +110,10 @@ local function runtimers(next_delay, min_wait) local next_time = now+ret; timer[1] = next_time; timers:insert(timer, next_time); - end + end peek = timers:peek(); - end + end if peek == nil then return next_delay; end -- cgit v1.2.3 From 02b23bdc6699dfcd5f14de1933a171b6415a64a5 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 11 Mar 2020 21:15:01 +0100 Subject: mod_mam,mod_muc_mam: Allow other work to be performed during archive cleanup (fixes #1504) This lets Prosody handle socket related work between each step in the cleanup in order to prevent the server from being completely blocked during this. An async storage backend would not need this but those are currently rare. --- plugins/mod_mam/mod_mam.lua | 6 +++++- plugins/mod_muc_mam.lua | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/plugins/mod_mam/mod_mam.lua b/plugins/mod_mam/mod_mam.lua index 295d90e1..e7d89a95 100644 --- a/plugins/mod_mam/mod_mam.lua +++ b/plugins/mod_mam/mod_mam.lua @@ -362,7 +362,8 @@ if cleanup_after ~= "never" then end end - cleanup_runner = require "util.async".runner(function () + local async = require "util.async"; + cleanup_runner = async.runner(function () local users = {}; local cut_off = datestamp(os.time() - cleanup_after); for date in cleanup_storage:users() do @@ -391,6 +392,9 @@ if cleanup_after ~= "never" then cleanup_map:set(cut_off, user, true); module:log("error", "Could not delete messages for user '%s': %s", user, err); end + local wait, done = async.waiter(); + module:add_timer(0.01, done); + wait(); end module:log("info", "Deleted %d expired messages for %d users", sum, num_users); end); diff --git a/plugins/mod_muc_mam.lua b/plugins/mod_muc_mam.lua index f7ea3136..5a01324d 100644 --- a/plugins/mod_muc_mam.lua +++ b/plugins/mod_muc_mam.lua @@ -437,7 +437,8 @@ if cleanup_after ~= "never" then end end - cleanup_runner = require "util.async".runner(function () + local async = require "util.async"; + cleanup_runner = async.runner(function () local rooms = {}; local cut_off = datestamp(os.time() - cleanup_after); for date in cleanup_storage:users() do @@ -466,6 +467,9 @@ if cleanup_after ~= "never" then cleanup_map:set(cut_off, room, true); module:log("error", "Could not delete messages for room '%s': %s", room, err); end + local wait, done = async.waiter(); + module:add_timer(0.01, done); + wait(); end module:log("info", "Deleted %d expired messages for %d rooms", sum, num_rooms); end); -- cgit v1.2.3 From e50a3fac0be9cee64746f88dda46989ec87e8e29 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 12 Mar 2020 20:32:07 +0000 Subject: MUC: Persist affiliation_data in new MUC format! --- plugins/muc/muc.lib.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 639ecc38..bc370da5 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -1449,6 +1449,7 @@ function room_mt:freeze(live) frozen = { _jid = self.jid; _data = self._data; + _affiliation_data = self._affiliation_data; }; for user, affiliation in pairs(self._affiliations) do frozen[user] = affiliation; -- cgit v1.2.3 From af11cae5114d8237c8f17be3a89482bc70cf42b8 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 22 Mar 2020 17:35:26 +0100 Subject: mod_admin_telnet: Handle unavailable cipher info (fixes #1510) The LuaSec :info() method gathers info using the OpenSSL function SSL_get_current_cipher(). Documentation for this function states that it may return NULL if no session has been established (yet). If so, the LuaSec functions wrapping this return nil, triggering a nil-indexing error in mod_admin_telnet. --- plugins/mod_admin_telnet.lua | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/plugins/mod_admin_telnet.lua b/plugins/mod_admin_telnet.lua index 59eca28b..b0e349da 100644 --- a/plugins/mod_admin_telnet.lua +++ b/plugins/mod_admin_telnet.lua @@ -528,11 +528,14 @@ local function tls_info(session, line) common_info(session, line); if session.secure then local sock = session.conn and session.conn.socket and session.conn:socket(); - if sock and sock.info then - local info = sock:info(); - line[#line+1] = ("(%s with %s)"):format(info.protocol, info.cipher); - else - line[#line+1] = "(cipher info unavailable)"; + if sock then + local info = sock.info and sock:info(); + if info then + line[#line+1] = ("(%s with %s)"):format(info.protocol, info.cipher); + else + -- TLS session might not be ready yet + line[#line+1] = "(cipher info unavailable)"; + end end else line[#line+1] = "(insecure)"; -- cgit v1.2.3 From 51f4e1c896e2135b3164847c37d8d2410a9127a4 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 22 Mar 2020 21:05:59 +0100 Subject: mod_storage_sql: Add index covering sort_id to improve performance (fixes #1505) --- plugins/mod_storage_sql.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/mod_storage_sql.lua b/plugins/mod_storage_sql.lua index 73207073..5b1c3603 100644 --- a/plugins/mod_storage_sql.lua +++ b/plugins/mod_storage_sql.lua @@ -500,6 +500,7 @@ local function create_table(engine) -- luacheck: ignore 431/engine Index { name="prosodyarchive_index", unique = engine.params.driver ~= "MySQL", "host", "user", "store", "key" }; Index { name="prosodyarchive_with_when", "host", "user", "store", "with", "when" }; Index { name="prosodyarchive_when", "host", "user", "store", "when" }; + Index { name="prosodyarchive_sort", "host", "user", "store", "sort_id" }; }; engine:transaction(function() ProsodyArchiveTable:create(engine); -- cgit v1.2.3 From 1ee06a447b217fd288654e65236bd81b65a640f5 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 29 Sep 2019 15:05:17 +0200 Subject: mod_register_ibr: Add FORM_TYPE as required by XEP-0077 (fixes #1511) Backport of f90abf142d53 from trunk --- plugins/mod_register_ibr.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/mod_register_ibr.lua b/plugins/mod_register_ibr.lua index bbe7581d..e04e6ecd 100644 --- a/plugins/mod_register_ibr.lua +++ b/plugins/mod_register_ibr.lua @@ -25,6 +25,7 @@ end); local account_details = module:open_store("account_details"); local field_map = { + FORM_TYPE = { name = "FORM_TYPE", type = "hidden", value = "jabber:iq:register" }; username = { name = "username", type = "text-single", label = "Username", required = true }; password = { name = "password", type = "text-private", label = "Password", required = true }; nick = { name = "nick", type = "text-single", label = "Nickname" }; @@ -50,6 +51,7 @@ local registration_form = dataform_new{ title = title; instructions = instructions; + field_map.FORM_TYPE; field_map.username; field_map.password; }; -- cgit v1.2.3 From b60c0cb4354cbbf4904ed9c774d9522f8d628085 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 3 Apr 2020 22:35:47 +0200 Subject: mod_vcard4: Report correct error condition (fixes #1521) On error, the second return value is the error condition, not the third. --- plugins/mod_vcard4.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/mod_vcard4.lua b/plugins/mod_vcard4.lua index 378c0602..04dbca9e 100644 --- a/plugins/mod_vcard4.lua +++ b/plugins/mod_vcard4.lua @@ -14,9 +14,9 @@ module:hook("iq-get/bare/urn:ietf:params:xml:ns:vcard-4.0:vcard", function (even local ok, id, item = pep_service:get_last_item("urn:xmpp:vcard4", stanza.attr.from); if ok and item then origin.send(st.reply(stanza):add_child(item.tags[1])); - elseif item == "item-not-found" or not id then + elseif id == "item-not-found" or not id then origin.send(st.error_reply(stanza, "cancel", "item-not-found")); - elseif item == "forbidden" then + elseif id == "forbidden" then origin.send(st.error_reply(stanza, "auth", "forbidden")); else origin.send(st.error_reply(stanza, "modify", "undefined-condition")); -- cgit v1.2.3 From ac080de447b2b400dadc78a7e550d5e8053d6790 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 10 Apr 2020 16:11:09 +0200 Subject: core.certmanager: Look for privkey.pem to go with fullchain.pem (fix #1526) This makes `prosodyctl cert import example.com /path/to/example.com/fullchain.pem` work. This was never intended to, yet users commonly tried this and got problems. --- core/certmanager.lua | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/core/certmanager.lua b/core/certmanager.lua index 5282a6f5..40021db6 100644 --- a/core/certmanager.lua +++ b/core/certmanager.lua @@ -73,13 +73,15 @@ local function find_cert(user_certs, name) local key_path = certs .. key_try[i]:format(name); if stat(crt_path, "mode") == "file" then - if key_path:sub(-4) == ".crt" then - key_path = key_path:sub(1, -4) .. "key"; - if stat(key_path, "mode") == "file" then - log("debug", "Selecting certificate %s with key %s for %s", crt_path, key_path, name); - return { certificate = crt_path, key = key_path }; + if crt_path == key_path then + if key_path:sub(-4) == ".crt" then + key_path = key_path:sub(1, -4) .. "key"; + elseif key_path:sub(-13) == "fullchain.pem" then + key_path = key_path:sub(1, -14) .. "privkey.pem"; end - elseif stat(key_path, "mode") == "file" then + end + + if stat(key_path, "mode") == "file" then log("debug", "Selecting certificate %s with key %s for %s", crt_path, key_path, name); return { certificate = crt_path, key = key_path }; end -- cgit v1.2.3 From 0b713578627418a5bc100d83253bb84b365fef3d Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 26 Jan 2020 16:40:21 +0100 Subject: util.prosodyctl: Tell prosody do daemonize via command line flag (fixes #1514) Backport of 88be11e9f9b9 --- util/prosodyctl.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua index 5f0c4d12..6c84ab6e 100644 --- a/util/prosodyctl.lua +++ b/util/prosodyctl.lua @@ -238,9 +238,9 @@ local function start(source_dir) return false, "already-running"; end if not source_dir then - os.execute("./prosody"); + os.execute("./prosody -D"); else - os.execute(source_dir.."/../../bin/prosody"); + os.execute(source_dir.."/../../bin/prosody -D"); end return true; end -- cgit v1.2.3 From 524ccf5c3f5457fe339a2c8b8a748ee5632dba9a Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 25 Aug 2019 20:22:35 +0200 Subject: core.certmanager: Move EECDH ciphers before EDH in default cipherstring (fixes #1513) Backport of 94e341dee51c The original intent of having kEDH before kEECDH was that if a `dhparam` file was specified, this would be interpreted as a preference by the admin for old and well-tested Diffie-Hellman key agreement over newer elliptic curve ones. Otherwise the faster elliptic curve ciphersuites would be preferred. This didn't really work as intended since this affects the ClientHello on outgoing s2s connections, leading to some servers using poorly configured kEDH. With Debian shipping OpenSSL settings that enforce a higher security level, this caused interoperability problems with servers that use DH params smaller than 2048 bits. E.g. jabber.org at the time of this writing has 1024 bit DH params. MattJ says > Curves have won, and OpenSSL is less weird about them now --- core/certmanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/certmanager.lua b/core/certmanager.lua index 40021db6..20b91318 100644 --- a/core/certmanager.lua +++ b/core/certmanager.lua @@ -125,8 +125,8 @@ local core_defaults = { "P-521", }; ciphers = { -- Enabled ciphers in order of preference: - "HIGH+kEDH", -- Ephemeral Diffie-Hellman key exchange, if a 'dhparam' file is set "HIGH+kEECDH", -- Ephemeral Elliptic curve Diffie-Hellman key exchange + "HIGH+kEDH", -- Ephemeral Diffie-Hellman key exchange, if a 'dhparam' file is set "HIGH", -- Other "High strength" ciphers -- Disabled cipher suites: "!PSK", -- Pre-Shared Key - not used for XMPP -- cgit v1.2.3 From 96e84d9954e165006533ab98f9183ddcbfd3937f Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 31 Dec 2019 02:48:49 +0100 Subject: prosodyctl check: Warn about conflict between mod_pep and mod_pep_simple Related #1483 --- prosodyctl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/prosodyctl b/prosodyctl index a732a73e..3242218d 100755 --- a/prosodyctl +++ b/prosodyctl @@ -937,6 +937,11 @@ function commands.check(arg) 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(""); -- cgit v1.2.3 From 87f0c198e51e0ea76090681ceb8705055ed3942b Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 31 Dec 2019 02:50:25 +0100 Subject: prosodyctl: Print a blank line to improve spacing and readability --- prosodyctl | 1 + 1 file changed, 1 insertion(+) diff --git a/prosodyctl b/prosodyctl index 3242218d..982b102e 100755 --- a/prosodyctl +++ b/prosodyctl @@ -934,6 +934,7 @@ function commands.check(arg) 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 -- cgit v1.2.3 From e84ead8c296f6f5b4d7f11be2c3e1e4eb61ff20d Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 2 Jan 2020 10:49:37 +0100 Subject: mod_websocket: Clear mask bit when reflecting ping frames (fixes #1484) --- plugins/mod_websocket.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/mod_websocket.lua b/plugins/mod_websocket.lua index 686a8981..6625c676 100644 --- a/plugins/mod_websocket.lua +++ b/plugins/mod_websocket.lua @@ -236,6 +236,7 @@ function handle_request(event) return; elseif opcode == 0x9 then -- Ping frame frame.opcode = 0xA; + frame.MASK = false; -- Clients send masked frames, servers don't, see #1484 conn:write(build_frame(frame)); return ""; elseif opcode == 0xA then -- Pong frame, MAY be sent unsolicited, eg as keepalive -- cgit v1.2.3 -- cgit v1.2.3 From bd3e6037e4a692becdeff0e91b0b1b5c38cb663a Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 19 Jan 2020 15:26:22 +0000 Subject: util.startup: Add startup step for parsing command-line options --- util/startup.lua | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/util/startup.lua b/util/startup.lua index e88ed709..fb898970 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -12,6 +12,60 @@ local dependencies = require "util.dependencies"; local original_logging_config; +local short_params = { D = "daemonize", F = "no-daemonize" }; +local value_params = { config = true }; + +function startup.parse_args() + local parsed_opts = {}; + + if #arg > 0 and arg[1] ~= "--config" then + 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 + end + prosody.opts = parsed_opts; +end + function startup.read_config() local filenames = {}; -- cgit v1.2.3 From a7abd40011fb09c4585f05da281d3026490bc2ae Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 19 Jan 2020 15:27:16 +0000 Subject: prosody/util.startup: Switch to parse_args() for --root and --config --- prosody | 6 +----- util/startup.lua | 12 ++++++------ 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/prosody b/prosody index 204fb36d..3659ca55 100755 --- a/prosody +++ b/prosody @@ -43,11 +43,7 @@ if CFG_DATADIR then end end -if #arg > 0 and arg[1] ~= "--config" then - print("Unknown command-line option: "..tostring(arg[1])); - print("Perhaps you meant to use prosodyctl instead?"); - return 1; -end + local startup = require "util.startup"; local async = require "util.async"; diff --git a/util/startup.lua b/util/startup.lua index fb898970..93f40c8f 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -70,12 +70,11 @@ function startup.read_config() local filenames = {}; local filename; - if arg[1] == "--config" and arg[2] then - table.insert(filenames, arg[2]); + if prosody.opts.config then + table.insert(filenames, prosody.opts.config); if CFG_CONFIGDIR then - table.insert(filenames, CFG_CONFIGDIR.."/"..arg[2]); + table.insert(filenames, CFG_CONFIGDIR.."/"..prosody.opts.config); end - table.remove(arg, 1); table.remove(arg, 1); elseif os.getenv("PROSODY_CONFIG") then -- Passed by prosodyctl table.insert(filenames, os.getenv("PROSODY_CONFIG")); else @@ -459,8 +458,7 @@ function startup.switch_user() os.exit(1); end prosody.current_uid = pposix.getuid(); - local arg_root = arg[1] == "--root"; - if arg_root then table.remove(arg, 1); end + local arg_root = prosody.opts.root; if prosody.current_uid == 0 and config.get("*", "run_as_root") ~= true and not arg_root then -- We haz root! local desired_user = config.get("*", "prosody_user") or "prosody"; @@ -569,6 +567,7 @@ end -- prosodyctl only function startup.prosodyctl() + startup.parse_args(); startup.init_global_state(); startup.read_config(); startup.force_console_logging(); @@ -589,6 +588,7 @@ end function startup.prosody() -- These actions are in a strict order, as many depend on -- previous steps to have already been performed + startup.parse_args(); startup.init_global_state(); startup.read_config(); startup.init_logging(); -- cgit v1.2.3 From 5113b2ec270a9c90f47a93f71780718e100371f3 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 19 Jan 2020 15:28:09 +0000 Subject: mod_posix: Support for command-line flags to override 'daemonize' config option -D / --daemonize -F / --no-daemonize --- plugins/mod_posix.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/mod_posix.lua b/plugins/mod_posix.lua index 23df4d23..fe826c22 100644 --- a/plugins/mod_posix.lua +++ b/plugins/mod_posix.lua @@ -126,7 +126,12 @@ function syslog_sink_maker(config) -- luacheck: ignore 212/config end require "core.loggingmanager".register_sink_type("syslog", syslog_sink_maker); -local daemonize = module:get_option("daemonize", prosody.installed); +local daemonize = prosody.opts.daemonize; + +if daemonize == nil then + -- Fall back to config file if not specified on command-line + daemonize = module:get_option("daemonize", prosody.installed); +end local function remove_log_sinks() local lm = require "core.loggingmanager"; -- cgit v1.2.3 From e59c4dcec2d2501aa3c6ebb29fcac1f964993c18 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 19 Jan 2020 15:39:13 +0000 Subject: util.startup: Fix logic to make --config work again --- util/startup.lua | 76 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/util/startup.lua b/util/startup.lua index 93f40c8f..e54d6935 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -18,50 +18,52 @@ local value_params = { config = true }; function startup.parse_args() local parsed_opts = {}; - if #arg > 0 and arg[1] ~= "--config" then - while true do - local raw_param = arg[1]; - if not raw_param then - break; - end + 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 + 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?"); + 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 - - 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 + 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 - parsed_opts[param_k] = param_v; end + parsed_opts[param_k] = param_v; + print("ARG", param_k, param_v); end prosody.opts = parsed_opts; end -- cgit v1.2.3 From 2c379eb7110b83716e6c6267be135fcdc72bdbd7 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 19 Jan 2020 15:50:16 +0000 Subject: util.startup: Remove accidentally-committed debugging --- util/startup.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/util/startup.lua b/util/startup.lua index e54d6935..35be3162 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -63,7 +63,6 @@ function startup.parse_args() end end parsed_opts[param_k] = param_v; - print("ARG", param_k, param_v); end prosody.opts = parsed_opts; end -- cgit v1.2.3 From 4bbc2c71208f7a53bd0d7ae02f17371b47657869 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 19 Jan 2020 15:50:32 +0000 Subject: util.startup: Ensure prosody.opts exists even when no options provided --- util/startup.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/startup.lua b/util/startup.lua index 35be3162..24ed6026 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -17,6 +17,7 @@ local value_params = { config = true }; function startup.parse_args() local parsed_opts = {}; + prosody.opts = parsed_opts; if #arg == 0 then return; @@ -64,7 +65,6 @@ function startup.parse_args() end parsed_opts[param_k] = param_v; end - prosody.opts = parsed_opts; end function startup.read_config() -- cgit v1.2.3 -- cgit v1.2.3 From eb65fad92ba437f1a3c2b0cd9d2bb0b62aae82e5 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 23 Jan 2020 21:43:36 +0000 Subject: Log warning when using prosodyctl start/stop/restart --- prosodyctl | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/prosodyctl b/prosodyctl index 982b102e..b16d9230 100755 --- a/prosodyctl +++ b/prosodyctl @@ -198,11 +198,25 @@ function commands.deluser(arg) return 1; end +local function service_command_warning(command) + if prosody.installed and configmanager.get("*", "prosodyctl_service_warnings") ~= false then + show_warning("WARNING: Use of prosodyctl start/stop/restart/reload is not recommended"); + show_warning(" if Prosody is managed by an init system - use that directly instead."); + if lfs.attributes("/etc/systemd") then + show_warning(" e.g. systemctl %s prosody", command); + elseif lfs.attributes("/etc/init.d/prosody") then + show_warning(" e.g. /etc/init.d/prosody %s", command); + end + show_warning(""); + end +end + function commands.start(arg) if arg[1] == "--help" then show_usage([[start]], [[Start Prosody]]); return 1; end + service_command_warning("start"); local ok, ret = prosodyctl.isrunning(); if not ok then show_message(error_messages[ret]); @@ -293,6 +307,8 @@ function commands.stop(arg) return 1; end + service_command_warning("stop"); + if not prosodyctl.isrunning() then show_message("Prosody is not running"); return 1; @@ -328,6 +344,8 @@ function commands.restart(arg) return 1; end + service_command_warning("restart"); + commands.stop(arg); return commands.start(arg); end @@ -421,6 +439,8 @@ function commands.reload(arg) return 1; end + service_command_warning("reload"); + if not prosodyctl.isrunning() then show_message("Prosody is not running"); return 1; -- cgit v1.2.3 From a67a18e9a22c0746a76129ca9303929cb037ffd4 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 23 Jan 2020 21:59:13 +0000 Subject: prosodyctl: Fix some luacheck warnings --- prosodyctl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/prosodyctl b/prosodyctl index b16d9230..028e08d8 100755 --- a/prosodyctl +++ b/prosodyctl @@ -69,6 +69,7 @@ local modulemanager = require "core.modulemanager" local prosodyctl = require "util.prosodyctl" local socket = require "socket" local dependencies = require "util.dependencies"; +local lfs = dependencies.softreq "lfs"; ----------------------- @@ -198,14 +199,15 @@ function commands.deluser(arg) return 1; end -local function service_command_warning(command) +local function service_command_warning(service_command) if prosody.installed and configmanager.get("*", "prosodyctl_service_warnings") ~= false then show_warning("WARNING: Use of prosodyctl start/stop/restart/reload is not recommended"); show_warning(" if Prosody is managed by an init system - use that directly instead."); + lfs = lfs or require if lfs.attributes("/etc/systemd") then - show_warning(" e.g. systemctl %s prosody", command); + show_warning(" e.g. systemctl %s prosody", service_command); elseif lfs.attributes("/etc/init.d/prosody") then - show_warning(" e.g. /etc/init.d/prosody %s", command); + show_warning(" e.g. /etc/init.d/prosody %s", service_command); end show_warning(""); end @@ -512,7 +514,6 @@ function commands.unregister(arg) end local openssl; -local lfs; local cert_commands = {}; -- cgit v1.2.3 From 9185758048972c64ad4cfcd41800dfa4671e7d37 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 24 Jan 2020 16:21:30 +0000 Subject: mod_websocket: Fire event on session creation (thanks Aaron van Meerten) --- plugins/mod_websocket.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/mod_websocket.lua b/plugins/mod_websocket.lua index 6625c676..177259e6 100644 --- a/plugins/mod_websocket.lua +++ b/plugins/mod_websocket.lua @@ -305,6 +305,8 @@ function handle_request(event) response.headers.sec_webSocket_accept = base64(sha1(request.headers.sec_websocket_key .. "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")); response.headers.sec_webSocket_protocol = "xmpp"; + module:fire_event("websocket-session", { session = session, request = request }); + session.log("debug", "Sending WebSocket handshake"); return ""; -- cgit v1.2.3 From 34603f5da0789d989907d8d596d26b1b31c04aba Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 24 Jan 2020 23:27:49 +0100 Subject: mod_admin_telnet: Create a DNS resolver per console session (fixes #1492) This is now the common pattern, eg see mod_s2s. --- plugins/mod_admin_telnet.lua | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/plugins/mod_admin_telnet.lua b/plugins/mod_admin_telnet.lua index 1cbe27a4..59eca28b 100644 --- a/plugins/mod_admin_telnet.lua +++ b/plugins/mod_admin_telnet.lua @@ -1074,36 +1074,49 @@ end def_env.dns = {}; local adns = require"net.adns"; -local dns = require"net.dns"; + +local function get_resolver(session) + local resolver = session.dns_resolver; + if not resolver then + resolver = adns.resolver(); + session.dns_resolver = resolver; + end + return resolver; +end function def_env.dns:lookup(name, typ, class) + local resolver = get_resolver(self.session); local ret = "Query sent"; local print = self.session.print; local function handler(...) ret = "Got response"; print(...); end - adns.lookup(handler, name, typ, class); + resolver:lookup(handler, name, typ, class); return true, ret; end function def_env.dns:addnameserver(...) - dns._resolver:addnameserver(...) + local resolver = get_resolver(self.session); + resolver._resolver:addnameserver(...) return true end function def_env.dns:setnameserver(...) - dns._resolver:setnameserver(...) + local resolver = get_resolver(self.session); + resolver._resolver:setnameserver(...) return true end function def_env.dns:purge() - dns.purge() + local resolver = get_resolver(self.session); + resolver._resolver:purge() return true end function def_env.dns:cache() - return true, "Cache:\n"..tostring(dns.cache()) + local resolver = get_resolver(self.session); + return true, "Cache:\n"..tostring(resolver._resolver.cache) end def_env.http = {}; -- cgit v1.2.3 From 2f72650639f0abb2715aa67404b5503974a95199 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 13 Feb 2020 14:27:12 +0000 Subject: mod_muc: Allow control over the server-admins-are-room-owners feature (see #1174) --- plugins/muc/mod_muc.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/muc/mod_muc.lua b/plugins/muc/mod_muc.lua index 954bae92..b9c7c859 100644 --- a/plugins/muc/mod_muc.lua +++ b/plugins/muc/mod_muc.lua @@ -104,7 +104,8 @@ local function is_admin(jid) return um_is_admin(jid, module.host); end -do -- Monkey patch to make server admins room owners +if module:get_option_boolean("component_admins_as_room_owners", true) then + -- Monkey patch to make server admins room owners local _get_affiliation = room_mt.get_affiliation; function room_mt:get_affiliation(jid) if is_admin(jid) then return "owner"; end -- cgit v1.2.3 From d924dbc40d04588889e4b18f5d0658b84116819f Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 21 Feb 2020 23:00:44 +0100 Subject: mod_storage_sql: Fix check for deletion limits (fixes #1494) The check was only performed if sql_manage_tables was set to true (the default) It should always be performed --- plugins/mod_storage_sql.lua | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/mod_storage_sql.lua b/plugins/mod_storage_sql.lua index a449091e..a9298138 100644 --- a/plugins/mod_storage_sql.lua +++ b/plugins/mod_storage_sql.lua @@ -622,15 +622,15 @@ function module.load() module:log("error", "Old database format detected. Please run: prosodyctl mod_%s upgrade", module.name); return false, "database upgrade needed"; end - if engine.params.driver == "SQLite3" then - for row in engine:select("PRAGMA compile_options") do - if row[1] == "ENABLE_UPDATE_DELETE_LIMIT" then - engine._have_delete_limit = true; - end - end - end end end); + if engine.params.driver == "SQLite3" then + for row in engine:select("PRAGMA compile_options") do + if row[1] == "ENABLE_UPDATE_DELETE_LIMIT" then + engine._have_delete_limit = true; + end + end + end engines[sql.db2uri(params)] = engine; end -- cgit v1.2.3 From 0a4ab367411a9d90dbe74995c0be4d7597429a1e Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 22 Feb 2020 16:23:43 +0100 Subject: mod_storage_sql: Move update limit check back one step, fixes traceback 0c00274528a4 moved it one step too far, so the check was performed before connecting to the database instead of after. --- plugins/mod_storage_sql.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/mod_storage_sql.lua b/plugins/mod_storage_sql.lua index a9298138..73207073 100644 --- a/plugins/mod_storage_sql.lua +++ b/plugins/mod_storage_sql.lua @@ -623,14 +623,14 @@ function module.load() return false, "database upgrade needed"; end end - end); - if engine.params.driver == "SQLite3" then - for row in engine:select("PRAGMA compile_options") do - if row[1] == "ENABLE_UPDATE_DELETE_LIMIT" then - engine._have_delete_limit = true; + if engine.params.driver == "SQLite3" then + for row in engine:select("PRAGMA compile_options") do + if row[1] == "ENABLE_UPDATE_DELETE_LIMIT" then + engine._have_delete_limit = true; + end end end - end + end); engines[sql.db2uri(params)] = engine; end -- cgit v1.2.3 From c008722d2b5ddb998d501d7259020f0df9788c33 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 24 Feb 2020 23:16:08 +0100 Subject: util.datamanager: Fix iterating over "users" (thanks marc0s) The 'store' path componend used to be unescaped until 756a2a00e7e7 added escaping to address issues with characters like '/' used in PEP, but with a special case for '_' which was already in common use in 'store' path components. Missed adding this escaping here. --- util/datamanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/datamanager.lua b/util/datamanager.lua index cf96887b..0d7060b7 100644 --- a/util/datamanager.lua +++ b/util/datamanager.lua @@ -320,7 +320,7 @@ local type_map = { local function users(host, store, typ) -- luacheck: ignore 431/store typ = type_map[typ or "keyval"]; - local store_dir = format("%s/%s/%s", data_path, encode(host), store); + local store_dir = format("%s/%s/%s", data_path, encode(host), store_encode(store)); local mode, err = lfs.attributes(store_dir, "mode"); if not mode then -- cgit v1.2.3 From 328fba00fd10271b5c6613722cd299f481ec03da Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 27 Feb 2020 19:42:51 +0100 Subject: mod_muc_mam: Fix traceback saving message from non-occupant (fixes #1497) Caused by a plugin sending a message from the room itself. --- plugins/mod_muc_mam.lua | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/plugins/mod_muc_mam.lua b/plugins/mod_muc_mam.lua index 2ce5e1b5..f7ea3136 100644 --- a/plugins/mod_muc_mam.lua +++ b/plugins/mod_muc_mam.lua @@ -335,11 +335,14 @@ local function save_to_history(self, stanza) if stanza.name == "message" and self:get_whois() == "anyone" then stored_stanza = st.clone(stanza); stored_stanza.attr.to = nil; - local actor = jid_bare(self._occupants[stanza.attr.from].jid); - local affiliation = self:get_affiliation(actor) or "none"; - local role = self:get_role(actor) or self:get_default_role(affiliation); - stored_stanza:add_direct_child(st.stanza("x", { xmlns = xmlns_muc_user }) - :tag("item", { affiliation = affiliation; role = role; jid = actor })); + local occupant = self._occupants[stanza.attr.from]; + if occupant then + local actor = jid_bare(occupant.jid); + local affiliation = self:get_affiliation(actor) or "none"; + local role = self:get_role(actor) or self:get_default_role(affiliation); + stored_stanza:add_direct_child(st.stanza("x", { xmlns = xmlns_muc_user }) + :tag("item", { affiliation = affiliation; role = role; jid = actor })); + end end -- Policy check -- cgit v1.2.3 From 344f1b055c96ebd5f964395e437837a3cca5db24 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 27 Feb 2020 20:01:54 +0100 Subject: mod_pubsub, mod_pep: Ensure correct number of children of (fixes #1496) --- plugins/mod_pep.lua | 2 +- plugins/mod_pubsub/mod_pubsub.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/mod_pep.lua b/plugins/mod_pep.lua index 12be41a2..f8047a92 100644 --- a/plugins/mod_pep.lua +++ b/plugins/mod_pep.lua @@ -51,7 +51,7 @@ function module.restore(data) end function is_item_stanza(item) - return st.is_stanza(item) and item.attr.xmlns == xmlns_pubsub and item.name == "item"; + return st.is_stanza(item) and item.attr.xmlns == xmlns_pubsub and item.name == "item" and #item.tags == 1; end function check_node_config(node, actor, new_config) -- luacheck: ignore 212/node 212/actor diff --git a/plugins/mod_pubsub/mod_pubsub.lua b/plugins/mod_pubsub/mod_pubsub.lua index 855c5fd2..4276bc5e 100644 --- a/plugins/mod_pubsub/mod_pubsub.lua +++ b/plugins/mod_pubsub/mod_pubsub.lua @@ -112,7 +112,7 @@ function check_node_config(node, actor, new_config) -- luacheck: ignore 212/acto end function is_item_stanza(item) - return st.is_stanza(item) and item.attr.xmlns == xmlns_pubsub and item.name == "item"; + return st.is_stanza(item) and item.attr.xmlns == xmlns_pubsub and item.name == "item" and #item.tags == 1; end module:hook("pubsub-summary/http://www.w3.org/2005/Atom", function (event) -- cgit v1.2.3 From ab045cc0cf8582a37c47dcd6fe90de9c113cd26e Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 11 Mar 2020 18:07:03 +0100 Subject: net.server_epoll: Fix indentation Some lines seem to have gotten the wrong indentation, possibly caused by Meld which often ignores lines with only whitespace changes and leaves their previous indentation. --- net/server_epoll.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/server_epoll.lua b/net/server_epoll.lua index 0c03ae15..2182d56a 100644 --- a/net/server_epoll.lua +++ b/net/server_epoll.lua @@ -102,7 +102,7 @@ local function runtimers(next_delay, min_wait) if peek > now then next_delay = peek - now; break; - end + end local _, timer, id = timers:pop(); local ok, ret = pcall(timer[2], now); @@ -110,10 +110,10 @@ local function runtimers(next_delay, min_wait) local next_time = now+ret; timer[1] = next_time; timers:insert(timer, next_time); - end + end peek = timers:peek(); - end + end if peek == nil then return next_delay; end -- cgit v1.2.3 From 48dcba89d77ec5ec7d3ed9df84dd4046b5099e45 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 11 Mar 2020 21:15:01 +0100 Subject: mod_mam,mod_muc_mam: Allow other work to be performed during archive cleanup (fixes #1504) This lets Prosody handle socket related work between each step in the cleanup in order to prevent the server from being completely blocked during this. An async storage backend would not need this but those are currently rare. --- plugins/mod_mam/mod_mam.lua | 6 +++++- plugins/mod_muc_mam.lua | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/plugins/mod_mam/mod_mam.lua b/plugins/mod_mam/mod_mam.lua index 295d90e1..e7d89a95 100644 --- a/plugins/mod_mam/mod_mam.lua +++ b/plugins/mod_mam/mod_mam.lua @@ -362,7 +362,8 @@ if cleanup_after ~= "never" then end end - cleanup_runner = require "util.async".runner(function () + local async = require "util.async"; + cleanup_runner = async.runner(function () local users = {}; local cut_off = datestamp(os.time() - cleanup_after); for date in cleanup_storage:users() do @@ -391,6 +392,9 @@ if cleanup_after ~= "never" then cleanup_map:set(cut_off, user, true); module:log("error", "Could not delete messages for user '%s': %s", user, err); end + local wait, done = async.waiter(); + module:add_timer(0.01, done); + wait(); end module:log("info", "Deleted %d expired messages for %d users", sum, num_users); end); diff --git a/plugins/mod_muc_mam.lua b/plugins/mod_muc_mam.lua index f7ea3136..5a01324d 100644 --- a/plugins/mod_muc_mam.lua +++ b/plugins/mod_muc_mam.lua @@ -437,7 +437,8 @@ if cleanup_after ~= "never" then end end - cleanup_runner = require "util.async".runner(function () + local async = require "util.async"; + cleanup_runner = async.runner(function () local rooms = {}; local cut_off = datestamp(os.time() - cleanup_after); for date in cleanup_storage:users() do @@ -466,6 +467,9 @@ if cleanup_after ~= "never" then cleanup_map:set(cut_off, room, true); module:log("error", "Could not delete messages for room '%s': %s", room, err); end + local wait, done = async.waiter(); + module:add_timer(0.01, done); + wait(); end module:log("info", "Deleted %d expired messages for %d rooms", sum, num_rooms); end); -- cgit v1.2.3 From 773203771da3d2b443ec7e05b6a180456e195b84 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 12 Mar 2020 20:32:07 +0000 Subject: MUC: Persist affiliation_data in new MUC format! --- plugins/muc/muc.lib.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 639ecc38..bc370da5 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -1449,6 +1449,7 @@ function room_mt:freeze(live) frozen = { _jid = self.jid; _data = self._data; + _affiliation_data = self._affiliation_data; }; for user, affiliation in pairs(self._affiliations) do frozen[user] = affiliation; -- cgit v1.2.3 From 9e95f5e9f6a5447b9b68fc20b3a490b553d6b06c Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 22 Mar 2020 17:35:26 +0100 Subject: mod_admin_telnet: Handle unavailable cipher info (fixes #1510) The LuaSec :info() method gathers info using the OpenSSL function SSL_get_current_cipher(). Documentation for this function states that it may return NULL if no session has been established (yet). If so, the LuaSec functions wrapping this return nil, triggering a nil-indexing error in mod_admin_telnet. --- plugins/mod_admin_telnet.lua | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/plugins/mod_admin_telnet.lua b/plugins/mod_admin_telnet.lua index 59eca28b..b0e349da 100644 --- a/plugins/mod_admin_telnet.lua +++ b/plugins/mod_admin_telnet.lua @@ -528,11 +528,14 @@ local function tls_info(session, line) common_info(session, line); if session.secure then local sock = session.conn and session.conn.socket and session.conn:socket(); - if sock and sock.info then - local info = sock:info(); - line[#line+1] = ("(%s with %s)"):format(info.protocol, info.cipher); - else - line[#line+1] = "(cipher info unavailable)"; + if sock then + local info = sock.info and sock:info(); + if info then + line[#line+1] = ("(%s with %s)"):format(info.protocol, info.cipher); + else + -- TLS session might not be ready yet + line[#line+1] = "(cipher info unavailable)"; + end end else line[#line+1] = "(insecure)"; -- cgit v1.2.3 From daa4995fdbb8783c0d412bb00bd84d214b50bd3d Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 22 Mar 2020 21:05:59 +0100 Subject: mod_storage_sql: Add index covering sort_id to improve performance (fixes #1505) --- plugins/mod_storage_sql.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/mod_storage_sql.lua b/plugins/mod_storage_sql.lua index 73207073..5b1c3603 100644 --- a/plugins/mod_storage_sql.lua +++ b/plugins/mod_storage_sql.lua @@ -500,6 +500,7 @@ local function create_table(engine) -- luacheck: ignore 431/engine Index { name="prosodyarchive_index", unique = engine.params.driver ~= "MySQL", "host", "user", "store", "key" }; Index { name="prosodyarchive_with_when", "host", "user", "store", "with", "when" }; Index { name="prosodyarchive_when", "host", "user", "store", "when" }; + Index { name="prosodyarchive_sort", "host", "user", "store", "sort_id" }; }; engine:transaction(function() ProsodyArchiveTable:create(engine); -- cgit v1.2.3 From bef0ce0e1fb7ea698b745595f9ce23017f855fc7 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sun, 29 Sep 2019 15:05:17 +0200 Subject: mod_register_ibr: Add FORM_TYPE as required by XEP-0077 (fixes #1511) Backport of f90abf142d53 from trunk --- plugins/mod_register_ibr.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/mod_register_ibr.lua b/plugins/mod_register_ibr.lua index bbe7581d..e04e6ecd 100644 --- a/plugins/mod_register_ibr.lua +++ b/plugins/mod_register_ibr.lua @@ -25,6 +25,7 @@ end); local account_details = module:open_store("account_details"); local field_map = { + FORM_TYPE = { name = "FORM_TYPE", type = "hidden", value = "jabber:iq:register" }; username = { name = "username", type = "text-single", label = "Username", required = true }; password = { name = "password", type = "text-private", label = "Password", required = true }; nick = { name = "nick", type = "text-single", label = "Nickname" }; @@ -50,6 +51,7 @@ local registration_form = dataform_new{ title = title; instructions = instructions; + field_map.FORM_TYPE; field_map.username; field_map.password; }; -- cgit v1.2.3 From 9de87bf685635dfd2b95f6f42b603daa0250f275 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 3 Apr 2020 22:35:47 +0200 Subject: mod_vcard4: Report correct error condition (fixes #1521) On error, the second return value is the error condition, not the third. --- plugins/mod_vcard4.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/mod_vcard4.lua b/plugins/mod_vcard4.lua index 378c0602..04dbca9e 100644 --- a/plugins/mod_vcard4.lua +++ b/plugins/mod_vcard4.lua @@ -14,9 +14,9 @@ module:hook("iq-get/bare/urn:ietf:params:xml:ns:vcard-4.0:vcard", function (even local ok, id, item = pep_service:get_last_item("urn:xmpp:vcard4", stanza.attr.from); if ok and item then origin.send(st.reply(stanza):add_child(item.tags[1])); - elseif item == "item-not-found" or not id then + elseif id == "item-not-found" or not id then origin.send(st.error_reply(stanza, "cancel", "item-not-found")); - elseif item == "forbidden" then + elseif id == "forbidden" then origin.send(st.error_reply(stanza, "auth", "forbidden")); else origin.send(st.error_reply(stanza, "modify", "undefined-condition")); -- cgit v1.2.3 From f0e3e8f10752facac7aa0b5923c1675f01187b27 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 10 Apr 2020 16:11:09 +0200 Subject: core.certmanager: Look for privkey.pem to go with fullchain.pem (fix #1526) This makes `prosodyctl cert import example.com /path/to/example.com/fullchain.pem` work. This was never intended to, yet users commonly tried this and got problems. --- core/certmanager.lua | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/core/certmanager.lua b/core/certmanager.lua index 5282a6f5..40021db6 100644 --- a/core/certmanager.lua +++ b/core/certmanager.lua @@ -73,13 +73,15 @@ local function find_cert(user_certs, name) local key_path = certs .. key_try[i]:format(name); if stat(crt_path, "mode") == "file" then - if key_path:sub(-4) == ".crt" then - key_path = key_path:sub(1, -4) .. "key"; - if stat(key_path, "mode") == "file" then - log("debug", "Selecting certificate %s with key %s for %s", crt_path, key_path, name); - return { certificate = crt_path, key = key_path }; + if crt_path == key_path then + if key_path:sub(-4) == ".crt" then + key_path = key_path:sub(1, -4) .. "key"; + elseif key_path:sub(-13) == "fullchain.pem" then + key_path = key_path:sub(1, -14) .. "privkey.pem"; end - elseif stat(key_path, "mode") == "file" then + end + + if stat(key_path, "mode") == "file" then log("debug", "Selecting certificate %s with key %s for %s", crt_path, key_path, name); return { certificate = crt_path, key = key_path }; end -- cgit v1.2.3 From 44a77f42f32d2a71a033c575ca90ed324b7aa0ab Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 26 Jan 2020 16:40:21 +0100 Subject: util.prosodyctl: Tell prosody do daemonize via command line flag (fixes #1514) Backport of 88be11e9f9b9 --- util/prosodyctl.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/prosodyctl.lua b/util/prosodyctl.lua index 5f0c4d12..6c84ab6e 100644 --- a/util/prosodyctl.lua +++ b/util/prosodyctl.lua @@ -238,9 +238,9 @@ local function start(source_dir) return false, "already-running"; end if not source_dir then - os.execute("./prosody"); + os.execute("./prosody -D"); else - os.execute(source_dir.."/../../bin/prosody"); + os.execute(source_dir.."/../../bin/prosody -D"); end return true; end -- cgit v1.2.3 From 415445e17a3beef3a99156e2ddbcdf39abf81de8 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 25 Aug 2019 20:22:35 +0200 Subject: core.certmanager: Move EECDH ciphers before EDH in default cipherstring (fixes #1513) Backport of 94e341dee51c The original intent of having kEDH before kEECDH was that if a `dhparam` file was specified, this would be interpreted as a preference by the admin for old and well-tested Diffie-Hellman key agreement over newer elliptic curve ones. Otherwise the faster elliptic curve ciphersuites would be preferred. This didn't really work as intended since this affects the ClientHello on outgoing s2s connections, leading to some servers using poorly configured kEDH. With Debian shipping OpenSSL settings that enforce a higher security level, this caused interoperability problems with servers that use DH params smaller than 2048 bits. E.g. jabber.org at the time of this writing has 1024 bit DH params. MattJ says > Curves have won, and OpenSSL is less weird about them now --- core/certmanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/certmanager.lua b/core/certmanager.lua index 40021db6..20b91318 100644 --- a/core/certmanager.lua +++ b/core/certmanager.lua @@ -125,8 +125,8 @@ local core_defaults = { "P-521", }; ciphers = { -- Enabled ciphers in order of preference: - "HIGH+kEDH", -- Ephemeral Diffie-Hellman key exchange, if a 'dhparam' file is set "HIGH+kEECDH", -- Ephemeral Elliptic curve Diffie-Hellman key exchange + "HIGH+kEDH", -- Ephemeral Diffie-Hellman key exchange, if a 'dhparam' file is set "HIGH", -- Other "High strength" ciphers -- Disabled cipher suites: "!PSK", -- Pre-Shared Key - not used for XMPP -- cgit v1.2.3 From 5e262dfdf6bdc94ac8a1b49c8c73124a90c6d1df Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 23 Apr 2020 13:16:25 +0100 Subject: MUC: Always include 'affiliation'/'role' attributes, defaulting to 'none' if nil --- plugins/muc/muc.lib.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index bc370da5..93334b88 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -143,7 +143,7 @@ end -- actor is the attribute table local function add_item(x, affiliation, role, jid, nick, actor_nick, actor_jid, reason) - x:tag("item", {affiliation = affiliation; role = role; jid = jid; nick = nick;}) + x:tag("item", {affiliation = affiliation or "none"; role = role; jid = jid; nick = nick;}) if actor_nick or actor_jid then x:tag("actor", {nick = actor_nick; jid = actor_jid;}):up() end -- cgit v1.2.3 From aabc695f33dd1602d4f55da6a5f97f6b6180011f Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 23 Apr 2020 13:16:25 +0100 Subject: MUC: Always include 'affiliation'/'role' attributes, defaulting to 'none' if nil --- plugins/muc/muc.lib.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index bc370da5..93334b88 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -143,7 +143,7 @@ end -- actor is the attribute table local function add_item(x, affiliation, role, jid, nick, actor_nick, actor_jid, reason) - x:tag("item", {affiliation = affiliation; role = role; jid = jid; nick = nick;}) + x:tag("item", {affiliation = affiliation or "none"; role = role; jid = jid; nick = nick;}) if actor_nick or actor_jid then x:tag("actor", {nick = actor_nick; jid = actor_jid;}):up() end -- cgit v1.2.3 From 262d16c2a679f01a1d712d3724bc28715c3daeb9 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 23 Apr 2020 13:29:23 +0100 Subject: MUC tests: Add to expected form field --- spec/scansion/muc_register.scs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/spec/scansion/muc_register.scs b/spec/scansion/muc_register.scs index e1eaf4e0..a077cd76 100644 --- a/spec/scansion/muc_register.scs +++ b/spec/scansion/muc_register.scs @@ -100,7 +100,9 @@ Juliet receives: http://jabber.org/protocol/muc#register - + + + @@ -339,7 +341,9 @@ Romeo receives: http://jabber.org/protocol/muc#register - + + + -- cgit v1.2.3 From b423e6cea01f29e77520a1b7f261ef639245a79a Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 23 Apr 2020 13:29:23 +0100 Subject: MUC tests: Add to expected form field --- spec/scansion/muc_register.scs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/spec/scansion/muc_register.scs b/spec/scansion/muc_register.scs index e1eaf4e0..a077cd76 100644 --- a/spec/scansion/muc_register.scs +++ b/spec/scansion/muc_register.scs @@ -100,7 +100,9 @@ Juliet receives: http://jabber.org/protocol/muc#register - + + + @@ -339,7 +341,9 @@ Romeo receives: http://jabber.org/protocol/muc#register - + + + -- cgit v1.2.3 From c4aebb62ea74ed42c4add8c9db1436a6326bdcd0 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 23 Apr 2020 13:43:29 +0100 Subject: Backed out changeset 18f2c7bc5795 (was testing against wrong branch) --- spec/scansion/muc_register.scs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/spec/scansion/muc_register.scs b/spec/scansion/muc_register.scs index a077cd76..e1eaf4e0 100644 --- a/spec/scansion/muc_register.scs +++ b/spec/scansion/muc_register.scs @@ -100,9 +100,7 @@ Juliet receives: http://jabber.org/protocol/muc#register - - - + @@ -341,9 +339,7 @@ Romeo receives: http://jabber.org/protocol/muc#register - - - + -- cgit v1.2.3 From 63968a43e55790745070640132f6cf30386527cb Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 23 Apr 2020 13:43:29 +0100 Subject: Backed out changeset 18f2c7bc5795 (was testing against wrong branch) --- spec/scansion/muc_register.scs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/spec/scansion/muc_register.scs b/spec/scansion/muc_register.scs index a077cd76..e1eaf4e0 100644 --- a/spec/scansion/muc_register.scs +++ b/spec/scansion/muc_register.scs @@ -100,9 +100,7 @@ Juliet receives: http://jabber.org/protocol/muc#register - - - + @@ -341,9 +339,7 @@ Romeo receives: http://jabber.org/protocol/muc#register - - - + -- cgit v1.2.3 From 68aedef579d411aad1a1c81b4b32ad5246c1143b Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 23 Apr 2020 13:44:47 +0100 Subject: MUC tests: Add missing affiliation attribute --- spec/scansion/muc_register.scs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/scansion/muc_register.scs b/spec/scansion/muc_register.scs index e1eaf4e0..a7e57177 100644 --- a/spec/scansion/muc_register.scs +++ b/spec/scansion/muc_register.scs @@ -198,7 +198,7 @@ Juliet receives: Romeo receives: - + -- cgit v1.2.3 From ab135bd3ef4db1445add63b1e9c2eb02ccf0fdcd Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 23 Apr 2020 13:44:47 +0100 Subject: MUC tests: Add missing affiliation attribute --- spec/scansion/muc_register.scs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/scansion/muc_register.scs b/spec/scansion/muc_register.scs index e1eaf4e0..a7e57177 100644 --- a/spec/scansion/muc_register.scs +++ b/spec/scansion/muc_register.scs @@ -198,7 +198,7 @@ Juliet receives: Romeo receives: - + -- cgit v1.2.3 From 2d57486958db8f1d07b7afd19340ece0a1a8714d Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 25 Apr 2020 20:55:00 +0200 Subject: mod_http_files: Avoid using inode in etag, fix #1498 --- plugins/mod_http_files.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_http_files.lua b/plugins/mod_http_files.lua index 1dae0d6d..a8398c01 100644 --- a/plugins/mod_http_files.lua +++ b/plugins/mod_http_files.lua @@ -112,7 +112,7 @@ function serve(opts) local last_modified = os_date('!%a, %d %b %Y %H:%M:%S GMT', attr.modification); response_headers.last_modified = last_modified; - local etag = ('"%02x-%x-%x-%x"'):format(attr.dev or 0, attr.ino or 0, attr.size or 0, attr.modification or 0); + local etag = ('"%x-%x-%x"'):format(attr.change or 0, attr.size or 0, attr.modification or 0); response_headers.etag = etag; local if_none_match = request_headers.if_none_match -- cgit v1.2.3 From f17e946824ec060011dfa2e097b5f16d6a30c79d Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 25 Apr 2020 20:55:00 +0200 Subject: mod_http_files: Avoid using inode in etag, fix #1498 --- plugins/mod_http_files.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_http_files.lua b/plugins/mod_http_files.lua index 1dae0d6d..a8398c01 100644 --- a/plugins/mod_http_files.lua +++ b/plugins/mod_http_files.lua @@ -112,7 +112,7 @@ function serve(opts) local last_modified = os_date('!%a, %d %b %Y %H:%M:%S GMT', attr.modification); response_headers.last_modified = last_modified; - local etag = ('"%02x-%x-%x-%x"'):format(attr.dev or 0, attr.ino or 0, attr.size or 0, attr.modification or 0); + local etag = ('"%x-%x-%x"'):format(attr.change or 0, attr.size or 0, attr.modification or 0); response_headers.etag = etag; local if_none_match = request_headers.if_none_match -- cgit v1.2.3 From 75c5a894fc8949106b13a01918a25bd8dc5c82c3 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 26 Apr 2020 20:39:39 +0200 Subject: mod_carbons: Fix handling of incoming MUC PMs #1540 27f5db07bec9 fixed this wrong. The code is supposed to check if the stanza is NOT sent to your bare JID. A MUC PM is always sent to your full JID. Hopefully nobody sends MUC invites to full JIDs, because those would be skipped by this as well. --- plugins/mod_carbons.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_carbons.lua b/plugins/mod_carbons.lua index 1dcd4a07..79d3e737 100644 --- a/plugins/mod_carbons.lua +++ b/plugins/mod_carbons.lua @@ -69,7 +69,7 @@ local function message_handler(event, c2s) elseif stanza:get_child("no-copy", "urn:xmpp:hints") then module:log("debug", "Message has no-copy hint, ignoring"); return - elseif not c2s and bare_jid == orig_from and stanza:get_child("x", "http://jabber.org/protocol/muc#user") then + elseif not c2s and bare_jid ~= orig_to and stanza:get_child("x", "http://jabber.org/protocol/muc#user") then module:log("debug", "MUC PM, ignoring"); return end -- cgit v1.2.3 From 8f8adbade99fa16331f14c3e784a2256d0e315c6 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 26 Apr 2020 20:39:39 +0200 Subject: mod_carbons: Fix handling of incoming MUC PMs #1540 27f5db07bec9 fixed this wrong. The code is supposed to check if the stanza is NOT sent to your bare JID. A MUC PM is always sent to your full JID. Hopefully nobody sends MUC invites to full JIDs, because those would be skipped by this as well. --- plugins/mod_carbons.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_carbons.lua b/plugins/mod_carbons.lua index 1dcd4a07..79d3e737 100644 --- a/plugins/mod_carbons.lua +++ b/plugins/mod_carbons.lua @@ -69,7 +69,7 @@ local function message_handler(event, c2s) elseif stanza:get_child("no-copy", "urn:xmpp:hints") then module:log("debug", "Message has no-copy hint, ignoring"); return - elseif not c2s and bare_jid == orig_from and stanza:get_child("x", "http://jabber.org/protocol/muc#user") then + elseif not c2s and bare_jid ~= orig_to and stanza:get_child("x", "http://jabber.org/protocol/muc#user") then module:log("debug", "MUC PM, ignoring"); return end -- cgit v1.2.3 From 8882e11c711176dbe1fd1133e5d8111eb5f06ef3 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 26 Apr 2020 20:58:51 +0200 Subject: mod_tls: Log when certificates are (re)loaded Meant to reduce user confusion over what's reloaded and not. --- plugins/mod_tls.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/mod_tls.lua b/plugins/mod_tls.lua index eb208e28..8c0c4e20 100644 --- a/plugins/mod_tls.lua +++ b/plugins/mod_tls.lua @@ -36,7 +36,7 @@ local host = hosts[module.host]; local ssl_ctx_c2s, ssl_ctx_s2sout, ssl_ctx_s2sin; local ssl_cfg_c2s, ssl_cfg_s2sout, ssl_cfg_s2sin; -function module.load() +function module.load(reload) local NULL, err = {}; local modhost = module.host; local parent = modhost:match("%.(.*)$"); @@ -63,6 +63,12 @@ function module.load() module:log("debug", "Creating context for s2sin"); ssl_ctx_s2sin, err, ssl_cfg_s2sin = create_context(host.host, "server", host_s2s, host_ssl, global_s2s); -- for incoming server connections if not ssl_ctx_s2sin then module:log("error", "Error creating contexts for s2sin: %s", err); end + + if reload then + module:log("info", "Certificates reloaded"); + else + module:log("info", "Certificates loaded"); + end end module:hook_global("config-reloaded", module.load); -- cgit v1.2.3 From 0190577d67e33396410305bfc9dc23f5de825838 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 26 Apr 2020 20:58:51 +0200 Subject: mod_tls: Log when certificates are (re)loaded Meant to reduce user confusion over what's reloaded and not. --- plugins/mod_tls.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/mod_tls.lua b/plugins/mod_tls.lua index eb208e28..8c0c4e20 100644 --- a/plugins/mod_tls.lua +++ b/plugins/mod_tls.lua @@ -36,7 +36,7 @@ local host = hosts[module.host]; local ssl_ctx_c2s, ssl_ctx_s2sout, ssl_ctx_s2sin; local ssl_cfg_c2s, ssl_cfg_s2sout, ssl_cfg_s2sin; -function module.load() +function module.load(reload) local NULL, err = {}; local modhost = module.host; local parent = modhost:match("%.(.*)$"); @@ -63,6 +63,12 @@ function module.load() module:log("debug", "Creating context for s2sin"); ssl_ctx_s2sin, err, ssl_cfg_s2sin = create_context(host.host, "server", host_s2s, host_ssl, global_s2s); -- for incoming server connections if not ssl_ctx_s2sin then module:log("error", "Error creating contexts for s2sin: %s", err); end + + if reload then + module:log("info", "Certificates reloaded"); + else + module:log("info", "Certificates loaded"); + end end module:hook_global("config-reloaded", module.load); -- cgit v1.2.3 From ee464bb36d566f5f47e0ab3d3ba562f227fb47bd Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 29 Apr 2020 18:04:05 +0200 Subject: mod_muc_mam: Fix stanza id filter event name, fixes #1546 Nice typo --- plugins/mod_muc_mam.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_muc_mam.lua b/plugins/mod_muc_mam.lua index 5a01324d..8216e2e6 100644 --- a/plugins/mod_muc_mam.lua +++ b/plugins/mod_muc_mam.lua @@ -309,7 +309,7 @@ module:hook("muc-get-history", function (event) return true; end, 1); -module:hook("muc-broadcast-messages", function (event) +module:hook("muc-broadcast-message", function (event) local room, stanza = event.room, event.stanza; -- Filter out that claim to be from us -- cgit v1.2.3 From 6b5447031cea9e858a3efa5051177b00184ab0da Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 29 Apr 2020 18:04:05 +0200 Subject: mod_muc_mam: Fix stanza id filter event name, fixes #1546 Nice typo --- plugins/mod_muc_mam.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_muc_mam.lua b/plugins/mod_muc_mam.lua index 5a01324d..8216e2e6 100644 --- a/plugins/mod_muc_mam.lua +++ b/plugins/mod_muc_mam.lua @@ -309,7 +309,7 @@ module:hook("muc-get-history", function (event) return true; end, 1); -module:hook("muc-broadcast-messages", function (event) +module:hook("muc-broadcast-message", function (event) local room, stanza = event.room, event.stanza; -- Filter out that claim to be from us -- cgit v1.2.3 From b199403ab512344f8f05ceef1a8bd20c399da5b8 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 8 May 2020 22:39:01 +0200 Subject: mod_muc_mam: Fix missing advertising of XEP-0359, fixes #1547 --- plugins/mod_muc_mam.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/mod_muc_mam.lua b/plugins/mod_muc_mam.lua index 8216e2e6..88018f72 100644 --- a/plugins/mod_muc_mam.lua +++ b/plugins/mod_muc_mam.lua @@ -395,6 +395,7 @@ module:add_feature(xmlns_mam); module:hook("muc-disco#info", function(event) event.reply:tag("feature", {var=xmlns_mam}):up(); + event.reply:tag("feature", {var=xmlns_st_id}):up(); end); -- Cleanup -- cgit v1.2.3 From 6860e08ad618d337b26f91b5c07add01a4ac4e3a Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 8 May 2020 22:39:01 +0200 Subject: mod_muc_mam: Fix missing advertising of XEP-0359, fixes #1547 --- plugins/mod_muc_mam.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/mod_muc_mam.lua b/plugins/mod_muc_mam.lua index 8216e2e6..88018f72 100644 --- a/plugins/mod_muc_mam.lua +++ b/plugins/mod_muc_mam.lua @@ -395,6 +395,7 @@ module:add_feature(xmlns_mam); module:hook("muc-disco#info", function(event) event.reply:tag("feature", {var=xmlns_mam}):up(); + event.reply:tag("feature", {var=xmlns_st_id}):up(); end); -- Cleanup -- cgit v1.2.3 From 3e5bae8965ab0c707919cd1012881f0fa188a6d1 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 8 May 2020 23:47:48 +0200 Subject: mod_csi_simple: Consider XEP-0353: Jingle Message Initiation important Improves experience with VoIP calls initiated via JMI Closes #1548 --- plugins/mod_csi_simple.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/mod_csi_simple.lua b/plugins/mod_csi_simple.lua index da2dd953..7fb6b41f 100644 --- a/plugins/mod_csi_simple.lua +++ b/plugins/mod_csi_simple.lua @@ -79,6 +79,9 @@ module:hook("csi-is-stanza-important", function (event) if stanza:get_child("encryption", "urn:xmpp:eme:0") then return true; end + if stanza:get_child(nil, "urn:xmpp:jingle-message:0") then + return true; + end return false; end return true; -- cgit v1.2.3 From f66524ac88716201ddb777411dba3419526ba0de Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 8 May 2020 23:47:48 +0200 Subject: mod_csi_simple: Consider XEP-0353: Jingle Message Initiation important Improves experience with VoIP calls initiated via JMI Closes #1548 --- plugins/mod_csi_simple.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/mod_csi_simple.lua b/plugins/mod_csi_simple.lua index da2dd953..7fb6b41f 100644 --- a/plugins/mod_csi_simple.lua +++ b/plugins/mod_csi_simple.lua @@ -79,6 +79,9 @@ module:hook("csi-is-stanza-important", function (event) if stanza:get_child("encryption", "urn:xmpp:eme:0") then return true; end + if stanza:get_child(nil, "urn:xmpp:jingle-message:0") then + return true; + end return false; end return true; -- cgit v1.2.3 From 3e4cbaf0f918f0c392518c055aef2593a6b9b1ed Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 11 May 2020 21:14:02 +0200 Subject: mod_muc_mam: Remove spoofed archive IDs before archiving, fix #1552 The stanza-id added during archiving looks exactly like what should be stripped, so the stripping must happen before archiving. Getting priorities right is hard! Also no test coverage yet. --- plugins/mod_muc_mam.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_muc_mam.lua b/plugins/mod_muc_mam.lua index 88018f72..711bf163 100644 --- a/plugins/mod_muc_mam.lua +++ b/plugins/mod_muc_mam.lua @@ -324,7 +324,7 @@ module:hook("muc-broadcast-message", function (event) return tag; end); -end, 0); +end, 1); -- Handle messages local function save_to_history(self, stanza) -- cgit v1.2.3 From 9a35d506e24a7441b59abfcbc25d1e7f85b3b1d9 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 11 May 2020 21:14:02 +0200 Subject: mod_muc_mam: Remove spoofed archive IDs before archiving, fix #1552 The stanza-id added during archiving looks exactly like what should be stripped, so the stripping must happen before archiving. Getting priorities right is hard! Also no test coverage yet. --- plugins/mod_muc_mam.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_muc_mam.lua b/plugins/mod_muc_mam.lua index 88018f72..711bf163 100644 --- a/plugins/mod_muc_mam.lua +++ b/plugins/mod_muc_mam.lua @@ -324,7 +324,7 @@ module:hook("muc-broadcast-message", function (event) return tag; end); -end, 0); +end, 1); -- Handle messages local function save_to_history(self, stanza) -- cgit v1.2.3 From 984bf1c1bfc83537db694e0a340e5f44772ee6ba Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 15 May 2020 21:22:35 +0200 Subject: mod_storage_internal: Fix error in time limited queries on items without 'when' field, fixes #1557 --- plugins/mod_storage_internal.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/mod_storage_internal.lua b/plugins/mod_storage_internal.lua index 42b451bd..0becfc8f 100644 --- a/plugins/mod_storage_internal.lua +++ b/plugins/mod_storage_internal.lua @@ -104,12 +104,14 @@ function archive:find(username, query) end if query.start then items:filter(function (item) - return item.when >= query.start; + local when = item.when or datetime.parse(item.attr.stamp); + return when >= query.start; end); end if query["end"] then items:filter(function (item) - return item.when <= query["end"]; + local when = item.when or datetime.parse(item.attr.stamp); + return when <= query["end"]; end); end count = #items; -- cgit v1.2.3 From f62650ffab2ee68ba037bd1a02c57890c6ebb78d Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 15 May 2020 21:22:35 +0200 Subject: mod_storage_internal: Fix error in time limited queries on items without 'when' field, fixes #1557 --- plugins/mod_storage_internal.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/mod_storage_internal.lua b/plugins/mod_storage_internal.lua index 42b451bd..0becfc8f 100644 --- a/plugins/mod_storage_internal.lua +++ b/plugins/mod_storage_internal.lua @@ -104,12 +104,14 @@ function archive:find(username, query) end if query.start then items:filter(function (item) - return item.when >= query.start; + local when = item.when or datetime.parse(item.attr.stamp); + return when >= query.start; end); end if query["end"] then items:filter(function (item) - return item.when <= query["end"]; + local when = item.when or datetime.parse(item.attr.stamp); + return when <= query["end"]; end); end count = #items; -- cgit v1.2.3 From f72fa3b2abd2bf18540460b37bf9ca1c8a479b7f Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 22 May 2020 20:59:01 +0200 Subject: util.sasl.scram: Apply saslprep before hashing password, fixes #1560 --- util/sasl/scram.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/util/sasl/scram.lua b/util/sasl/scram.lua index 043f328b..f64feb8b 100644 --- a/util/sasl/scram.lua +++ b/util/sasl/scram.lua @@ -106,6 +106,10 @@ local function getAuthenticationDatabaseSHA1(password, salt, iteration_count) if iteration_count < 4096 then log("warn", "Iteration count < 4096 which is the suggested minimum according to RFC 5802.") end + password = saslprep(password); + if not password then + return false, "password fails SASLprep"; + 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"); -- cgit v1.2.3 From c4bc5d5c13d97f34f4792f595235cd8157165bf9 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 22 May 2020 20:59:01 +0200 Subject: util.sasl.scram: Apply saslprep before hashing password, fixes #1560 --- util/sasl/scram.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/util/sasl/scram.lua b/util/sasl/scram.lua index 043f328b..f64feb8b 100644 --- a/util/sasl/scram.lua +++ b/util/sasl/scram.lua @@ -106,6 +106,10 @@ local function getAuthenticationDatabaseSHA1(password, salt, iteration_count) if iteration_count < 4096 then log("warn", "Iteration count < 4096 which is the suggested minimum according to RFC 5802.") end + password = saslprep(password); + if not password then + return false, "password fails SASLprep"; + 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"); -- cgit v1.2.3 From 7621990511255e6d9199af6451a4791abde3aea5 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 22 May 2020 21:05:45 +0200 Subject: util.sasl.plain: Apply saslprep to stored password Fixes something like #1560 here too. The password sent by the user already had saslprep applied. --- util/sasl/plain.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/sasl/plain.lua b/util/sasl/plain.lua index 00c6bd20..43a66c5b 100644 --- a/util/sasl/plain.lua +++ b/util/sasl/plain.lua @@ -70,7 +70,7 @@ local function plain(self, message) if self.profile.plain then local correct_password; correct_password, state = self.profile.plain(self, authentication, self.realm); - correct = (correct_password == password); + correct = (saslprep(correct_password) == password); elseif self.profile.plain_test then correct, state = self.profile.plain_test(self, authentication, password, self.realm); end -- cgit v1.2.3 From d5a9b65197f506263e262997ce8610487f6a5093 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 22 May 2020 21:05:45 +0200 Subject: util.sasl.plain: Apply saslprep to stored password Fixes something like #1560 here too. The password sent by the user already had saslprep applied. --- util/sasl/plain.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/sasl/plain.lua b/util/sasl/plain.lua index 00c6bd20..43a66c5b 100644 --- a/util/sasl/plain.lua +++ b/util/sasl/plain.lua @@ -70,7 +70,7 @@ local function plain(self, message) if self.profile.plain then local correct_password; correct_password, state = self.profile.plain(self, authentication, self.realm); - correct = (correct_password == password); + correct = (saslprep(correct_password) == password); elseif self.profile.plain_test then correct, state = self.profile.plain_test(self, authentication, password, self.realm); end -- cgit v1.2.3 From 5a0f5d2de06dfec97d1f736286a8f6be1625dece Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 23 May 2020 14:17:04 +0200 Subject: mod_auth_internal_*: Apply saslprep to passwords Related to #1560 --- plugins/mod_auth_insecure.lua | 5 +++++ plugins/mod_auth_internal_hashed.lua | 7 ++++++- plugins/mod_auth_internal_plain.lua | 15 ++++++++++++++- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/plugins/mod_auth_insecure.lua b/plugins/mod_auth_insecure.lua index 9e23c29f..dc5ee616 100644 --- a/plugins/mod_auth_insecure.lua +++ b/plugins/mod_auth_insecure.lua @@ -9,6 +9,7 @@ local datamanager = require "util.datamanager"; local new_sasl = require "util.sasl".new; +local saslprep = require "util.encodings".stringprep.saslprep; local host = module.host; local provider = { name = "insecure" }; @@ -21,6 +22,10 @@ end function provider.set_password(username, password) local account = datamanager.load(username, host, "accounts"); + password = saslprep(password); + if not password then + return nil, "Password fails SASLprep."; + end if account then account.password = password; return datamanager.store(username, host, "accounts", account); diff --git a/plugins/mod_auth_internal_hashed.lua b/plugins/mod_auth_internal_hashed.lua index 083f648b..15058098 100644 --- a/plugins/mod_auth_internal_hashed.lua +++ b/plugins/mod_auth_internal_hashed.lua @@ -15,6 +15,7 @@ local generate_uuid = require "util.uuid".generate; local new_sasl = require "util.sasl".new; local hex = require"util.hex"; local to_hex, from_hex = hex.to, hex.from; +local saslprep = require "util.encodings".stringprep.saslprep; local log = module._log; local host = module.host; @@ -32,9 +33,13 @@ local provider = {}; function provider.test_password(username, password) log("debug", "test password for user '%s'", username); local credentials = accounts:get(username) or {}; + password = saslprep(password); + if not password then + return nil, "Password fails SASLprep."; + end if credentials.password ~= nil and string.len(credentials.password) ~= 0 then - if credentials.password ~= password then + if saslprep(credentials.password) ~= password then return nil, "Auth failed. Provided password is incorrect."; end diff --git a/plugins/mod_auth_internal_plain.lua b/plugins/mod_auth_internal_plain.lua index 276efb64..56ef52d5 100644 --- a/plugins/mod_auth_internal_plain.lua +++ b/plugins/mod_auth_internal_plain.lua @@ -8,6 +8,7 @@ local usermanager = require "core.usermanager"; local new_sasl = require "util.sasl".new; +local saslprep = require "util.encodings".stringprep.saslprep; local log = module._log; local host = module.host; @@ -20,8 +21,12 @@ local provider = {}; function provider.test_password(username, password) log("debug", "test password for user '%s'", username); local credentials = accounts:get(username) or {}; + password = saslprep(password); + if not password then + return nil, "Password fails SASLprep."; + end - if password == credentials.password then + if password == saslprep(credentials.password) then return true; else return nil, "Auth failed. Invalid username or password."; @@ -35,6 +40,10 @@ end function provider.set_password(username, password) log("debug", "set_password for username '%s'", username); + password = saslprep(password); + if not password then + return nil, "Password fails SASLprep."; + end local account = accounts:get(username); if account then account.password = password; @@ -57,6 +66,10 @@ function provider.users() end function provider.create_user(username, password) + password = saslprep(password); + if not password then + return nil, "Password fails SASLprep."; + end return accounts:set(username, {password = password}); end -- cgit v1.2.3 From 0c44ebb5a382797bb4c7bee6a851dd39dd36842b Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 23 May 2020 14:17:04 +0200 Subject: mod_auth_internal_*: Apply saslprep to passwords Related to #1560 --- plugins/mod_auth_insecure.lua | 5 +++++ plugins/mod_auth_internal_hashed.lua | 7 ++++++- plugins/mod_auth_internal_plain.lua | 15 ++++++++++++++- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/plugins/mod_auth_insecure.lua b/plugins/mod_auth_insecure.lua index 9e23c29f..dc5ee616 100644 --- a/plugins/mod_auth_insecure.lua +++ b/plugins/mod_auth_insecure.lua @@ -9,6 +9,7 @@ local datamanager = require "util.datamanager"; local new_sasl = require "util.sasl".new; +local saslprep = require "util.encodings".stringprep.saslprep; local host = module.host; local provider = { name = "insecure" }; @@ -21,6 +22,10 @@ end function provider.set_password(username, password) local account = datamanager.load(username, host, "accounts"); + password = saslprep(password); + if not password then + return nil, "Password fails SASLprep."; + end if account then account.password = password; return datamanager.store(username, host, "accounts", account); diff --git a/plugins/mod_auth_internal_hashed.lua b/plugins/mod_auth_internal_hashed.lua index 083f648b..15058098 100644 --- a/plugins/mod_auth_internal_hashed.lua +++ b/plugins/mod_auth_internal_hashed.lua @@ -15,6 +15,7 @@ local generate_uuid = require "util.uuid".generate; local new_sasl = require "util.sasl".new; local hex = require"util.hex"; local to_hex, from_hex = hex.to, hex.from; +local saslprep = require "util.encodings".stringprep.saslprep; local log = module._log; local host = module.host; @@ -32,9 +33,13 @@ local provider = {}; function provider.test_password(username, password) log("debug", "test password for user '%s'", username); local credentials = accounts:get(username) or {}; + password = saslprep(password); + if not password then + return nil, "Password fails SASLprep."; + end if credentials.password ~= nil and string.len(credentials.password) ~= 0 then - if credentials.password ~= password then + if saslprep(credentials.password) ~= password then return nil, "Auth failed. Provided password is incorrect."; end diff --git a/plugins/mod_auth_internal_plain.lua b/plugins/mod_auth_internal_plain.lua index 276efb64..56ef52d5 100644 --- a/plugins/mod_auth_internal_plain.lua +++ b/plugins/mod_auth_internal_plain.lua @@ -8,6 +8,7 @@ local usermanager = require "core.usermanager"; local new_sasl = require "util.sasl".new; +local saslprep = require "util.encodings".stringprep.saslprep; local log = module._log; local host = module.host; @@ -20,8 +21,12 @@ local provider = {}; function provider.test_password(username, password) log("debug", "test password for user '%s'", username); local credentials = accounts:get(username) or {}; + password = saslprep(password); + if not password then + return nil, "Password fails SASLprep."; + end - if password == credentials.password then + if password == saslprep(credentials.password) then return true; else return nil, "Auth failed. Invalid username or password."; @@ -35,6 +40,10 @@ end function provider.set_password(username, password) log("debug", "set_password for username '%s'", username); + password = saslprep(password); + if not password then + return nil, "Password fails SASLprep."; + end local account = accounts:get(username); if account then account.password = password; @@ -57,6 +66,10 @@ function provider.users() end function provider.create_user(username, password) + password = saslprep(password); + if not password then + return nil, "Password fails SASLprep."; + end return accounts:set(username, {password = password}); end -- cgit v1.2.3 From 396ae378944abc3981024617ae87605974364335 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 21 Jun 2020 19:20:54 +0200 Subject: mod_muc_mam: Don't strip MUC tags, fix #1567 --- plugins/mod_muc_mam.lua | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugins/mod_muc_mam.lua b/plugins/mod_muc_mam.lua index 711bf163..f0417889 100644 --- a/plugins/mod_muc_mam.lua +++ b/plugins/mod_muc_mam.lua @@ -318,9 +318,6 @@ module:hook("muc-broadcast-message", function (event) and jid_prep(tag.attr.by) == room.jid then return nil; end - if tag.name == "x" and tag.attr.xmlns == xmlns_muc_user then - return nil; - end return tag; end); -- cgit v1.2.3 From 7448c5c1b181055cabbe707c2deb9e4b575df507 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 22 Jun 2020 14:56:44 +0200 Subject: util.startup: Remove duplicated initialization of logging (fix #1527) --- util/startup.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/util/startup.lua b/util/startup.lua index 24ed6026..2a7e8a36 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -597,7 +597,6 @@ function startup.prosody() startup.sandbox_require(); startup.set_function_metatable(); startup.check_dependencies(); - startup.init_logging(); startup.load_libraries(); startup.setup_plugindir(); startup.setup_datadir(); -- cgit v1.2.3 From d12c7e9416b242120df35419220a332196cd8413 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 23 Jun 2020 15:39:31 +0200 Subject: net.http.server: Strip port from Host header in IPv6 friendly way (fix #1302) E.g. given `[::1]:5280` it would previously result in only `[` instead of the correct `[::1]` --- net/http/server.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/http/server.lua b/net/http/server.lua index 9b63d516..d0bd3294 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -207,7 +207,7 @@ function handle_request(conn, request, finish_cb) }; conn._http_open_response = response; - local host = (request.headers.host or ""):match("[^:]+"); + local host = (request.headers.host or ""):gsub(":%d+$",""); -- Some sanity checking local err_code, err; -- cgit v1.2.3 From ba013abb83c578589b4ed413b5a330d9e835d764 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 23 Jun 2020 15:43:57 +0200 Subject: net.http.server: Fix reporting of missing Host header The "Missing or invalid 'Host' header" case was dead code previously because `host` was always at least an empty string. --- net/http/server.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/http/server.lua b/net/http/server.lua index d0bd3294..18704962 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -207,7 +207,8 @@ function handle_request(conn, request, finish_cb) }; conn._http_open_response = response; - local host = (request.headers.host or ""):gsub(":%d+$",""); + local host = request.headers.host; + if host then host = host:gsub(":%d+$",""); end -- Some sanity checking local err_code, err; -- cgit v1.2.3 From cc5eeb60cdba4f8aea1d45b7c8a5c2a532462d2e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 7 Jul 2020 13:52:25 +0100 Subject: net.http: Fix traceback on invalid URL passed to request() --- net/http.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/http.lua b/net/http.lua index fe5250ac..0adac26c 100644 --- a/net/http.lua +++ b/net/http.lua @@ -183,14 +183,15 @@ end local function request(self, u, ex, callback) local req = url.parse(u); - req.url = u; - req.http = self; if not (req and req.host) then callback("invalid-url", 0, req); return nil, "invalid-url"; end + req.url = u; + req.http = self; + if not req.path then req.path = "/"; end -- cgit v1.2.3 From e3a2cc2d407e3b1736843dd90f898caf53a8cd3a Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 10 Jul 2020 13:00:02 +0100 Subject: net.resolvers.basic: Default conn_type to 'tcp' consistently if unspecified (thanks marc0s) Fixes a traceback when passed an IP address with no conn_type. --- net/resolvers/basic.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/resolvers/basic.lua b/net/resolvers/basic.lua index 08c71ef5..867ccf60 100644 --- a/net/resolvers/basic.lua +++ b/net/resolvers/basic.lua @@ -58,6 +58,7 @@ end local function new(hostname, port, conn_type, extra) local ascii_host = idna_to_ascii(hostname); local targets = nil; + conn_type = conn_type or "tcp"; local is_ip = inet_pton(hostname); if not is_ip and hostname:sub(1,1) == '[' then @@ -75,7 +76,7 @@ local function new(hostname, port, conn_type, extra) return setmetatable({ hostname = ascii_host; port = port; - conn_type = conn_type or "tcp"; + conn_type = conn_type; extra = extra; targets = targets; }, resolver_mt); -- cgit v1.2.3 From 1ec3ab3c2fc96368675e3b679cf50c3396ef8369 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 8 Aug 2020 13:11:11 +0100 Subject: net.http: Re-expose destroy_request() function This was accidentally turned private in 647adfd8f738 as part of refactoring for Lua 5.2+. --- net/http.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/net/http.lua b/net/http.lua index 0adac26c..14107bf4 100644 --- a/net/http.lua +++ b/net/http.lua @@ -302,4 +302,5 @@ return { urldecode = util_http.urldecode; formencode = util_http.formencode; formdecode = util_http.formdecode; + destroy_request = destroy_request; }; -- cgit v1.2.3 From 09fd5f5c24ce919563cdadf0919901664cc3de30 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 8 Aug 2020 13:13:50 +0100 Subject: net.http: Add request:cancel() method This is a new API that should be used in preference to http.destroy_request() when possible, as it ensures the callback is always called (with an error of course). APIs that have edge-cases where they don't call callbacks have, from experience, shown to be difficult to work with and often lead to unintentional leaks when the callback was expected to free up certain resources. --- net/http.lua | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/net/http.lua b/net/http.lua index 14107bf4..ae9d2974 100644 --- a/net/http.lua +++ b/net/http.lua @@ -56,6 +56,16 @@ local function destroy_request(request) end end +local function cancel_request(request, reason) + if request.callback then + request.callback(reason or "cancelled", 0, request); + request.callback = nil; + end + if request.conn then + destroy_request(request); + end +end + local function request_reader(request, data, err) if not request.parser then local function error_cb(reason) @@ -105,6 +115,7 @@ function listener.onconnect(conn) end req.reader = request_reader; req.state = "status"; + req.cancel = cancel_request; requests[req.conn] = req; -- cgit v1.2.3 From d0178fb089d0c757051959114b10e67820dfd981 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 1 Aug 2020 12:58:37 +0200 Subject: mod_s2s: Escape invalid XML in loggin (same way as mod_c2s) fix #1574 --- plugins/mod_s2s/mod_s2s.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/mod_s2s/mod_s2s.lua b/plugins/mod_s2s/mod_s2s.lua index aae37b7f..3f56b953 100644 --- a/plugins/mod_s2s/mod_s2s.lua +++ b/plugins/mod_s2s/mod_s2s.lua @@ -595,8 +595,7 @@ local function initialize_session(session) if data then local ok, err = stream:feed(data); if ok then return; end - log("warn", "Received invalid XML: %s", data); - log("warn", "Problem was: %s", err); + log("debug", "Received invalid XML (%s) %d bytes: %s", tostring(err), #data, data:sub(1, 300):gsub("[\r\n]+", " "):gsub("[%z\1-\31]", "_")); session:close("not-well-formed"); end end -- cgit v1.2.3 -- cgit v1.2.3 From ce940fed493f2c138fa43e220c0ebef3c846f09d Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 17 Aug 2020 23:01:14 +0200 Subject: net.server: Backport client parts of SNI support from trunk (#409) Partial backports of the following commits from trunk: 6c804b6b2ca2 net.http: Pass server name along for SNI (fixes #1408) 75d2874502c3 net.server_select: SNI support (#409) 9a905888b96c net.server_event: Add SNI support (#409) adc0672b700e net.server_epoll: Add support for SNI (#409) d4390c427a66 net.server: Handle server name (SNI) as extra argument --- net/http.lua | 2 +- net/server_epoll.lua | 20 +++++++++++++++----- net/server_event.lua | 17 ++++++++++++----- net/server_select.lua | 19 ++++++++++++++----- 4 files changed, 42 insertions(+), 16 deletions(-) diff --git a/net/http.lua b/net/http.lua index ae9d2974..0768cdab 100644 --- a/net/http.lua +++ b/net/http.lua @@ -272,7 +272,7 @@ local function request(self, u, ex, callback) sslctx = ex and ex.sslctx or self.options and self.options.sslctx; end - local http_service = basic_resolver.new(host, port_number); + local http_service = basic_resolver.new(host, port_number, "tcp", { servername = req.host }); connect(http_service, listener, { sslctx = sslctx }, req); self.events.fire_event("request", { http = self, request = req, url = u }); diff --git a/net/server_epoll.lua b/net/server_epoll.lua index 2182d56a..953bbb11 100644 --- a/net/server_epoll.lua +++ b/net/server_epoll.lua @@ -483,6 +483,9 @@ function interface:tlshandskake() end conn:settimeout(0); self.conn = conn; + if conn.sni and self.servername then + conn:sni(self.servername); + end self:on("starttls"); self.ondrain = nil; self.onwritable = interface.tlshandskake; @@ -512,7 +515,7 @@ function interface:tlshandskake() end end -local function wrapsocket(client, server, read_size, listeners, tls_ctx) -- luasocket object -> interface object +local function wrapsocket(client, server, read_size, listeners, tls_ctx, extra) -- luasocket object -> interface object client:settimeout(0); local conn = setmetatable({ conn = client; @@ -523,8 +526,15 @@ local function wrapsocket(client, server, read_size, listeners, tls_ctx) -- luas writebuffer = {}; tls_ctx = tls_ctx or (server and server.tls_ctx); tls_direct = server and server.tls_direct; + extra = extra; }, interface_mt); + if extra then + if extra.servername then + conn.servername = extra.servername; + end + end + conn:updatenames(); return conn; end @@ -617,8 +627,8 @@ local function addserver(addr, port, listeners, read_size, tls_ctx) end -- COMPAT -local function wrapclient(conn, addr, port, listeners, read_size, tls_ctx) - local client = wrapsocket(conn, nil, read_size, listeners, tls_ctx); +local function wrapclient(conn, addr, port, listeners, read_size, tls_ctx, extra) + local client = wrapsocket(conn, nil, read_size, listeners, tls_ctx, extra); if not client.peername then client.peername, client.peerport = addr, port; end @@ -631,7 +641,7 @@ local function wrapclient(conn, addr, port, listeners, read_size, tls_ctx) end -- New outgoing TCP connection -local function addclient(addr, port, listeners, read_size, tls_ctx, typ) +local function addclient(addr, port, listeners, read_size, tls_ctx, typ, extra) local create; if not typ then local n = inet_pton(addr); @@ -653,7 +663,7 @@ local function addclient(addr, port, listeners, read_size, tls_ctx, typ) if not ok then return ok, err; end local ok, err = conn:setpeername(addr, port); if not ok and err ~= "timeout" then return ok, err; end - local client = wrapsocket(conn, nil, read_size, listeners, tls_ctx) + local client = wrapsocket(conn, nil, read_size, listeners, tls_ctx, extra) local ok, err = client:init(); if not ok then return ok, err; end if tls_ctx then diff --git a/net/server_event.lua b/net/server_event.lua index 11bd6a29..746526ce 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -164,6 +164,11 @@ function interface_mt:_start_ssl(call_onconnect) -- old socket will be destroyed debug( "fatal error while ssl wrapping:", err ) return false end + + if self.conn.sni and self.servername then + self.conn:sni(self.servername); + end + self.conn:settimeout( 0 ) -- set non blocking local handshakecallback = coroutine_wrap(function( event ) local _, err @@ -456,7 +461,7 @@ end -- End of client interface methods -local function handleclient( client, ip, port, server, pattern, listener, sslctx ) -- creates an client interface +local function handleclient( client, ip, port, server, pattern, listener, sslctx, extra ) -- creates an client interface --vdebug("creating client interfacce...") local interface = { type = "client"; @@ -492,6 +497,8 @@ local function handleclient( client, ip, port, server, pattern, listener, sslctx _serverport = (server and server:port() or nil), _sslctx = sslctx; -- parameters _usingssl = false; -- client is using ssl; + extra = extra; + servername = extra and extra.servername; } if not has_luasec then interface.starttls = false; end interface.id = tostring(interface):match("%x+$"); @@ -716,14 +723,14 @@ local function addserver( addr, port, listener, pattern, sslctx, startssl ) -- return interface end -local function wrapclient( client, ip, port, listeners, pattern, sslctx ) - local interface = handleclient( client, ip, port, nil, pattern, listeners, sslctx ) +local function wrapclient( client, ip, port, listeners, pattern, sslctx, extra ) + local interface = handleclient( client, ip, port, nil, pattern, listeners, sslctx, extra ) interface:_start_connection(sslctx) return interface, client --function handleclient( client, ip, port, server, pattern, listener, _, sslctx ) -- creates an client interface end -local function addclient( addr, serverport, listener, pattern, sslctx, typ ) +local function addclient( addr, serverport, listener, pattern, sslctx, typ, extra ) if sslctx and not has_luasec then debug "need luasec, but not available" return nil, "luasec not found" @@ -750,7 +757,7 @@ local function addclient( addr, serverport, listener, pattern, sslctx, typ ) local res, err = client:setpeername( addr, serverport ) -- connect if res or ( err == "timeout" ) then local ip, port = client:getsockname( ) - local interface = wrapclient( client, ip, serverport, listener, pattern, sslctx ) + local interface = wrapclient( client, ip, serverport, listener, pattern, sslctx, extra ) debug( "new connection id:", interface.id ) return interface, err else diff --git a/net/server_select.lua b/net/server_select.lua index 1a40a6d3..deb8fe48 100644 --- a/net/server_select.lua +++ b/net/server_select.lua @@ -264,7 +264,7 @@ wrapserver = function( listeners, socket, ip, serverport, pattern, sslctx ) -- t return handler end -wrapconnection = function( server, listeners, socket, ip, serverport, clientport, pattern, sslctx ) -- this function wraps a client to a handler object +wrapconnection = function( server, listeners, socket, ip, serverport, clientport, pattern, sslctx, extra ) -- this function wraps a client to a handler object if socket:getfd() >= _maxfd then out_error("server.lua: Disallowed FD number: "..socket:getfd()) -- PROTIP: Switch to libevent @@ -314,6 +314,11 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport local handler = bufferqueue -- saves a table ^_^ + handler.extra = extra + if extra then + handler.servername = extra.servername + end + handler.dispatch = function( ) return dispatch end @@ -624,6 +629,10 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport return nil, err -- fatal error end + if socket.sni and self.servername then + socket:sni(self.servername); + end + socket:settimeout( 0 ) -- add the new socket to our system @@ -977,8 +986,8 @@ end --// EXPERIMENTAL //-- -local wrapclient = function( socket, ip, serverport, listeners, pattern, sslctx ) - local handler, socket, err = wrapconnection( nil, listeners, socket, ip, serverport, "clientport", pattern, sslctx ) +local wrapclient = function( socket, ip, serverport, listeners, pattern, sslctx, extra ) + local handler, socket, err = wrapconnection( nil, listeners, socket, ip, serverport, "clientport", pattern, sslctx, extra) if not handler then return nil, err end _socketlist[ socket ] = handler if not sslctx then @@ -997,7 +1006,7 @@ local wrapclient = function( socket, ip, serverport, listeners, pattern, sslctx return handler, socket end -local addclient = function( address, port, listeners, pattern, sslctx, typ ) +local addclient = function( address, port, listeners, pattern, sslctx, typ, extra ) local err if type( listeners ) ~= "table" then err = "invalid listener table" @@ -1034,7 +1043,7 @@ local addclient = function( address, port, listeners, pattern, sslctx, typ ) client:settimeout( 0 ) local ok, err = client:setpeername( address, port ) if ok or err == "timeout" or err == "Operation already in progress" then - return wrapclient( client, address, port, listeners, pattern, sslctx ) + return wrapclient( client, address, port, listeners, pattern, sslctx, extra ) else return nil, err end -- cgit v1.2.3 From 0b3dfb6e44835e521e88359fb4b0ccc3d52f57c4 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 15 Sep 2020 09:08:21 +0100 Subject: net.http: Add feature discovery (currently just contains SNI) --- net/http.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/http.lua b/net/http.lua index 0768cdab..7335d210 100644 --- a/net/http.lua +++ b/net/http.lua @@ -314,4 +314,7 @@ return { formencode = util_http.formencode; formdecode = util_http.formdecode; destroy_request = destroy_request; + features = { + sni = true; + }; }; -- cgit v1.2.3 From 5f8f75ddab1610b193ced17dec46164b6db72db8 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 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 dab7182de90bdbf4ee134eb77886fc6923a8f3ba Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 21 Jun 2020 19:20:54 +0200 Subject: mod_muc_mam: Don't strip MUC tags, fix #1567 --- plugins/mod_muc_mam.lua | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugins/mod_muc_mam.lua b/plugins/mod_muc_mam.lua index 711bf163..f0417889 100644 --- a/plugins/mod_muc_mam.lua +++ b/plugins/mod_muc_mam.lua @@ -318,9 +318,6 @@ module:hook("muc-broadcast-message", function (event) and jid_prep(tag.attr.by) == room.jid then return nil; end - if tag.name == "x" and tag.attr.xmlns == xmlns_muc_user then - return nil; - end return tag; end); -- cgit v1.2.3 From 54989d19aab814da0056ffbe083435487dcf28a7 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 22 Jun 2020 14:56:44 +0200 Subject: util.startup: Remove duplicated initialization of logging (fix #1527) --- util/startup.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/util/startup.lua b/util/startup.lua index 24ed6026..2a7e8a36 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -597,7 +597,6 @@ function startup.prosody() startup.sandbox_require(); startup.set_function_metatable(); startup.check_dependencies(); - startup.init_logging(); startup.load_libraries(); startup.setup_plugindir(); startup.setup_datadir(); -- cgit v1.2.3 From 5f5b5f09427a8de6ec3261c6850da5e63f579d70 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 23 Jun 2020 15:39:31 +0200 Subject: net.http.server: Strip port from Host header in IPv6 friendly way (fix #1302) E.g. given `[::1]:5280` it would previously result in only `[` instead of the correct `[::1]` --- net/http/server.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/http/server.lua b/net/http/server.lua index 9b63d516..d0bd3294 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -207,7 +207,7 @@ function handle_request(conn, request, finish_cb) }; conn._http_open_response = response; - local host = (request.headers.host or ""):match("[^:]+"); + local host = (request.headers.host or ""):gsub(":%d+$",""); -- Some sanity checking local err_code, err; -- cgit v1.2.3 From 28aad07a07e730f5f90c032649b9581c67cde4bf Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 23 Jun 2020 15:43:57 +0200 Subject: net.http.server: Fix reporting of missing Host header The "Missing or invalid 'Host' header" case was dead code previously because `host` was always at least an empty string. --- net/http/server.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/http/server.lua b/net/http/server.lua index d0bd3294..18704962 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -207,7 +207,8 @@ function handle_request(conn, request, finish_cb) }; conn._http_open_response = response; - local host = (request.headers.host or ""):gsub(":%d+$",""); + local host = request.headers.host; + if host then host = host:gsub(":%d+$",""); end -- Some sanity checking local err_code, err; -- cgit v1.2.3 From d2edbeb5ec784f1a2c57098fa0508718c802ab74 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 7 Jul 2020 13:52:25 +0100 Subject: net.http: Fix traceback on invalid URL passed to request() --- net/http.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/http.lua b/net/http.lua index fe5250ac..0adac26c 100644 --- a/net/http.lua +++ b/net/http.lua @@ -183,14 +183,15 @@ end local function request(self, u, ex, callback) local req = url.parse(u); - req.url = u; - req.http = self; if not (req and req.host) then callback("invalid-url", 0, req); return nil, "invalid-url"; end + req.url = u; + req.http = self; + if not req.path then req.path = "/"; end -- cgit v1.2.3 From e0deaa36fe2735e59a62f50c44b5b5704c53b28b Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 10 Jul 2020 13:00:02 +0100 Subject: net.resolvers.basic: Default conn_type to 'tcp' consistently if unspecified (thanks marc0s) Fixes a traceback when passed an IP address with no conn_type. --- net/resolvers/basic.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/resolvers/basic.lua b/net/resolvers/basic.lua index 08c71ef5..867ccf60 100644 --- a/net/resolvers/basic.lua +++ b/net/resolvers/basic.lua @@ -58,6 +58,7 @@ end local function new(hostname, port, conn_type, extra) local ascii_host = idna_to_ascii(hostname); local targets = nil; + conn_type = conn_type or "tcp"; local is_ip = inet_pton(hostname); if not is_ip and hostname:sub(1,1) == '[' then @@ -75,7 +76,7 @@ local function new(hostname, port, conn_type, extra) return setmetatable({ hostname = ascii_host; port = port; - conn_type = conn_type or "tcp"; + conn_type = conn_type; extra = extra; targets = targets; }, resolver_mt); -- cgit v1.2.3 From 566680e94768030f7484420f8ea2415a5c5fec75 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 8 Aug 2020 13:11:11 +0100 Subject: net.http: Re-expose destroy_request() function This was accidentally turned private in 647adfd8f738 as part of refactoring for Lua 5.2+. --- net/http.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/net/http.lua b/net/http.lua index 0adac26c..14107bf4 100644 --- a/net/http.lua +++ b/net/http.lua @@ -302,4 +302,5 @@ return { urldecode = util_http.urldecode; formencode = util_http.formencode; formdecode = util_http.formdecode; + destroy_request = destroy_request; }; -- cgit v1.2.3 From e1cc07c773b16c6a376dda569f5cf0bfab0998a0 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 8 Aug 2020 13:13:50 +0100 Subject: net.http: Add request:cancel() method This is a new API that should be used in preference to http.destroy_request() when possible, as it ensures the callback is always called (with an error of course). APIs that have edge-cases where they don't call callbacks have, from experience, shown to be difficult to work with and often lead to unintentional leaks when the callback was expected to free up certain resources. --- net/http.lua | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/net/http.lua b/net/http.lua index 14107bf4..ae9d2974 100644 --- a/net/http.lua +++ b/net/http.lua @@ -56,6 +56,16 @@ local function destroy_request(request) end end +local function cancel_request(request, reason) + if request.callback then + request.callback(reason or "cancelled", 0, request); + request.callback = nil; + end + if request.conn then + destroy_request(request); + end +end + local function request_reader(request, data, err) if not request.parser then local function error_cb(reason) @@ -105,6 +115,7 @@ function listener.onconnect(conn) end req.reader = request_reader; req.state = "status"; + req.cancel = cancel_request; requests[req.conn] = req; -- cgit v1.2.3 From a45a94ad3f2f7fbef2f361cbc25a4c5d8c256e05 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 1 Aug 2020 12:58:37 +0200 Subject: mod_s2s: Escape invalid XML in loggin (same way as mod_c2s) fix #1574 --- plugins/mod_s2s/mod_s2s.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/mod_s2s/mod_s2s.lua b/plugins/mod_s2s/mod_s2s.lua index aae37b7f..3f56b953 100644 --- a/plugins/mod_s2s/mod_s2s.lua +++ b/plugins/mod_s2s/mod_s2s.lua @@ -595,8 +595,7 @@ local function initialize_session(session) if data then local ok, err = stream:feed(data); if ok then return; end - log("warn", "Received invalid XML: %s", data); - log("warn", "Problem was: %s", err); + log("debug", "Received invalid XML (%s) %d bytes: %s", tostring(err), #data, data:sub(1, 300):gsub("[\r\n]+", " "):gsub("[%z\1-\31]", "_")); session:close("not-well-formed"); end end -- cgit v1.2.3 -- cgit v1.2.3 From 4c9f43a09efbe6387c6efa1f7742c61cedc67945 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 17 Aug 2020 23:01:14 +0200 Subject: net.server: Backport client parts of SNI support from trunk (#409) Partial backports of the following commits from trunk: 6c804b6b2ca2 net.http: Pass server name along for SNI (fixes #1408) 75d2874502c3 net.server_select: SNI support (#409) 9a905888b96c net.server_event: Add SNI support (#409) adc0672b700e net.server_epoll: Add support for SNI (#409) d4390c427a66 net.server: Handle server name (SNI) as extra argument --- net/http.lua | 2 +- net/server_epoll.lua | 20 +++++++++++++++----- net/server_event.lua | 17 ++++++++++++----- net/server_select.lua | 19 ++++++++++++++----- 4 files changed, 42 insertions(+), 16 deletions(-) diff --git a/net/http.lua b/net/http.lua index ae9d2974..0768cdab 100644 --- a/net/http.lua +++ b/net/http.lua @@ -272,7 +272,7 @@ local function request(self, u, ex, callback) sslctx = ex and ex.sslctx or self.options and self.options.sslctx; end - local http_service = basic_resolver.new(host, port_number); + local http_service = basic_resolver.new(host, port_number, "tcp", { servername = req.host }); connect(http_service, listener, { sslctx = sslctx }, req); self.events.fire_event("request", { http = self, request = req, url = u }); diff --git a/net/server_epoll.lua b/net/server_epoll.lua index 2182d56a..953bbb11 100644 --- a/net/server_epoll.lua +++ b/net/server_epoll.lua @@ -483,6 +483,9 @@ function interface:tlshandskake() end conn:settimeout(0); self.conn = conn; + if conn.sni and self.servername then + conn:sni(self.servername); + end self:on("starttls"); self.ondrain = nil; self.onwritable = interface.tlshandskake; @@ -512,7 +515,7 @@ function interface:tlshandskake() end end -local function wrapsocket(client, server, read_size, listeners, tls_ctx) -- luasocket object -> interface object +local function wrapsocket(client, server, read_size, listeners, tls_ctx, extra) -- luasocket object -> interface object client:settimeout(0); local conn = setmetatable({ conn = client; @@ -523,8 +526,15 @@ local function wrapsocket(client, server, read_size, listeners, tls_ctx) -- luas writebuffer = {}; tls_ctx = tls_ctx or (server and server.tls_ctx); tls_direct = server and server.tls_direct; + extra = extra; }, interface_mt); + if extra then + if extra.servername then + conn.servername = extra.servername; + end + end + conn:updatenames(); return conn; end @@ -617,8 +627,8 @@ local function addserver(addr, port, listeners, read_size, tls_ctx) end -- COMPAT -local function wrapclient(conn, addr, port, listeners, read_size, tls_ctx) - local client = wrapsocket(conn, nil, read_size, listeners, tls_ctx); +local function wrapclient(conn, addr, port, listeners, read_size, tls_ctx, extra) + local client = wrapsocket(conn, nil, read_size, listeners, tls_ctx, extra); if not client.peername then client.peername, client.peerport = addr, port; end @@ -631,7 +641,7 @@ local function wrapclient(conn, addr, port, listeners, read_size, tls_ctx) end -- New outgoing TCP connection -local function addclient(addr, port, listeners, read_size, tls_ctx, typ) +local function addclient(addr, port, listeners, read_size, tls_ctx, typ, extra) local create; if not typ then local n = inet_pton(addr); @@ -653,7 +663,7 @@ local function addclient(addr, port, listeners, read_size, tls_ctx, typ) if not ok then return ok, err; end local ok, err = conn:setpeername(addr, port); if not ok and err ~= "timeout" then return ok, err; end - local client = wrapsocket(conn, nil, read_size, listeners, tls_ctx) + local client = wrapsocket(conn, nil, read_size, listeners, tls_ctx, extra) local ok, err = client:init(); if not ok then return ok, err; end if tls_ctx then diff --git a/net/server_event.lua b/net/server_event.lua index 11bd6a29..746526ce 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -164,6 +164,11 @@ function interface_mt:_start_ssl(call_onconnect) -- old socket will be destroyed debug( "fatal error while ssl wrapping:", err ) return false end + + if self.conn.sni and self.servername then + self.conn:sni(self.servername); + end + self.conn:settimeout( 0 ) -- set non blocking local handshakecallback = coroutine_wrap(function( event ) local _, err @@ -456,7 +461,7 @@ end -- End of client interface methods -local function handleclient( client, ip, port, server, pattern, listener, sslctx ) -- creates an client interface +local function handleclient( client, ip, port, server, pattern, listener, sslctx, extra ) -- creates an client interface --vdebug("creating client interfacce...") local interface = { type = "client"; @@ -492,6 +497,8 @@ local function handleclient( client, ip, port, server, pattern, listener, sslctx _serverport = (server and server:port() or nil), _sslctx = sslctx; -- parameters _usingssl = false; -- client is using ssl; + extra = extra; + servername = extra and extra.servername; } if not has_luasec then interface.starttls = false; end interface.id = tostring(interface):match("%x+$"); @@ -716,14 +723,14 @@ local function addserver( addr, port, listener, pattern, sslctx, startssl ) -- return interface end -local function wrapclient( client, ip, port, listeners, pattern, sslctx ) - local interface = handleclient( client, ip, port, nil, pattern, listeners, sslctx ) +local function wrapclient( client, ip, port, listeners, pattern, sslctx, extra ) + local interface = handleclient( client, ip, port, nil, pattern, listeners, sslctx, extra ) interface:_start_connection(sslctx) return interface, client --function handleclient( client, ip, port, server, pattern, listener, _, sslctx ) -- creates an client interface end -local function addclient( addr, serverport, listener, pattern, sslctx, typ ) +local function addclient( addr, serverport, listener, pattern, sslctx, typ, extra ) if sslctx and not has_luasec then debug "need luasec, but not available" return nil, "luasec not found" @@ -750,7 +757,7 @@ local function addclient( addr, serverport, listener, pattern, sslctx, typ ) local res, err = client:setpeername( addr, serverport ) -- connect if res or ( err == "timeout" ) then local ip, port = client:getsockname( ) - local interface = wrapclient( client, ip, serverport, listener, pattern, sslctx ) + local interface = wrapclient( client, ip, serverport, listener, pattern, sslctx, extra ) debug( "new connection id:", interface.id ) return interface, err else diff --git a/net/server_select.lua b/net/server_select.lua index 1a40a6d3..deb8fe48 100644 --- a/net/server_select.lua +++ b/net/server_select.lua @@ -264,7 +264,7 @@ wrapserver = function( listeners, socket, ip, serverport, pattern, sslctx ) -- t return handler end -wrapconnection = function( server, listeners, socket, ip, serverport, clientport, pattern, sslctx ) -- this function wraps a client to a handler object +wrapconnection = function( server, listeners, socket, ip, serverport, clientport, pattern, sslctx, extra ) -- this function wraps a client to a handler object if socket:getfd() >= _maxfd then out_error("server.lua: Disallowed FD number: "..socket:getfd()) -- PROTIP: Switch to libevent @@ -314,6 +314,11 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport local handler = bufferqueue -- saves a table ^_^ + handler.extra = extra + if extra then + handler.servername = extra.servername + end + handler.dispatch = function( ) return dispatch end @@ -624,6 +629,10 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport return nil, err -- fatal error end + if socket.sni and self.servername then + socket:sni(self.servername); + end + socket:settimeout( 0 ) -- add the new socket to our system @@ -977,8 +986,8 @@ end --// EXPERIMENTAL //-- -local wrapclient = function( socket, ip, serverport, listeners, pattern, sslctx ) - local handler, socket, err = wrapconnection( nil, listeners, socket, ip, serverport, "clientport", pattern, sslctx ) +local wrapclient = function( socket, ip, serverport, listeners, pattern, sslctx, extra ) + local handler, socket, err = wrapconnection( nil, listeners, socket, ip, serverport, "clientport", pattern, sslctx, extra) if not handler then return nil, err end _socketlist[ socket ] = handler if not sslctx then @@ -997,7 +1006,7 @@ local wrapclient = function( socket, ip, serverport, listeners, pattern, sslctx return handler, socket end -local addclient = function( address, port, listeners, pattern, sslctx, typ ) +local addclient = function( address, port, listeners, pattern, sslctx, typ, extra ) local err if type( listeners ) ~= "table" then err = "invalid listener table" @@ -1034,7 +1043,7 @@ local addclient = function( address, port, listeners, pattern, sslctx, typ ) client:settimeout( 0 ) local ok, err = client:setpeername( address, port ) if ok or err == "timeout" or err == "Operation already in progress" then - return wrapclient( client, address, port, listeners, pattern, sslctx ) + return wrapclient( client, address, port, listeners, pattern, sslctx, extra ) else return nil, err end -- cgit v1.2.3 From fd6971514d3f59750a3487d3a407f8c589156ff9 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 15 Sep 2020 09:08:21 +0100 Subject: net.http: Add feature discovery (currently just contains SNI) --- net/http.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/http.lua b/net/http.lua index 0768cdab..7335d210 100644 --- a/net/http.lua +++ b/net/http.lua @@ -314,4 +314,7 @@ return { formencode = util_http.formencode; formdecode = util_http.formdecode; destroy_request = destroy_request; + features = { + sni = true; + }; }; -- cgit v1.2.3 From 7fc254e94cd6d8a5af21537bc90b5cb1a507326d 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 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 f46f4b97ebf16c4816d5e8847baeb062c0212bd9 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(-) 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 e83907f4990b671d8953f64f1b2663d0df0226d4 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(+) diff --git a/util/startup.lua b/util/startup.lua index 2a7e8a36..c1f4ec8b 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -12,6 +12,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 }; @@ -544,6 +546,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", @@ -573,6 +588,7 @@ function startup.prosodyctl() startup.read_config(); startup.force_console_logging(); startup.init_logging(); + startup.init_gc(); startup.setup_plugindir(); startup.setup_datadir(); startup.chdir(); @@ -593,6 +609,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 9d1dd4c4ee7e2a70b2a73e7b5a13298e662df585 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(-) 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 0e146820c08a7b61535f43ba00e101ed9f4ba70a 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(+) diff --git a/util/startup.lua b/util/startup.lua index 2a7e8a36..c1f4ec8b 100644 --- a/util/startup.lua +++ b/util/startup.lua @@ -12,6 +12,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 }; @@ -544,6 +546,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", @@ -573,6 +588,7 @@ function startup.prosodyctl() startup.read_config(); startup.force_console_logging(); startup.init_logging(); + startup.init_gc(); startup.setup_plugindir(); startup.setup_datadir(); startup.chdir(); @@ -593,6 +609,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 6774f93b5da1642c1baef1387880da484428981e 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/queue.lua | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/util/queue.lua b/util/queue.lua index 728e905f..c8e71514 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) --luacheck: ignore 431/t return function (t, pos) -- cgit v1.2.3 From 0156ad50d3f7b8fcfc12f0ebf9d2c2fee4df36ee 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 | 176 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 util/dbuffer.lua diff --git a/util/dbuffer.lua b/util/dbuffer.lua new file mode 100644 index 00000000..a50f3a64 --- /dev/null +++ b/util/dbuffer.lua @@ -0,0 +1,176 @@ +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 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; + 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 and requested_bytes > self._length then + return nil; + end + + local chunk, read_bytes = self:read_chunk(requested_bytes); + 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; + 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(self.front_consumed+1):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 not front_chunk or #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; +}; -- cgit v1.2.3 From ee0fe27609bccc9752cdbff100dace64e2c4d55b Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 24 Aug 2020 17:28:48 +0200 Subject: util.dbuffer: Simplify test case An earlier theory involved the bug being related to collapsing multiple items, so it exercised that too. Also correct the comment, it referred to the space in "hello world" in an earlier version before the test string was changed to "foobar", which was what was tested in a REPL --- spec/util_dbuffer_spec.lua | 130 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 spec/util_dbuffer_spec.lua diff --git a/spec/util_dbuffer_spec.lua b/spec/util_dbuffer_spec.lua new file mode 100644 index 00000000..af404042 --- /dev/null +++ b/spec/util_dbuffer_spec.lua @@ -0,0 +1,130 @@ +local dbuffer = require "util.dbuffer"; +describe("util.dbuffer", function () + describe("#new", function () + it("has a constructor", function () + assert.Function(dbuffer.new); + end); + it("can be created", function () + assert.truthy(dbuffer.new()); + end); + it("won't create an empty buffer", function () + assert.falsy(dbuffer.new(0)); + end); + it("won't create a negatively sized buffer", function () + assert.falsy(dbuffer.new(-1)); + end); + end); + describe(":write", function () + local b = dbuffer.new(); + it("works", function () + assert.truthy(b:write("hi")); + end); + end); + + describe(":read", function () + it("supports optional bytes parameter", function () + -- should return the frontmost chunk + local b = dbuffer.new(); + assert.truthy(b:write("hello")); + assert.truthy(b:write(" ")); + assert.truthy(b:write("world")); + assert.equal("h", b:read(1)); + + assert.equal("ello", b:read()); + assert.equal(" ", b:read()); + assert.equal("world", b:read()); + end); + end); + + describe(":discard", function () + local b = dbuffer.new(); + it("works", function () + assert.truthy(b:write("hello world")); + assert.truthy(b:discard(6)); + assert.equal(5, b:length()); + assert.equal("world", b:read(5)); + end); + end); + + describe(":collapse()", function () + it("works on an empty buffer", function () + local b = dbuffer.new(); + b:collapse(); + end); + end); + + describe(":sub", function () + -- Helper function to compare buffer:sub() with string:sub() + local s = "hello world"; + local function test_sub(b, x, y) + local string_result, buffer_result = s:sub(x, y), b:sub(x, y); + assert.equals(string_result, buffer_result, ("buffer:sub(%d, %s) does not match string:sub()"):format(x, y and ("%d"):format(y) or "nil")); + end + + it("works", function () + local b = dbuffer.new(); + assert.truthy(b:write("hello world")); + assert.equals("hello", b:sub(1, 5)); + end); + + it("works after discard", function () + local b = dbuffer.new(256); + assert.truthy(b:write("foobar")); + assert.equals("foobar", b:sub(1, 6)); + assert.truthy(b:discard(3)); -- consume "foo" + assert.equals("bar", b:sub(1, 3)); + end); + + it("supports optional end parameter", function () + local b = dbuffer.new(); + assert.truthy(b:write("hello world")); + assert.equals("hello world", b:sub(1)); + assert.equals("world", b:sub(-5)); + end); + + it("is equivalent to string:sub", function () + local b = dbuffer.new(11); + assert.truthy(b:write(s)); + for i = -13, 13 do + for j = -13, 13 do + test_sub(b, i, j); + end + end + end); + end); + + describe(":byte", function () + -- Helper function to compare buffer:byte() with string:byte() + local s = "hello world" + local function test_byte(b, x, y) + local string_result, buffer_result = {s:byte(x, y)}, {b:byte(x, y)}; + assert.same(string_result, buffer_result, ("buffer:byte(%d, %s) does not match string:byte()"):format(x, y and ("%d"):format(y) or "nil")); + end + + it("is equivalent to string:byte", function () + local b = dbuffer.new(11); + assert.truthy(b:write(s)); + test_byte(b, 1); + test_byte(b, 3); + test_byte(b, -1); + test_byte(b, -3); + for i = -13, 13 do + for j = -13, 13 do + test_byte(b, i, j); + end + end + end); + + it("works with characters > 127", function () + local b = dbuffer.new(); + b:write(string.char(0, 140)); + local r = { b:byte(1, 2) }; + assert.same({ 0, 140 }, r); + end); + + it("works on an empty buffer", function () + local b = dbuffer.new(); + assert.equal("", b:sub(1,1)); + end); + end); +end); -- cgit v1.2.3 From f9550cb3416b5c579b7efabe85495951464868c8 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 17 Sep 2020 13:00:19 +0100 Subject: net.websocket.frames: Allow all methods to work on non-string objects Instead of using the string library, use methods from the passed object, which are assumed to be equivalent. This provides compatibility with objects from util.ringbuffer and util.dbuffer, for example. --- net/websocket/frames.lua | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/net/websocket/frames.lua b/net/websocket/frames.lua index ba25d261..e1b5527a 100644 --- a/net/websocket/frames.lua +++ b/net/websocket/frames.lua @@ -16,13 +16,12 @@ local bor = bit.bor; local bxor = bit.bxor; local lshift = bit.lshift; local rshift = bit.rshift; +local unpack = table.unpack or unpack; -- luacheck: ignore 113 local t_concat = table.concat; -local s_byte = string.byte; local s_char= string.char; -local s_sub = string.sub; -local s_pack = string.pack; -- luacheck: ignore 143 -local s_unpack = string.unpack; -- luacheck: ignore 143 +local s_pack = string.pack; +local s_unpack = string.unpack; if not s_pack and softreq"struct" then s_pack = softreq"struct".pack; @@ -30,12 +29,12 @@ if not s_pack and softreq"struct" then end local function read_uint16be(str, pos) - local l1, l2 = s_byte(str, pos, pos+1); + local l1, l2 = str:byte(pos, pos+1); return l1*256 + l2; end -- FIXME: this may lose precision local function read_uint64be(str, pos) - local l1, l2, l3, l4, l5, l6, l7, l8 = s_byte(str, pos, pos+7); + local l1, l2, l3, l4, l5, l6, l7, l8 = str:byte(pos, pos+7); local h = lshift(l1, 24) + lshift(l2, 16) + lshift(l3, 8) + l4; local l = lshift(l5, 24) + lshift(l6, 16) + lshift(l7, 8) + l8; return h * 2^32 + l; @@ -63,9 +62,15 @@ end if s_unpack then function read_uint16be(str, pos) + if type(str) ~= "string" then + str, pos = str:sub(pos, pos+1), 1; + end return s_unpack(">I2", str, pos); end function read_uint64be(str, pos) + if type(str) ~= "string" then + str, pos = str:sub(pos, pos+7), 1; + end return s_unpack(">I8", str, pos); end end @@ -73,7 +78,7 @@ end local function parse_frame_header(frame) if #frame < 2 then return; end - local byte1, byte2 = s_byte(frame, 1, 2); + local byte1, byte2 = frame:byte(1, 2); local result = { FIN = band(byte1, 0x80) > 0; RSV1 = band(byte1, 0x40) > 0; @@ -102,7 +107,7 @@ local function parse_frame_header(frame) end if result.MASK then - result.key = { s_byte(frame, length_bytes+3, length_bytes+6) }; + result.key = { frame:byte(length_bytes+3, length_bytes+6) }; end return result, header_length; @@ -121,7 +126,7 @@ local function apply_mask(str, key, from, to) for i = from, to do local key_index = counter%key_len + 1; counter = counter + 1; - data[counter] = s_char(bxor(key[key_index], s_byte(str, i))); + data[counter] = s_char(bxor(key[key_index], str:byte(i))); end return t_concat(data); end @@ -189,7 +194,7 @@ local function parse_close(data) if #data >= 2 then code = read_uint16be(data, 1); if #data > 2 then - message = s_sub(data, 3); + message = data:sub(3); end end return code, message -- cgit v1.2.3 From d8be48981f7e64f984c05f88ecf381da831f7232 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 17 Sep 2020 13:04:46 +0100 Subject: mod_websocket: Switch partial frame buffering to util.dbuffer This improves performance and enforces stanza size limits earlier in the pipeline. --- net/websocket/frames.lua | 4 ++-- plugins/mod_websocket.lua | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/net/websocket/frames.lua b/net/websocket/frames.lua index e1b5527a..65f9122c 100644 --- a/net/websocket/frames.lua +++ b/net/websocket/frames.lua @@ -20,8 +20,8 @@ local unpack = table.unpack or unpack; -- luacheck: ignore 113 local t_concat = table.concat; local s_char= string.char; -local s_pack = string.pack; -local s_unpack = string.unpack; +local s_pack = string.pack; -- luacheck: ignore 143 +local s_unpack = string.unpack; -- luacheck: ignore 143 if not s_pack and softreq"struct" then s_pack = softreq"struct".pack; diff --git a/plugins/mod_websocket.lua b/plugins/mod_websocket.lua index 177259e6..ad94486b 100644 --- a/plugins/mod_websocket.lua +++ b/plugins/mod_websocket.lua @@ -18,6 +18,7 @@ local contains_token = require "util.http".contains_token; local portmanager = require "core.portmanager"; local sm_destroy_session = require"core.sessionmanager".destroy_session; local log = module._log; +local dbuffer = require "util.dbuffer"; local websocket_frames = require"net.websocket.frames"; local parse_frame = websocket_frames.parse; @@ -27,6 +28,8 @@ local parse_close = websocket_frames.parse_close; local t_concat = table.concat; +local stanza_size_limit = module:get_option_number("c2s_stanza_size_limit", 10 * 1024 * 1024); +local frame_fragment_limit = module:get_option_number("websocket_frame_fragment_limit", 8); local stream_close_timeout = module:get_option_number("c2s_close_timeout", 5); local consider_websocket_secure = module:get_option_boolean("consider_websocket_secure"); local cross_domain = module:get_option_set("cross_domain_websocket", {}); @@ -269,14 +272,16 @@ function handle_request(event) session.open_stream = session_open_stream; session.close = session_close; - local frameBuffer = ""; + -- max frame header is 22 bytes + local frameBuffer = dbuffer.new(stanza_size_limit + 22, frame_fragment_limit); add_filter(session, "bytes/in", function(data) + frameBuffer:write(data); + local cache = {}; - frameBuffer = frameBuffer .. data; local frame, length = parse_frame(frameBuffer); while frame do - frameBuffer = frameBuffer:sub(length + 1); + frameBuffer:discard(length); local result = handle_frame(frame); if not result then return; end cache[#cache+1] = filter_open_close(result); -- cgit v1.2.3 From 7f839ba44930e5384a2c170e136ef837c7b54cd2 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 17 Sep 2020 16:41:48 +0100 Subject: mod_websocket: handle full frame buffer and raise stream error --- plugins/mod_websocket.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/mod_websocket.lua b/plugins/mod_websocket.lua index ad94486b..d3b035db 100644 --- a/plugins/mod_websocket.lua +++ b/plugins/mod_websocket.lua @@ -275,7 +275,11 @@ function handle_request(event) -- max frame header is 22 bytes local frameBuffer = dbuffer.new(stanza_size_limit + 22, frame_fragment_limit); add_filter(session, "bytes/in", function(data) - frameBuffer:write(data); + if not frameBuffer:write(data) then + session.log("warn", "websocket frame buffer full - terminating session"); + session:close({ condition = "resource-constraint", text = "frame buffer exceeded" }); + return; + end local cache = {}; local frame, length = parse_frame(frameBuffer); -- cgit v1.2.3 From 2eee29ff1eec57e9204f2487b0349abb80e9c9f0 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 17 Sep 2020 16:42:14 +0100 Subject: mod_websocket: Add separate limit for frame buffer size --- plugins/mod_websocket.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/mod_websocket.lua b/plugins/mod_websocket.lua index d3b035db..53a1d452 100644 --- a/plugins/mod_websocket.lua +++ b/plugins/mod_websocket.lua @@ -29,6 +29,7 @@ local parse_close = websocket_frames.parse_close; local t_concat = table.concat; local stanza_size_limit = module:get_option_number("c2s_stanza_size_limit", 10 * 1024 * 1024); +local frame_buffer_limit = module:get_option_number("websocket_frame_buffer_limit", 2 * stanza_size_limit); local frame_fragment_limit = module:get_option_number("websocket_frame_fragment_limit", 8); local stream_close_timeout = module:get_option_number("c2s_close_timeout", 5); local consider_websocket_secure = module:get_option_boolean("consider_websocket_secure"); @@ -272,8 +273,7 @@ function handle_request(event) session.open_stream = session_open_stream; session.close = session_close; - -- max frame header is 22 bytes - local frameBuffer = dbuffer.new(stanza_size_limit + 22, frame_fragment_limit); + local frameBuffer = dbuffer.new(frame_buffer_limit, frame_fragment_limit); add_filter(session, "bytes/in", function(data) if not frameBuffer:write(data) then session.log("warn", "websocket frame buffer full - terminating session"); -- cgit v1.2.3 From 68898a08805177d6e2ad52c88580893eed506d66 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 17 Sep 2020 16:42:36 +0100 Subject: mod_websocket: Enforce stanza size limit and close stream --- plugins/mod_websocket.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/mod_websocket.lua b/plugins/mod_websocket.lua index 53a1d452..0bd001f4 100644 --- a/plugins/mod_websocket.lua +++ b/plugins/mod_websocket.lua @@ -285,6 +285,10 @@ function handle_request(event) local frame, length = parse_frame(frameBuffer); while frame do + if length > stanza_size_limit then + session:close({ condition = "policy-violation", text = "stanza too large" }); + return; + end frameBuffer:discard(length); local result = handle_frame(frame); if not result then return; end -- cgit v1.2.3 From fb2a77467f01351f3b88d45b436fd7420eba90dc Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 28 Sep 2020 16:36:12 +0100 Subject: mod_websocket: Continue to process data already in the buffer after an error occurs Previously any error, or even a normal websocket close frame, would return early, leaving potentially entire frames in the buffer unprocessed and then discarded. This change stops processing new data, but returns an existing processed data up to the point of the error/close. --- plugins/mod_websocket.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_websocket.lua b/plugins/mod_websocket.lua index 0bd001f4..ecc12a45 100644 --- a/plugins/mod_websocket.lua +++ b/plugins/mod_websocket.lua @@ -291,7 +291,7 @@ function handle_request(event) end frameBuffer:discard(length); local result = handle_frame(frame); - if not result then return; end + if not result then break; end cache[#cache+1] = filter_open_close(result); frame, length = parse_frame(frameBuffer); end -- cgit v1.2.3 From 3cd5ae91199caf59972c2d6d953fa3619da8a552 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 29 Sep 2020 13:58:32 +0100 Subject: net.websocket.frames: Additionally return partial frame if there is one --- net/websocket/frames.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/websocket/frames.lua b/net/websocket/frames.lua index 65f9122c..a0c0d4cd 100644 --- a/net/websocket/frames.lua +++ b/net/websocket/frames.lua @@ -141,7 +141,7 @@ end local function parse_frame(frame) local result, pos = parse_frame_header(frame); - if result == nil or #frame < (pos + result.length) then return; end + if result == nil or #frame < (pos + result.length) then return nil, nil, result; end result.data = parse_frame_body(frame, result, pos+1); return result, pos + result.length; end -- cgit v1.2.3 From 2546c272bc9f683885d2b4943e70e21696a4c762 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 29 Sep 2020 15:18:32 +0100 Subject: mod_websocket: Refactor frame validity checking, also check partially-received frames against constraints --- plugins/mod_websocket.lua | 172 +++++++++++++++++++++++++--------------------- 1 file changed, 95 insertions(+), 77 deletions(-) diff --git a/plugins/mod_websocket.lua b/plugins/mod_websocket.lua index ecc12a45..a613672b 100644 --- a/plugins/mod_websocket.lua +++ b/plugins/mod_websocket.lua @@ -142,6 +142,65 @@ local function filter_open_close(data) return data; end + +local function validate_frame(frame, max_length) + local opcode, length = frame.opcode, frame.length; + + if max_length and length > max_length then + return false, 1009, "Payload too large"; + end + + -- Error cases + if frame.RSV1 or frame.RSV2 or frame.RSV3 then -- Reserved bits non zero + return false, 1002, "Reserved bits not zero"; + end + + if opcode == 0x8 and frame.data then -- close frame + if length == 1 then + return false, 1002, "Close frame with payload, but too short for status code"; + elseif length >= 2 then + local status_code = parse_close(frame.data) + if status_code < 1000 then + return false, 1002, "Closed with invalid status code"; + elseif ((status_code > 1003 and status_code < 1007) or status_code > 1011) and status_code < 3000 then + return false, 1002, "Closed with reserved status code"; + end + end + end + + if opcode >= 0x8 then + if length > 125 then -- Control frame with too much payload + return false, 1002, "Payload too large"; + end + + if not frame.FIN then -- Fragmented control frame + return false, 1002, "Fragmented control frame"; + end + end + + if (opcode > 0x2 and opcode < 0x8) or (opcode > 0xA) then + return false, 1002, "Reserved opcode"; + end + + -- Check opcode + if opcode == 0x2 then -- Binary frame + return false, 1003, "Only text frames are supported, RFC 7395 3.2"; + elseif opcode == 0x8 then -- Close request + return false, 1000, "Goodbye"; + end + + -- Other (XMPP-specific) validity checks + if not frame.FIN then + return false, 1003, "Continuation frames are not supported, RFC 7395 3.3.3"; + end + if opcode == 0x01 and frame.data and frame.data:byte(1, 1) ~= 60 then + return false, 1007, "Invalid payload start character, RFC 7395 3.3.3"; + end + + return true; +end + + function handle_request(event) local request, response = event.request, event.response; local conn = response.conn; @@ -172,90 +231,40 @@ function handle_request(event) conn:close(); end - local dataBuffer; - local function handle_frame(frame) - local opcode = frame.opcode; - local length = frame.length; - module:log("debug", "Websocket received frame: opcode=%0x, %i bytes", frame.opcode, #frame.data); - - -- Error cases - if frame.RSV1 or frame.RSV2 or frame.RSV3 then -- Reserved bits non zero - websocket_close(1002, "Reserved bits not zero"); - return false; - end - - if opcode == 0x8 then -- close frame - if length == 1 then - websocket_close(1002, "Close frame with payload, but too short for status code"); - return false; - elseif length >= 2 then - local status_code = parse_close(frame.data) - if status_code < 1000 then - websocket_close(1002, "Closed with invalid status code"); - return false; - elseif ((status_code > 1003 and status_code < 1007) or status_code > 1011) and status_code < 3000 then - websocket_close(1002, "Closed with reserved status code"); - return false; - end - end - end - - if opcode >= 0x8 then - if length > 125 then -- Control frame with too much payload - websocket_close(1002, "Payload too large"); - return false; - end - - if not frame.FIN then -- Fragmented control frame - websocket_close(1002, "Fragmented control frame"); - return false; - end - end - - if (opcode > 0x2 and opcode < 0x8) or (opcode > 0xA) then - websocket_close(1002, "Reserved opcode"); - return false; + local function websocket_handle_error(session, code, message) + if code == 1009 then -- stanza size limit exceeded + -- we close the session, rather than the connection, + -- otherwise a resuming client will simply resend the + -- offending stanza + session:close({ condition = "policy-violation", text = "stanza too large" }); + else + websocket_close(code, message); end + end - if opcode == 0x0 and not dataBuffer then - websocket_close(1002, "Unexpected continuation frame"); - return false; - end + local function handle_frame(frame) + module:log("debug", "Websocket received frame: opcode=%0x, %i bytes", frame.opcode, #frame.data); - if (opcode == 0x1 or opcode == 0x2) and dataBuffer then - websocket_close(1002, "Continuation frame expected"); - return false; + -- Check frame makes sense + local frame_ok, err_status, err_text = validate_frame(frame, stanza_size_limit); + if not frame_ok then + return frame_ok, err_status, err_text; end - -- Valid cases - if opcode == 0x0 then -- Continuation frame - dataBuffer[#dataBuffer+1] = frame.data; - elseif opcode == 0x1 then -- Text frame - dataBuffer = {frame.data}; - elseif opcode == 0x2 then -- Binary frame - websocket_close(1003, "Only text frames are supported"); - return; - elseif opcode == 0x8 then -- Close request - websocket_close(1000, "Goodbye"); - return; - elseif opcode == 0x9 then -- Ping frame + local opcode = frame.opcode; + if opcode == 0x9 then -- Ping frame frame.opcode = 0xA; frame.MASK = false; -- Clients send masked frames, servers don't, see #1484 conn:write(build_frame(frame)); return ""; elseif opcode == 0xA then -- Pong frame, MAY be sent unsolicited, eg as keepalive return ""; - else + elseif opcode ~= 0x1 then -- Not text frame (which is all we support) log("warn", "Received frame with unsupported opcode %i", opcode); return ""; end - if frame.FIN then - local data = t_concat(dataBuffer, ""); - dataBuffer = nil; - return data; - end - return ""; + return frame.data; end conn:setlistener(c2s_listener); @@ -282,19 +291,28 @@ function handle_request(event) end local cache = {}; - local frame, length = parse_frame(frameBuffer); + local frame, length, partial = parse_frame(frameBuffer); while frame do - if length > stanza_size_limit then - session:close({ condition = "policy-violation", text = "stanza too large" }); - return; - end frameBuffer:discard(length); - local result = handle_frame(frame); - if not result then break; end + local result, err_status, err_text = handle_frame(frame); + if not result then + websocket_handle_error(session, err_status, err_text); + break; + end cache[#cache+1] = filter_open_close(result); - frame, length = parse_frame(frameBuffer); + frame, length, partial = parse_frame(frameBuffer); end + + if partial then + -- The header of the next frame is already in the buffer, run + -- some early validation here + local frame_ok, err_status, err_text = validate_frame(partial, stanza_size_limit); + if not frame_ok then + websocket_handle_error(session, err_status, err_text); + end + end + return t_concat(cache, ""); end); -- cgit v1.2.3 From d215f5acca46c2c68ddcd3cedd834e4d0c39e670 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Tue, 29 Sep 2020 21:27:16 -0500 Subject: util.indexedbheap: Fix heap datastructure corruption in :reschedule(smaller_value) --- spec/util_indexedbheap_spec.lua | 33 +++++++++++++++++++++++++++++++++ util/indexedbheap.lua | 2 +- 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 spec/util_indexedbheap_spec.lua diff --git a/spec/util_indexedbheap_spec.lua b/spec/util_indexedbheap_spec.lua new file mode 100644 index 00000000..b10ee385 --- /dev/null +++ b/spec/util_indexedbheap_spec.lua @@ -0,0 +1,33 @@ +local ibh = require"util.indexedbheap"; + +local function verify_heap_property(priorities) + for k in ipairs(priorities) do + local parent = priorities[k]; + local childA = priorities[2*k]; + local childB = priorities[2*k+1]; + -- print("-", parent, childA, childB) + assert(childA == nil or childA > parent, "heap property violated"); + assert(childB == nil or childB > parent, "heap property violated"); + end +end + +local h +setup(function () + h = ibh.create(); +end) +describe("util.indexedbheap", function () + it("item can be moved from end to top", function () + verify_heap_property(h); + h:insert("a", 1); + verify_heap_property(h); + h:insert("b", 2); + verify_heap_property(h); + h:insert("c", 3); + verify_heap_property(h); + local id = h:insert("*", 10); + verify_heap_property(h); + h:reprioritize(id, 0); + verify_heap_property(h); + assert.same({ 0, "*", id }, { h:pop() }); + end) +end); diff --git a/util/indexedbheap.lua b/util/indexedbheap.lua index 7f193d54..45830673 100644 --- a/util/indexedbheap.lua +++ b/util/indexedbheap.lua @@ -23,7 +23,7 @@ local function _percolate_up(self, k, sync, index) local tmp_sync = sync[k]; while k ~= 1 do local parent = math_floor(k/2); - if tmp < self[parent] then break; end + if tmp >= self[parent] then break; end self[k] = self[parent]; sync[k] = sync[parent]; index[sync[k]] = k; -- cgit v1.2.3 From fd6e45c240cc2aadf9d0053489e3125e1dad580a Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 31 May 2020 22:39:34 +0200 Subject: mod_c2s,mod_s2s: Make stanza size limits configurable --- plugins/mod_c2s.lua | 3 ++- plugins/mod_s2s/mod_s2s.lua | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/mod_c2s.lua b/plugins/mod_c2s.lua index 15d3a9be..c648850f 100644 --- a/plugins/mod_c2s.lua +++ b/plugins/mod_c2s.lua @@ -26,6 +26,7 @@ local log = module._log; local c2s_timeout = module:get_option_number("c2s_timeout", 300); local stream_close_timeout = module:get_option_number("c2s_close_timeout", 5); local opt_keepalives = module:get_option_boolean("c2s_tcp_keepalives", module:get_option_boolean("tcp_keepalives", true)); +local stanza_size_limit = module:get_option_number("c2s_stanza_size_limit"); -- TODO come up with a sensible default (util.xmppstream defaults to 10M) local measure_connections = module:measure("connections", "amount"); local measure_ipv6 = module:measure("ipv6", "amount"); @@ -262,7 +263,7 @@ function listener.onconnect(conn) session.close = session_close; - local stream = new_xmpp_stream(session, stream_callbacks); + local stream = new_xmpp_stream(session, stream_callbacks, stanza_size_limit); session.stream = stream; session.notopen = true; diff --git a/plugins/mod_s2s/mod_s2s.lua b/plugins/mod_s2s/mod_s2s.lua index 3f56b953..5439aa96 100644 --- a/plugins/mod_s2s/mod_s2s.lua +++ b/plugins/mod_s2s/mod_s2s.lua @@ -37,6 +37,7 @@ local secure_auth = module:get_option_boolean("s2s_secure_auth", false); -- One local secure_domains, insecure_domains = module:get_option_set("s2s_secure_domains", {})._items, module:get_option_set("s2s_insecure_domains", {})._items; local require_encryption = module:get_option_boolean("s2s_require_encryption", false); +local stanza_size_limit = module:get_option_number("s2s_stanza_size_limit"); -- TODO come up with a sensible default (util.xmppstream defaults to 10M) local measure_connections = module:measure("connections", "amount"); local measure_ipv6 = module:measure("ipv6", "amount"); @@ -550,7 +551,7 @@ end -- Session initialization logic shared by incoming and outgoing local function initialize_session(session) - local stream = new_xmpp_stream(session, stream_callbacks); + local stream = new_xmpp_stream(session, stream_callbacks, stanza_size_limit); session.thread = runner(function (stanza) if stanza.name == nil then -- cgit v1.2.3 -- cgit v1.2.3 From df1332f46b6816919f54279e2acc371c990979d5 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 3 Oct 2020 14:59:11 +0200 Subject: mod_bosh: Pick out the 'wait' before checking it instead of earlier Going to add more host related checks, so to keep the wait variable closer to the related checks --- plugins/mod_bosh.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/mod_bosh.lua b/plugins/mod_bosh.lua index d4701148..7f750613 100644 --- a/plugins/mod_bosh.lua +++ b/plugins/mod_bosh.lua @@ -269,7 +269,6 @@ function stream_callbacks.streamopened(context, attr) context.notopen = nil; -- Signals that we accept this opening tag local to_host = nameprep(attr.to); - local wait = tonumber(attr.wait); if not to_host then log("debug", "BOSH client tried to connect to invalid host: %s", tostring(attr.to)); local close_reply = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate", @@ -277,6 +276,8 @@ function stream_callbacks.streamopened(context, attr) response:send(tostring(close_reply)); return; end + + local wait = tonumber(attr.wait); if not rid or (not attr.wait or not wait or wait < 0 or wait % 1 ~= 0) then log("debug", "BOSH client sent invalid rid or wait attributes: rid=%s, wait=%s", tostring(attr.rid), tostring(attr.wait)); local close_reply = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate", -- cgit v1.2.3 From ac02007aa2f7a48c376e6be3d9424643cb98e369 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 3 Oct 2020 15:03:09 +0200 Subject: mod_bosh: Ensure that stream is directed to a VirtualHost (fixes #425) --- plugins/mod_bosh.lua | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/plugins/mod_bosh.lua b/plugins/mod_bosh.lua index 7f750613..52168670 100644 --- a/plugins/mod_bosh.lua +++ b/plugins/mod_bosh.lua @@ -277,6 +277,22 @@ function stream_callbacks.streamopened(context, attr) return; end + if not prosody.hosts[to_host] then + log("debug", "BOSH client tried to connect to non-existant host: %s", attr.to); + local close_reply = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate", + ["xmlns:stream"] = xmlns_streams, condition = "improper-addressing" }); + response:send(tostring(close_reply)); + return; + end + + if prosody.hosts[to_host].type ~= "local" then + log("debug", "BOSH client tried to connect to %s host: %s", prosody.hosts[to_host].type, attr.to); + local close_reply = st.stanza("body", { xmlns = xmlns_bosh, type = "terminate", + ["xmlns:stream"] = xmlns_streams, condition = "improper-addressing" }); + response:send(tostring(close_reply)); + return; + end + local wait = tonumber(attr.wait); if not rid or (not attr.wait or not wait or wait < 0 or wait % 1 ~= 0) then log("debug", "BOSH client sent invalid rid or wait attributes: rid=%s, wait=%s", tostring(attr.rid), tostring(attr.wait)); -- cgit v1.2.3 From 53e43a8effacfe958fb5a3c4fc14833e20c2d6ce Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 4 Oct 2020 18:00:32 +0200 Subject: MUC: Correct advertising of subject write access (really fixes #1155) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thanks pep. and lovetox XEP-0045 §6.4: > any field defined for the muc\#roomconfig FORM_TYPE can be included in > the extended service discovery fields Probably happened because the same mistake is in #1155 --- plugins/muc/subject.lib.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/muc/subject.lib.lua b/plugins/muc/subject.lib.lua index 938abf61..14256bbc 100644 --- a/plugins/muc/subject.lib.lua +++ b/plugins/muc/subject.lib.lua @@ -31,10 +31,10 @@ end module:hook("muc-disco#info", function (event) table.insert(event.form, { - name = "muc#roominfo_changesubject"; + name = "muc#roomconfig_changesubject"; type = "boolean"; }); - event.formdata["muc#roominfo_changesubject"] = get_changesubject(event.room); + event.formdata["muc#roomconfig_changesubject"] = get_changesubject(event.room); end); module:hook("muc-config-form", function(event) -- cgit v1.2.3 From 45346e93dc72f438dd7623707f416217c34b9014 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 5 Oct 2020 22:12:29 +0200 Subject: util.dependencies: Check for bitop library same way as net.websocket.frames (fixes #1594) --- util/dependencies.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/dependencies.lua b/util/dependencies.lua index 7c7b938e..24975567 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"bit" or softreq"bit32"; if not bit then missingdep("lua-bitops", { -- cgit v1.2.3 From 531f26f144e35c9b3f3c4cf5fb4b278534520d00 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 7 Oct 2020 13:33:02 +0200 Subject: MUC: Preserve disco 'node' attribute (or lack thereof) in response (fix #1595) (thanks lessthan3) --- plugins/muc/muc.lib.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 93334b88..3139c131 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -336,12 +336,12 @@ function room_mt:send_occupant_list(to, filter) end function room_mt:get_disco_info(stanza) - local node = stanza.tags[1].attr.node or ""; + local node = stanza.tags[1].attr.node; local reply = st.reply(stanza):tag("query", { xmlns = "http://jabber.org/protocol/disco#info", node = node }); local event_name = "muc-disco#info"; local event_data = { room = self, reply = reply, stanza = stanza }; - if node ~= "" then + if node and node ~= "" then event_name = event_name.."/"..node; else event_data.form = dataform.new { -- cgit v1.2.3 From 282471bcaf402ce69a46f280e8eeb15fbada360f Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 12 Oct 2020 20:20:02 +0200 Subject: util.dbuffer: Expose length as :len() method, like strings Ref #1598 --- spec/util_dbuffer_spec.lua | 1 + util/dbuffer.lua | 1 + 2 files changed, 2 insertions(+) diff --git a/spec/util_dbuffer_spec.lua b/spec/util_dbuffer_spec.lua index af404042..2b1af835 100644 --- a/spec/util_dbuffer_spec.lua +++ b/spec/util_dbuffer_spec.lua @@ -42,6 +42,7 @@ describe("util.dbuffer", function () assert.truthy(b:write("hello world")); assert.truthy(b:discard(6)); assert.equal(5, b:length()); + assert.equal(5, b:len()); assert.equal("world", b:read(5)); end); end); diff --git a/util/dbuffer.lua b/util/dbuffer.lua index a50f3a64..9d58b306 100644 --- a/util/dbuffer.lua +++ b/util/dbuffer.lua @@ -135,6 +135,7 @@ end function dbuffer_methods:length() return self._length; end +dbuffer_methods.len = dbuffer_methods.length; -- strings have :len() dynamic_buffer_mt.__len = dbuffer_methods.length; -- support # operator function dbuffer_methods:collapse(bytes) -- cgit v1.2.3 From a63b4e5cc449601e94a9a48756ea0200c6410d02 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 12 Oct 2020 20:21:18 +0200 Subject: net.websocket.frames: Read buffer length correctly in Lua 5.1 (fix #1598) COMPAT: The __len metamethod does not work with tables in Lua 5.1. Both strings and util.dbuffer now expose their length as a :len() method. --- net/websocket/frames.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/websocket/frames.lua b/net/websocket/frames.lua index a0c0d4cd..9cb5f4fe 100644 --- a/net/websocket/frames.lua +++ b/net/websocket/frames.lua @@ -76,7 +76,7 @@ if s_unpack then end local function parse_frame_header(frame) - if #frame < 2 then return; end + if frame:len() < 2 then return; end local byte1, byte2 = frame:byte(1, 2); local result = { @@ -98,7 +98,7 @@ local function parse_frame_header(frame) end local header_length = 2 + length_bytes + (result.MASK and 4 or 0); - if #frame < header_length then return; end + if frame:len() < header_length then return; end if length_bytes == 2 then result.length = read_uint16be(frame, 3); @@ -141,7 +141,7 @@ end local function parse_frame(frame) local result, pos = parse_frame_header(frame); - if result == nil or #frame < (pos + result.length) then return nil, nil, result; end + if result == nil or frame:len() < (pos + result.length) then return nil, nil, result; end result.data = parse_frame_body(frame, result, pos+1); return result, pos + result.length; end -- cgit v1.2.3 From d5e2d562f70de7ecb6e6e827c24a8a16899058b4 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 13 Oct 2020 11:55:28 +0100 Subject: net.http.server: Don't send Content-Length on 1xx/204 responses, per RFC (fixes #1596) --- net/http/server.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/http/server.lua b/net/http/server.lua index 18704962..3873bbe0 100644 --- a/net/http/server.lua +++ b/net/http/server.lua @@ -295,7 +295,10 @@ _M.prepare_header = prepare_header; function _M.send_response(response, body) if response.finished then return; end body = body or response.body or ""; - response.headers.content_length = #body; + -- Per RFC 7230, informational (1xx) and 204 (no content) should have no c-l header + if response.status_code > 199 and response.status_code ~= 204 then + response.headers.content_length = #body; + end local output = prepare_header(response); t_insert(output, body); response.conn:write(t_concat(output)); -- cgit v1.2.3 From 78f70bd67bc998c59a67aae26a97ef0c625c277d Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 14 Oct 2020 19:02:48 +0200 Subject: net.websocket.frames: Add small test covering xor-masking This is basically a recording of current behavior, to detect changes. --- spec/net_websocket_frames_spec.lua | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/spec/net_websocket_frames_spec.lua b/spec/net_websocket_frames_spec.lua index d4df3a54..9499cee7 100644 --- a/spec/net_websocket_frames_spec.lua +++ b/spec/net_websocket_frames_spec.lua @@ -32,6 +32,17 @@ describe("net.websocket.frames", function () ["RSV2"] = false; ["RSV3"] = false; }; + with_mask = { + ["opcode"] = 0; + ["length"] = 5; + ["data"] = "hello"; + ["key"] = { 32, 0, 32, 0, }; + ["FIN"] = true; + ["MASK"] = true; + ["RSV1"] = false; + ["RSV2"] = false; + ["RSV3"] = false; + }; } describe("build", function () @@ -40,6 +51,7 @@ describe("net.websocket.frames", function () assert.equal("\0\0", build(test_frames.simple_empty)); assert.equal("\0\5hello", build(test_frames.simple_data)); assert.equal("\128\0", build(test_frames.simple_fin)); + assert.equal("\128\133 \0 \0HeLlO", build(test_frames.with_mask)) end); end); @@ -49,6 +61,7 @@ describe("net.websocket.frames", function () assert.same(test_frames.simple_empty, parse("\0\0")); assert.same(test_frames.simple_data, parse("\0\5hello")); assert.same(test_frames.simple_fin, parse("\128\0")); + assert.same(test_frames.with_mask, parse("\128\133 \0 \0HeLlO")); end); end); -- cgit v1.2.3 From a328a6b58041e2fdca51d5667e71c97409f8920a Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 7 Sep 2019 13:37:47 +0200 Subject: util.strbitop: Library for bitwise operations on strings --- util-src/GNUmakefile | 2 +- util-src/makefile | 2 +- util-src/strbitop.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 util-src/strbitop.c diff --git a/util-src/GNUmakefile b/util-src/GNUmakefile index 054c9201..a8b3529f 100644 --- a/util-src/GNUmakefile +++ b/util-src/GNUmakefile @@ -7,7 +7,7 @@ INSTALL_DATA=install -m644 TARGET?=../util/ ALL=encodings.so hashes.so net.so pposix.so signal.so table.so \ - ringbuffer.so time.so poll.so compat.so + ringbuffer.so time.so poll.so compat.so strbitop.so ifdef RANDOM ALL+=crand.so diff --git a/util-src/makefile b/util-src/makefile index 700633b4..02bad40a 100644 --- a/util-src/makefile +++ b/util-src/makefile @@ -6,7 +6,7 @@ INSTALL_DATA=install -m644 TARGET?=../util/ ALL=encodings.so hashes.so net.so pposix.so signal.so table.so \ - ringbuffer.so time.so poll.so compat.so + ringbuffer.so time.so poll.so compat.so strbitop.so .ifdef $(RANDOM) ALL+=crand.so diff --git a/util-src/strbitop.c b/util-src/strbitop.c new file mode 100644 index 00000000..a26288e5 --- /dev/null +++ b/util-src/strbitop.c @@ -0,0 +1,91 @@ +/* + * This project is MIT licensed. Please see the + * COPYING file in the source package for more information. + * + * Copyright (C) 2016 Kim Alvefur + */ + +#include +#include + +#if (LUA_VERSION_NUM == 501) +#define luaL_setfuncs(L, R, N) luaL_register(L, NULL, R) +#endif + +/* TODO Deduplicate code somehow */ + +int strop_and(lua_State* L) { + luaL_Buffer buf; + size_t a, b, i; + const char* str_a = luaL_checklstring(L, 1, &a); + const char* str_b = luaL_checklstring(L, 2, &b); + + luaL_buffinit(L, &buf); + + if(a == 0 || b == 0) { + lua_settop(L, 1); + return 1; + } + + for(i = 0; i < a; i++) { + luaL_addchar(&buf, str_a[i] & str_b[i % b]); + } + + luaL_pushresult(&buf); + return 1; +} + +int strop_or(lua_State* L) { + luaL_Buffer buf; + size_t a, b, i; + const char* str_a = luaL_checklstring(L, 1, &a); + const char* str_b = luaL_checklstring(L, 2, &b); + + luaL_buffinit(L, &buf); + + if(a == 0 || b == 0) { + lua_settop(L, 1); + return 1; + } + + for(i = 0; i < a; i++) { + luaL_addchar(&buf, str_a[i] | str_b[i % b]); + } + + luaL_pushresult(&buf); + return 1; +} + +int strop_xor(lua_State* L) { + luaL_Buffer buf; + size_t a, b, i; + const char* str_a = luaL_checklstring(L, 1, &a); + const char* str_b = luaL_checklstring(L, 2, &b); + + luaL_buffinit(L, &buf); + + if(a == 0 || b == 0) { + lua_settop(L, 1); + return 1; + } + + for(i = 0; i < a; i++) { + luaL_addchar(&buf, str_a[i] ^ str_b[i % b]); + } + + luaL_pushresult(&buf); + return 1; +} + +LUA_API int luaopen_util_strbitop(lua_State *L) { + luaL_Reg exports[] = { + { "sand", strop_and }, + { "sor", strop_or }, + { "sxor", strop_xor }, + { NULL, NULL } + }; + + lua_newtable(L); + luaL_setfuncs(L, exports, 0); + return 1; +} -- cgit v1.2.3 From 517c36edcf6ee63e15a333af469da91e19c8995a Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 14 Oct 2020 19:41:42 +0200 Subject: net.websocket.frames: Use C string XOR implementation --- net/websocket/frames.lua | 30 +++++++----------------------- spec/net_websocket_frames_spec.lua | 2 +- 2 files changed, 8 insertions(+), 24 deletions(-) diff --git a/net/websocket/frames.lua b/net/websocket/frames.lua index 9cb5f4fe..1d0ac06f 100644 --- a/net/websocket/frames.lua +++ b/net/websocket/frames.lua @@ -13,12 +13,11 @@ local bit = assert(softreq"bit" or softreq"bit32", "No bit module found. See https://prosody.im/doc/depends#bitop"); local band = bit.band; local bor = bit.bor; -local bxor = bit.bxor; local lshift = bit.lshift; local rshift = bit.rshift; -local unpack = table.unpack or unpack; -- luacheck: ignore 113 +local sbit = require "util.strbitop"; +local sxor = sbit.sxor; -local t_concat = table.concat; local s_char= string.char; local s_pack = string.pack; -- luacheck: ignore 143 local s_unpack = string.unpack; -- luacheck: ignore 143 @@ -107,7 +106,7 @@ local function parse_frame_header(frame) end if result.MASK then - result.key = { frame:byte(length_bytes+3, length_bytes+6) }; + result.key = frame:sub(length_bytes+3, length_bytes+6); end return result, header_length; @@ -116,19 +115,7 @@ end -- XORs the string `str` with the array of bytes `key` -- TODO: optimize local function apply_mask(str, key, from, to) - from = from or 1 - if from < 0 then from = #str + from + 1 end -- negative indices - to = to or #str - if to < 0 then to = #str + to + 1 end -- negative indices - local key_len = #key - local counter = 0; - local data = {}; - for i = from, to do - local key_index = counter%key_len + 1; - counter = counter + 1; - data[counter] = s_char(bxor(key[key_index], str:byte(i))); - end - return t_concat(data); + return sxor(str:sub(from or 1, to or -1), key); end local function parse_frame_body(frame, header, pos) @@ -175,15 +162,12 @@ local function build_frame(desc) local key = "" if desc.MASK then - local key_a = desc.key - if key_a then - key = s_char(unpack(key_a, 1, 4)); - else + key = desc.key + if not key then key = random_bytes(4); - key_a = {key:byte(1,4)}; end b2 = bor(b2, 0x80); - data = apply_mask(data, key_a); + data = apply_mask(data, key); end return s_char(b1, b2) .. length_extra .. key .. data diff --git a/spec/net_websocket_frames_spec.lua b/spec/net_websocket_frames_spec.lua index 9499cee7..244afa3b 100644 --- a/spec/net_websocket_frames_spec.lua +++ b/spec/net_websocket_frames_spec.lua @@ -36,7 +36,7 @@ describe("net.websocket.frames", function () ["opcode"] = 0; ["length"] = 5; ["data"] = "hello"; - ["key"] = { 32, 0, 32, 0, }; + ["key"] = " \0 \0"; ["FIN"] = true; ["MASK"] = true; ["RSV1"] = false; -- cgit v1.2.3 From 12e7ec39fced7a5b6cdc9d40a97c3a91ced980eb Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 15 Oct 2020 14:01:22 +0100 Subject: net.websocket.frames: Add test for empty frame with MASK and key set --- spec/net_websocket_frames_spec.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/spec/net_websocket_frames_spec.lua b/spec/net_websocket_frames_spec.lua index 244afa3b..519be7b9 100644 --- a/spec/net_websocket_frames_spec.lua +++ b/spec/net_websocket_frames_spec.lua @@ -43,6 +43,15 @@ describe("net.websocket.frames", function () ["RSV2"] = false; ["RSV3"] = false; }; + empty_with_mask = { + ["opcode"] = 0; + ["key"] = " \0 \0"; + ["FIN"] = true; + ["MASK"] = true; + ["RSV1"] = false; + ["RSV2"] = false; + ["RSV3"] = false; + }; } describe("build", function () @@ -52,6 +61,7 @@ describe("net.websocket.frames", function () assert.equal("\0\5hello", build(test_frames.simple_data)); assert.equal("\128\0", build(test_frames.simple_fin)); assert.equal("\128\133 \0 \0HeLlO", build(test_frames.with_mask)) + assert.equal("\128\128 \0 \0", build(test_frames.empty_with_mask)) end); end); -- cgit v1.2.3 From a084de6f375e688c6d8ecc89beb47120f64cb3a5 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 15 Oct 2020 16:26:56 +0200 Subject: util.strbitop: Reformat code astyle \ --indent=tab \ --attach-classes \ --indent-switches \ --break-blocks \ --pad-oper \ --unpad-paren \ --add-braces \ --align-pointer=name \ --lineend=linux \ *.c --- util-src/strbitop.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/util-src/strbitop.c b/util-src/strbitop.c index a26288e5..89fce661 100644 --- a/util-src/strbitop.c +++ b/util-src/strbitop.c @@ -14,11 +14,11 @@ /* TODO Deduplicate code somehow */ -int strop_and(lua_State* L) { +int strop_and(lua_State *L) { luaL_Buffer buf; size_t a, b, i; - const char* str_a = luaL_checklstring(L, 1, &a); - const char* str_b = luaL_checklstring(L, 2, &b); + const char *str_a = luaL_checklstring(L, 1, &a); + const char *str_b = luaL_checklstring(L, 2, &b); luaL_buffinit(L, &buf); @@ -35,11 +35,11 @@ int strop_and(lua_State* L) { return 1; } -int strop_or(lua_State* L) { +int strop_or(lua_State *L) { luaL_Buffer buf; size_t a, b, i; - const char* str_a = luaL_checklstring(L, 1, &a); - const char* str_b = luaL_checklstring(L, 2, &b); + const char *str_a = luaL_checklstring(L, 1, &a); + const char *str_b = luaL_checklstring(L, 2, &b); luaL_buffinit(L, &buf); @@ -56,11 +56,11 @@ int strop_or(lua_State* L) { return 1; } -int strop_xor(lua_State* L) { +int strop_xor(lua_State *L) { luaL_Buffer buf; size_t a, b, i; - const char* str_a = luaL_checklstring(L, 1, &a); - const char* str_b = luaL_checklstring(L, 2, &b); + const char *str_a = luaL_checklstring(L, 1, &a); + const char *str_b = luaL_checklstring(L, 2, &b); luaL_buffinit(L, &buf); -- cgit v1.2.3 From 7b3f51b8ee3a085c15edd525efb99816b3e0ba36 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 15 Oct 2020 16:41:51 +0200 Subject: util.strbitop: Add tests covering basics Also as docs --- spec/util_strbitop.lua | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 spec/util_strbitop.lua diff --git a/spec/util_strbitop.lua b/spec/util_strbitop.lua new file mode 100644 index 00000000..58a13772 --- /dev/null +++ b/spec/util_strbitop.lua @@ -0,0 +1,41 @@ +local strbitop = require "util.strbitop"; +describe("util.strbitop", function () + describe("sand()", function () + it("works", function () + assert.equal(string.rep("Aa", 100), strbitop.sand(string.rep("a", 200), "Aa")); + end); + it("returns empty string if first argument is empty", function () + assert.equal("", strbitop.sand("", "")); + assert.equal("", strbitop.sand("", "key")); + end); + it("returns initial string if key is empty", function () + assert.equal("hello", strbitop.sand("hello", "")); + end); + end); + + describe("sor()", function () + it("works", function () + assert.equal(string.rep("a", 200), strbitop.sor(string.rep("Aa", 100), "a")); + end); + it("returns empty string if first argument is empty", function () + assert.equal("", strbitop.sor("", "")); + assert.equal("", strbitop.sor("", "key")); + end); + it("returns initial string if key is empty", function () + assert.equal("hello", strbitop.sor("hello", "")); + end); + end); + + describe("sxor()", function () + it("works", function () + assert.equal(string.rep("Aa", 100), strbitop.sxor(string.rep("a", 200), " \0")); + end); + it("returns empty string if first argument is empty", function () + assert.equal("", strbitop.sxor("", "")); + assert.equal("", strbitop.sxor("", "key")); + end); + it("returns initial string if key is empty", function () + assert.equal("hello", strbitop.sxor("hello", "")); + end); + end); +end); -- cgit v1.2.3 From 65430476ed708a9c828c31fbd45ef462a84794b7 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 15 Oct 2020 16:43:30 +0200 Subject: util.strbitop: Create buffer in the correct size (optimization) This avoids dynamically growing the buffer as Lua does when luaL_addchar is used, thus saving on realloc calls. --- util-src/strbitop.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/util-src/strbitop.c b/util-src/strbitop.c index 89fce661..922048fd 100644 --- a/util-src/strbitop.c +++ b/util-src/strbitop.c @@ -2,7 +2,7 @@ * This project is MIT licensed. Please see the * COPYING file in the source package for more information. * - * Copyright (C) 2016 Kim Alvefur + * Copyright (C) 2016-2020 Kim Alvefur */ #include @@ -27,10 +27,13 @@ int strop_and(lua_State *L) { return 1; } + char *cbuf = luaL_buffinitsize(L, &buf, a); + for(i = 0; i < a; i++) { - luaL_addchar(&buf, str_a[i] & str_b[i % b]); + cbuf[i] = str_a[i] & str_b[i % b]; } + luaL_addsize(&buf, a); luaL_pushresult(&buf); return 1; } @@ -48,10 +51,13 @@ int strop_or(lua_State *L) { return 1; } + char *cbuf = luaL_buffinitsize(L, &buf, a); + for(i = 0; i < a; i++) { - luaL_addchar(&buf, str_a[i] | str_b[i % b]); + cbuf[i] = str_a[i] | str_b[i % b]; } + luaL_addsize(&buf, a); luaL_pushresult(&buf); return 1; } @@ -62,17 +68,18 @@ int strop_xor(lua_State *L) { const char *str_a = luaL_checklstring(L, 1, &a); const char *str_b = luaL_checklstring(L, 2, &b); - luaL_buffinit(L, &buf); - if(a == 0 || b == 0) { lua_settop(L, 1); return 1; } + char *cbuf = luaL_buffinitsize(L, &buf, a); + for(i = 0; i < a; i++) { - luaL_addchar(&buf, str_a[i] ^ str_b[i % b]); + cbuf[i] = str_a[i] ^ str_b[i % b]; } + luaL_addsize(&buf, a); luaL_pushresult(&buf); return 1; } -- cgit v1.2.3 From db01177f86f29a84df010659e6958c93c9746879 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 15 Oct 2020 17:05:53 +0200 Subject: util.strbitop: Remove redundant init function When you have 3 almost identical functions, you tend to edit one and then copypaste. Forgot to remove this line from the other two. --- util-src/strbitop.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/util-src/strbitop.c b/util-src/strbitop.c index 922048fd..8c6367c8 100644 --- a/util-src/strbitop.c +++ b/util-src/strbitop.c @@ -20,8 +20,6 @@ int strop_and(lua_State *L) { const char *str_a = luaL_checklstring(L, 1, &a); const char *str_b = luaL_checklstring(L, 2, &b); - luaL_buffinit(L, &buf); - if(a == 0 || b == 0) { lua_settop(L, 1); return 1; @@ -44,8 +42,6 @@ int strop_or(lua_State *L) { const char *str_a = luaL_checklstring(L, 1, &a); const char *str_b = luaL_checklstring(L, 2, &b); - luaL_buffinit(L, &buf); - if(a == 0 || b == 0) { lua_settop(L, 1); return 1; -- cgit v1.2.3 From dd5c18b666b47170ef53183699ebe6bc983a7e2f Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 15 Oct 2020 17:12:33 +0200 Subject: Back out 6dde2c9fa272: Doesn't work on Lua 5.1 --- util-src/strbitop.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/util-src/strbitop.c b/util-src/strbitop.c index 8c6367c8..2c6a4e4d 100644 --- a/util-src/strbitop.c +++ b/util-src/strbitop.c @@ -2,7 +2,7 @@ * This project is MIT licensed. Please see the * COPYING file in the source package for more information. * - * Copyright (C) 2016-2020 Kim Alvefur + * Copyright (C) 2016 Kim Alvefur */ #include @@ -25,13 +25,10 @@ int strop_and(lua_State *L) { return 1; } - char *cbuf = luaL_buffinitsize(L, &buf, a); - for(i = 0; i < a; i++) { - cbuf[i] = str_a[i] & str_b[i % b]; + luaL_addchar(&buf, str_a[i] & str_b[i % b]); } - luaL_addsize(&buf, a); luaL_pushresult(&buf); return 1; } @@ -47,13 +44,10 @@ int strop_or(lua_State *L) { return 1; } - char *cbuf = luaL_buffinitsize(L, &buf, a); - for(i = 0; i < a; i++) { - cbuf[i] = str_a[i] | str_b[i % b]; + luaL_addchar(&buf, str_a[i] | str_b[i % b]); } - luaL_addsize(&buf, a); luaL_pushresult(&buf); return 1; } @@ -64,18 +58,17 @@ int strop_xor(lua_State *L) { const char *str_a = luaL_checklstring(L, 1, &a); const char *str_b = luaL_checklstring(L, 2, &b); + luaL_buffinit(L, &buf); + if(a == 0 || b == 0) { lua_settop(L, 1); return 1; } - char *cbuf = luaL_buffinitsize(L, &buf, a); - for(i = 0; i < a; i++) { - cbuf[i] = str_a[i] ^ str_b[i % b]; + luaL_addchar(&buf, str_a[i] ^ str_b[i % b]); } - luaL_addsize(&buf, a); luaL_pushresult(&buf); return 1; } -- cgit v1.2.3 From d88ed459e28d88605679cad254c3531db4b7a2f4 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 15 Oct 2020 17:21:58 +0200 Subject: Back out changeset 2c1583bb0e0f Same reason as 712b2e6a09d9 --- util-src/strbitop.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/util-src/strbitop.c b/util-src/strbitop.c index 2c6a4e4d..89fce661 100644 --- a/util-src/strbitop.c +++ b/util-src/strbitop.c @@ -20,6 +20,8 @@ int strop_and(lua_State *L) { const char *str_a = luaL_checklstring(L, 1, &a); const char *str_b = luaL_checklstring(L, 2, &b); + luaL_buffinit(L, &buf); + if(a == 0 || b == 0) { lua_settop(L, 1); return 1; @@ -39,6 +41,8 @@ int strop_or(lua_State *L) { const char *str_a = luaL_checklstring(L, 1, &a); const char *str_b = luaL_checklstring(L, 2, &b); + luaL_buffinit(L, &buf); + if(a == 0 || b == 0) { lua_settop(L, 1); return 1; -- cgit v1.2.3 From c2e10f8ef6e4b9f3aa3d6823acba4351ea69a1f3 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 16 Oct 2020 13:38:04 +0100 Subject: util.debug: Fix locals being reported under wrong stack frame in some cases (+tests!!) --- spec/util_debug_spec.lua | 93 ++++++++++++++++++++++++++++++++++++++++++++++++ util/debug.lua | 2 +- 2 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 spec/util_debug_spec.lua diff --git a/spec/util_debug_spec.lua b/spec/util_debug_spec.lua new file mode 100644 index 00000000..510b7ac0 --- /dev/null +++ b/spec/util_debug_spec.lua @@ -0,0 +1,93 @@ +local dbg = require "util.debug"; + +describe("util.debug", function () + describe("traceback()", function () + it("works", function () + local tb = dbg.traceback(); + assert.is_string(tb); + end); + end); + describe("get_traceback_table()", function () + it("works", function () + local count = 0; + -- MUST stay in sync with the line numbers of these functions: + local f1_defined, f3_defined = 43, 15; + local function f3(f3_param) --luacheck: ignore 212/f3_param + count = count + 1; + + for i = 1, 2 do + local tb = dbg.get_traceback_table(i == 1 and coroutine.running() or nil, 0); + assert.is_table(tb); + --print(dbg.traceback(), "\n\n\n", require "util.serialization".serialize(tb, { fatal = false, unquoted = true})); + local found_f1, found_f3; + for _, frame in ipairs(tb) do + if frame.info.linedefined == f1_defined then + assert.equal(0, #frame.locals); + assert.equal("f2", frame.upvalues[1].name); + assert.equal("f1_upvalue", frame.upvalues[2].name); + found_f1 = true; + elseif frame.info.linedefined == f3_defined then + assert.equal("f3_param", frame.locals[1].name); + found_f3 = true; + end + end + assert.is_true(found_f1); + assert.is_true(found_f3); + end + end + local function f2() + local f2_local = "hello"; + return f3(f2_local); + end + local f1_upvalue = "upvalue1"; + local function f1() + f2(f1_upvalue); + end + + -- ok/err are caught and re-thrown so that + -- busted gets to handle them in its own way + local ok, err; + local function hook() + debug.sethook(); + ok, err = pcall(f1); + end + + -- Test the traceback is correct in various + -- types of caller environments + + -- From a Lua hook + debug.sethook(hook, "crl", 1); + local a = string.sub("abcdef", 3, 4); + assert.equal("cd", a); + debug.sethook(); + assert.equal(1, count); + + if not ok then + error(err); + end + ok, err = nil, nil; + + -- From a signal handler (C hook) + require "util.signal".signal("SIGUSR1", hook); + require "util.signal".raise("SIGUSR1"); + assert.equal(2, count); + + if not ok then + error(err); + end + ok, err = nil, nil; + + -- Inside a coroutine + local co = coroutine.create(function () + hook(); + end); + coroutine.resume(co); + + if not ok then + error(err); + end + + assert.equal(3, count); + end); + end); +end); diff --git a/util/debug.lua b/util/debug.lua index 9a28395a..4c924d40 100644 --- a/util/debug.lua +++ b/util/debug.lua @@ -104,7 +104,7 @@ local function get_traceback_table(thread, start_level) levels[(level-start_level)+1] = { level = level; info = info; - locals = get_locals_table(thread, level+(thread and 0 or 1)); + locals = get_locals_table(thread, level+1); upvalues = get_upvalues_table(info.func); }; end -- cgit v1.2.3 From 11dcc6f62a64ea971371b858c642b123c887c852 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 28 Oct 2020 14:21:09 +0000 Subject: util.dbuffer: Optimize :sub() and :byte() --- util/dbuffer.lua | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/util/dbuffer.lua b/util/dbuffer.lua index 9d58b306..640c1449 100644 --- a/util/dbuffer.lua +++ b/util/dbuffer.lua @@ -1,5 +1,6 @@ local queue = require "util.queue"; +local s_byte, s_sub = string.byte, string.sub; local dbuffer_methods = {}; local dynamic_buffer_mt = { __index = dbuffer_methods }; @@ -101,7 +102,11 @@ function dbuffer_methods:discard(requested_bytes) return true; end -function dbuffer_methods:sub(i, j) +-- Normalize i, j into absolute offsets within the +-- front chunk (accounting for front_consumed), and +-- ensure there is enough data in the first chunk +-- to cover any subsequent :sub() or :byte() operation +function dbuffer_methods:_prep_sub(i, j) if j == nil then j = -1; end @@ -118,18 +123,35 @@ function dbuffer_methods:sub(i, j) j = self._length; end if i > j then - return ""; + return nil; end self:collapse(j); - return self.items:peek():sub(self.front_consumed+1):sub(i, j); + if self.front_consumed > 0 then + i = i + self.front_consumed; + j = j + self.front_consumed; + end + + return i, j; +end + +function dbuffer_methods:sub(i, j) + i, j = self:_prep_sub(i, j); + if not i then + return ""; + end + return s_sub(self.items:peek(), 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); + i, j = self:_prep_sub(i, j); + if not i then + return; + end + return s_byte(self.items:peek(), i, j); end function dbuffer_methods:length() -- cgit v1.2.3 From b6d303a710d66140d70899f234d3a76445b19835 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 5 Nov 2020 22:27:17 +0100 Subject: mod_pubsub: Lower priority of default generator in order to avoid conflict with a handler at the default (0) priority, making it easier to write your own formatting in plugins. this follows the common pattern of default modules having lower priority --- plugins/mod_pubsub/mod_pubsub.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_pubsub/mod_pubsub.lua b/plugins/mod_pubsub/mod_pubsub.lua index 4276bc5e..b6019c71 100644 --- a/plugins/mod_pubsub/mod_pubsub.lua +++ b/plugins/mod_pubsub/mod_pubsub.lua @@ -127,7 +127,7 @@ module:hook("pubsub-summary/http://www.w3.org/2005/Atom", function (event) end end return summary; -end); +end, -1); module:hook("iq/host/"..xmlns_pubsub..":pubsub", handle_pubsub_iq); module:hook("iq/host/"..xmlns_pubsub_owner..":pubsub", handle_pubsub_iq); -- cgit v1.2.3 From 1810ddbc6d65799d65ecefd99e68658675d8cbef Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 6 Nov 2020 13:49:40 +0100 Subject: mod_pubsub: Fix notification stanza type setting (fixes #1605) --- plugins/mod_pubsub/mod_pubsub.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_pubsub/mod_pubsub.lua b/plugins/mod_pubsub/mod_pubsub.lua index b6019c71..c13630c9 100644 --- a/plugins/mod_pubsub/mod_pubsub.lua +++ b/plugins/mod_pubsub/mod_pubsub.lua @@ -72,7 +72,7 @@ function simple_broadcast(kind, node, jids, item, actor, node_obj) end local id = new_id(); - local msg_type = node_obj and node_obj.config.message_type or "headline"; + local msg_type = node_obj and node_obj.config.notification_type or "headline"; local message = st.message({ from = module.host, type = msg_type, id = id }) :tag("event", { xmlns = xmlns_pubsub_event }) :tag(kind, { node = node }) -- cgit v1.2.3 From 8a0198f4be5976852e12b6e622882ef3854cbf1f Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 11 Nov 2020 16:00:41 +0100 Subject: util.stanza: Reject ASCII control characters (fixes #1606) --- spec/util_stanza_spec.lua | 1 + util/stanza.lua | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/spec/util_stanza_spec.lua b/spec/util_stanza_spec.lua index 6fbae41a..da29f890 100644 --- a/spec/util_stanza_spec.lua +++ b/spec/util_stanza_spec.lua @@ -200,6 +200,7 @@ describe("util.stanza", function() ["number"] = 1234, ["table"] = {}; ["utf8"] = string.char(0xF4, 0x90, 0x80, 0x80); ["nil"] = "nil"; ["boolean"] = true; + ["control characters"] = "\0\1\2\3"; }; for value_type, value in pairs(invalid_names) do diff --git a/util/stanza.lua b/util/stanza.lua index a90d56b3..cf2818ec 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -45,6 +45,10 @@ local _ENV = nil; local stanza_mt = { __name = "stanza" }; stanza_mt.__index = stanza_mt; +local function valid_xml_cdata(str, attr) + return not s_find(str, attr and "[^\1\9\10\13\20-~\128-\247]" or "[^\9\10\13\20-~\128-\247]"); +end + local function check_name(name, name_type) if type(name) ~= "string" then error("invalid "..name_type.." name: expected string, got "..type(name)); @@ -52,6 +56,8 @@ local function check_name(name, name_type) error("invalid "..name_type.." name: empty string"); elseif s_find(name, "[<>& '\"]") then error("invalid "..name_type.." name: contains invalid characters"); + elseif not valid_xml_cdata(name, name_type == "attribute") then + error("invalid "..name_type.." name: contains control characters"); elseif not valid_utf8(name) then error("invalid "..name_type.." name: contains invalid utf8"); end @@ -60,7 +66,9 @@ end local function check_text(text, text_type) if type(text) ~= "string" then error("invalid "..text_type.." value: expected string, got "..type(text)); - elseif not valid_utf8(text) then + elseif not valid_xml_cdata(text) then + error("invalid "..text_type.." value: contains control characters"); + elseif not valid_utf8(text, false) then error("invalid "..text_type.." value: contains invalid utf8"); end end -- cgit v1.2.3 From ab2d0056a576bed08c65c00afe495858e3f1a446 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Nov 2020 20:38:51 +0000 Subject: util.promise: Use xpcall() for promise function to preserve tracebacks --- util/promise.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/promise.lua b/util/promise.lua index 07c9c4dc..75c8697b 100644 --- a/util/promise.lua +++ b/util/promise.lua @@ -78,7 +78,7 @@ local function new(f) local p = setmetatable({ _state = "pending", _next = next_pending, _pending_on_fulfilled = {}, _pending_on_rejected = {} }, promise_mt); if f then local resolve, reject = new_resolve_functions(p); - local ok, ret = pcall(f, resolve, reject); + local ok, ret = xpcall(f, debug.traceback, resolve, reject); if not ok and p._state == "pending" then reject(ret); end -- cgit v1.2.3 From 01d42aaa1bb99bbccae12fa4b3044e5e2bfc3b4f Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 23 Nov 2020 21:42:52 +0100 Subject: mod_saslauth: Disable 'tls-unique' channel binding with TLS 1.3 (closes #1542) The 'tls-unique' channel binding is undefined in TLS 1.3 according to a single sentence in parenthesis in Apendix C of RFC 8446 This may trigger downgrade protection in clients that were expecting channel binding to be available. --- plugins/mod_saslauth.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/mod_saslauth.lua b/plugins/mod_saslauth.lua index fba84ef8..333640fc 100644 --- a/plugins/mod_saslauth.lua +++ b/plugins/mod_saslauth.lua @@ -252,7 +252,10 @@ module:hook("stream-features", function(event) -- FIXME: would be nice to have this check only once and not for every socket if sasl_handler.add_cb_handler then local socket = origin.conn:socket(); - if socket.getpeerfinished then + local info = socket.info and socket:info(); + if info.protocol == "TLSv1.3" then + log("debug", "Channel binding 'tls-unique' undefined in context of TLS 1.3"); + elseif socket.getpeerfinished then sasl_handler:add_cb_handler("tls-unique", tls_unique); end sasl_handler["userdata"] = { -- cgit v1.2.3 From 5adb3ba2dc673b8195425bfa6035a6dc0a1d9920 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 23 Nov 2020 21:52:46 +0100 Subject: mod_saslauth: Only advertise channel binding if a finished message is available In some cases this method returns nothing, unclear why. --- plugins/mod_saslauth.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_saslauth.lua b/plugins/mod_saslauth.lua index 333640fc..e8bca7c3 100644 --- a/plugins/mod_saslauth.lua +++ b/plugins/mod_saslauth.lua @@ -255,7 +255,7 @@ module:hook("stream-features", function(event) local info = socket.info and socket:info(); if info.protocol == "TLSv1.3" then log("debug", "Channel binding 'tls-unique' undefined in context of TLS 1.3"); - elseif socket.getpeerfinished then + elseif socket.getpeerfinished and socket:getpeerfinished() then sasl_handler:add_cb_handler("tls-unique", tls_unique); end sasl_handler["userdata"] = { -- cgit v1.2.3 From e34e79a6c056481cf75c084d081adfd8f5fe0c04 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 10 Dec 2020 11:53:10 +0100 Subject: mod_s2s: Prevent whitespace keepalives the stream has been opened This will result in the stream timing out instead, which is probably correct if the stream has not been opened yet. This was already done for c2s in e69df8093387 Thanks Ge0rG --- plugins/mod_s2s/mod_s2s.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/mod_s2s/mod_s2s.lua b/plugins/mod_s2s/mod_s2s.lua index 5439aa96..4fce20eb 100644 --- a/plugins/mod_s2s/mod_s2s.lua +++ b/plugins/mod_s2s/mod_s2s.lua @@ -163,7 +163,10 @@ function route_to_new_session(event) end local function keepalive(event) - return event.session.sends2s(' '); + local session = event.session; + if not session.notopen then + return event.session.send(' '); + end end module:hook("s2s-read-timeout", keepalive, -1); -- cgit v1.2.3 From 2d7720dc0957c77ff5b44df1f176e024666e2e70 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 15 Dec 2020 13:36:08 +0000 Subject: MUC: Fix logic bug causing unnecessary presence to be sent, fixes #1615 (thanks damencho) --- plugins/muc/muc.lib.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 3139c131..037baa37 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -1326,8 +1326,8 @@ function room_mt:set_affiliation(actor, jid, affiliation, reason, data) if occupant.role == nil then module:fire_event("muc-occupant-left", {room = self; nick = occupant.nick; occupant = occupant;}); elseif is_semi_anonymous and - (old_role == "moderator" and occupant.role ~= "moderator") or - (old_role ~= "moderator" and occupant.role == "moderator") then -- Has gained or lost moderator status + ((old_role == "moderator" and occupant.role ~= "moderator") or + (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 -- cgit v1.2.3 From 379a6cfa90c5c01387b7a2ec89a29e500e704662 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 16 Dec 2020 08:50:20 +0100 Subject: mod_s2s: Fix copypaste mistake in b3ae48362f78 Directly sending something over s2s needs to use sends2s() but the code copied from mod_c2s had .send() --- plugins/mod_s2s/mod_s2s.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_s2s/mod_s2s.lua b/plugins/mod_s2s/mod_s2s.lua index 4fce20eb..8e398f56 100644 --- a/plugins/mod_s2s/mod_s2s.lua +++ b/plugins/mod_s2s/mod_s2s.lua @@ -165,7 +165,7 @@ end local function keepalive(event) local session = event.session; if not session.notopen then - return event.session.send(' '); + return event.session.sends2s(' '); end end -- cgit v1.2.3 From 086b5cffbb9bb050aad72779634fbc5bb73d6335 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 24 Dec 2020 17:57:28 +0100 Subject: util.stanza: Move misplaced argument to correct place valid_utf8() takes only one argument, so the false was probably meant to be valid_xml_cdata(text, attribute=false) --- util/stanza.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/stanza.lua b/util/stanza.lua index cf2818ec..a8c619d0 100644 --- a/util/stanza.lua +++ b/util/stanza.lua @@ -66,9 +66,9 @@ end local function check_text(text, text_type) if type(text) ~= "string" then error("invalid "..text_type.." value: expected string, got "..type(text)); - elseif not valid_xml_cdata(text) then + elseif not valid_xml_cdata(text, false) then error("invalid "..text_type.." value: contains control characters"); - elseif not valid_utf8(text, false) then + elseif not valid_utf8(text) then error("invalid "..text_type.." value: contains invalid utf8"); end end -- cgit v1.2.3 From a02503d2704ecdb91963a50a6b56c55ae7a8a910 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 8 Jan 2021 21:57:19 +0100 Subject: net.server_epoll: Ensure timers can't run more than once per tick This makes sure that a timer that returns 0 (or less) does not prevent runtimers() from completing, as well as making sure a timer added with zero timeout from within a timer does not run until the next tick. Thanks tmolitor --- net/server_epoll.lua | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/net/server_epoll.lua b/net/server_epoll.lua index 953bbb11..e2be448c 100644 --- a/net/server_epoll.lua +++ b/net/server_epoll.lua @@ -97,10 +97,10 @@ local function runtimers(next_delay, min_wait) -- Any timers at all? local now = gettime(); local peek = timers:peek(); + local readd; while peek do if peek > now then - next_delay = peek - now; break; end @@ -109,13 +109,29 @@ local function runtimers(next_delay, min_wait) if ok and type(ret) == "number" then local next_time = now+ret; timer[1] = next_time; - timers:insert(timer, next_time); + -- Delay insertion of timers to be re-added + -- so they don't get called again this tick + if readd then + readd[id] = timer; + else + readd = { [id] = timer }; + end end peek = timers:peek(); end + + if readd then + for _, timer in pairs(readd) do + timers:insert(timer, timer[2]); + end + peek = timers:peek(); + end + if peek == nil then return next_delay; + else + next_delay = peek - now; end if next_delay < min_wait then -- cgit v1.2.3 From 609b9d1ecff8b15d4f6886413df129c4d41b7b74 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 8 Jan 2021 23:23:56 +0100 Subject: util.timer: Ensure timers can't run more than once per tick (fixes #1620) See longer explanation in 2c559953ad41 --- util/timer.lua | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/util/timer.lua b/util/timer.lua index 4670e196..bc3836be 100644 --- a/util/timer.lua +++ b/util/timer.lua @@ -15,6 +15,7 @@ local debug_traceback = debug.traceback; local tostring = tostring; local xpcall = require "util.xpcall".xpcall; local math_max = math.max; +local pairs = pairs; local _ENV = nil; -- luacheck: std none @@ -29,6 +30,7 @@ local next_time = nil; local function _traceback_handler(err) log("error", "Traceback[timer]: %s", debug_traceback(tostring(err), 2)); end local function _on_timer(now) local peek; + local readd; while true do peek = h:peek(); if peek == nil or peek > now then break; end @@ -38,11 +40,22 @@ local function _on_timer(now) --item(now, id, _param); local success, err = xpcall(callback, _traceback_handler, now, id, param); if success and type(err) == "number" then - h:insert(callback, err + now, id); -- re-add + if readd then + readd[id] = { callback, err + now }; + else + readd = { [id] = { callback, err + now } }; + end params[id] = param; end end + if readd then + for id,timer in pairs(readd) do + h:insert(timer[1], timer[2], id); + end + peek = h:peek(); + end + if peek ~= nil and _active_timers > 1 and peek == next_time then -- Another instance of _on_timer already set next_time to the same value, -- so it should be safe to not renew this timer event -- cgit v1.2.3 From d2b73ddd3e7afaf2c7d44a1d3fb35141ce247acb Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 10 Jan 2021 14:54:03 +0100 Subject: net.server_epoll: Fix off-by-one in 2c559953ad41 Thanks tmolitor --- net/server_epoll.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/server_epoll.lua b/net/server_epoll.lua index e2be448c..53a67dd5 100644 --- a/net/server_epoll.lua +++ b/net/server_epoll.lua @@ -123,7 +123,7 @@ local function runtimers(next_delay, min_wait) if readd then for _, timer in pairs(readd) do - timers:insert(timer, timer[2]); + timers:insert(timer, timer[1]); end peek = timers:peek(); end -- cgit v1.2.3 From 45a89cbb0c700e4bd1ded320d377b328efa44834 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Tue, 12 Jan 2021 13:25:08 +0100 Subject: net.adns: Prevent empty packets from being sent on "connect" (fix #1619) Thanks Ge0rG for testing --- net/adns.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/adns.lua b/net/adns.lua index 560e4b53..0bdf6ee3 100644 --- a/net/adns.lua +++ b/net/adns.lua @@ -50,6 +50,11 @@ local function new_async_socket(sock, resolver) if not handler then return nil, err; end + if handler.set then + -- server_epoll: only watch for incoming data + -- avoids sending empty packet on first 'onwritable' event + handler:set(true, false); + end handler.settimeout = function () end handler.setsockname = function (_, ...) return sock:setsockname(...); end -- cgit v1.2.3 From acdd02670bff6f1db4aa49b086928a3e9714d3cd Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 25 Jan 2021 20:58:11 +0100 Subject: util.interpolation: Fix combination of filters and fallback values #1623 --- util/interpolation.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/interpolation.lua b/util/interpolation.lua index e0ccf47b..3e1f8c4a 100644 --- a/util/interpolation.lua +++ b/util/interpolation.lua @@ -43,11 +43,11 @@ local function new_render(pat, escape, funcs) end end if funcs then - while value ~= nil and opt == '|' do + while opt == '|' do local f; f, raw, opt, e = s_match(block, "^([%a_][%w_.]*)(!?)(%p?)()", e); f = funcs[f]; - if f then value = f(value); end + if value ~= nil and f then value = f(value); end end end if opt == '#' or opt == '%' then -- cgit v1.2.3