Software /
code /
prosody-modules
Changeset
2753:77adb2b5c847
Merge with goffi
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Sun, 27 Aug 2017 21:11:26 +0200 |
parents | 2751:6b710a8bdf03 (diff) 2752:d0e75bf21d30 (current diff) |
children | 2755:dbab58abd3e2 |
files | |
diffstat | 18 files changed, 331 insertions(+), 193 deletions(-) [+] |
line wrap: on
line diff
--- a/mod_auth_http_async/mod_auth_http_async.lua Sun Aug 27 20:46:04 2017 +0200 +++ b/mod_auth_http_async/mod_auth_http_async.lua Sun Aug 27 21:11:26 2017 +0200 @@ -28,6 +28,7 @@ end local function async_http_auth(url, username, password) +module:log("debug", "async_http_auth()"); local http = require "net.http"; local wait, done = async.waiter(); local content, code, request, response; @@ -49,7 +50,8 @@ return nil, "Auth failed. Invalid username or password."; end -local function sync_http_auth(url) +local function sync_http_auth(url,username, password) +module:log("debug", "sync_http_auth()"); local http = require "socket.http"; local https = require "ssl.https"; local request; @@ -60,7 +62,7 @@ end local _, code, headers, status = request{ url = url, - headers = { ACCEPT = "application/json, text/plain, */*"; } + headers = { Authorization = "Basic "..base64(username..":"..password); } }; if type(code) == "number" and code >= 200 and code <= 299 then module:log("debug", "HTTP auth provider confirmed valid password"); @@ -77,7 +79,7 @@ if (have_async) then return async_http_auth(url, username, password); else - return sync_http_auth(url); + return sync_http_auth(url, username, password); end end @@ -111,4 +113,4 @@ }); end -module:provides("auth", provider); +module:provides("auth", provider); \ No newline at end of file
--- a/mod_block_registrations/README.markdown Sun Aug 27 20:46:04 2017 +0200 +++ b/mod_block_registrations/README.markdown Sun Aug 27 21:11:26 2017 +0200 @@ -31,7 +31,7 @@ block_registrations_matching = { "master$" -- matches anything ending with master: postmaster, hostmaster, webmaster, etc. } - block_registrations_require = "^[a-zA-Z0-9_-.]+$" -- Allow only simple ASCII characters in usernames + block_registrations_require = "^[a-zA-Z0-9_.-]+$" -- Allow only simple ASCII characters in usernames Compatibility =============
--- a/mod_captcha_registration/modules/mod_register.lua Sun Aug 27 20:46:04 2017 +0200 +++ b/mod_captcha_registration/modules/mod_register.lua Sun Aug 27 21:11:26 2017 +0200 @@ -205,7 +205,7 @@ local recent_ips = {}; local min_seconds_between_registrations = module:get_option("min_seconds_between_registrations"); local whitelist_only = module:get_option("whitelist_registration_only"); -local whitelisted_ips = module:get_option("registration_whitelist") or { "127.0.0.1" }; +local whitelisted_ips = module:get_option("registration_whitelist") or { "127.0.0.1", "::1" }; local blacklisted_ips = module:get_option("registration_blacklist") or {}; for _, ip in ipairs(whitelisted_ips) do whitelisted_ips[ip] = true; end @@ -386,4 +386,4 @@ route = { ["GET /*"] = handle_http_request; }; -}); \ No newline at end of file +});
--- a/mod_cloud_notify/mod_cloud_notify.lua Sun Aug 27 20:46:04 2017 +0200 +++ b/mod_cloud_notify/mod_cloud_notify.lua Sun Aug 27 21:11:26 2017 +0200 @@ -4,6 +4,7 @@ -- -- This file is MIT/X11 licensed. +local t_insert = table.insert; local st = require"util.stanza"; local jid = require"util.jid"; local dataform = require"util.dataforms".new; @@ -76,10 +77,12 @@ if push_errors[push_identifier] >= max_push_errors then module:log("warn", "Disabling push notifications for identifier '%s'", push_identifier); -- remove push settings from sessions - for _, session in pairs(host_sessions[node].sessions) do - if session.push_identifier == push_identifier then - session.push_identifier = nil; - session.push_settings = nil; + if host_sessions[node] then + for _, session in pairs(host_sessions[node].sessions) do + if session.push_identifier == push_identifier then + session.push_identifier = nil; + session.push_settings = nil; + end end end -- save changed global config @@ -110,7 +113,7 @@ if hashes.sha256(push_identifier, true) == stanza.attr.id then if user_push_services[push_identifier] and user_push_services[push_identifier].jid == from and push_errors[push_identifier] > 0 then push_errors[push_identifier] = 0; - module:log("debug", "Push succeeded, error count for identifier '%s' is now at %s", push_identifier, tostring(push_errors[push_identifier])); + module:log("debug", "Push succeeded, error count for identifier '%s' is now at %s again", push_identifier, tostring(push_errors[push_identifier])); end end end @@ -132,6 +135,8 @@ local push_jid = enable.attr.jid; -- SHOULD contain a 'node' attribute local push_node = enable.attr.node; + -- CAN contain a 'include_payload' attribute + local include_payload = enable.attr.include_payload; if not push_jid then origin.log("debug", "Push notification enable request missing the 'jid' field"); origin.send(st.error_reply(stanza, "modify", "bad-request", "Missing jid")); @@ -146,6 +151,7 @@ local push_service = { jid = push_jid; node = push_node; + include_payload = include_payload; count = 0; options = publish_options and st.preserialize(publish_options); }; @@ -197,6 +203,23 @@ end module:hook("iq-set/self/"..xmlns_push..":disable", push_disable); +-- clone a stanza and strip it +local function strip_stanza(stanza) + local tags = {}; + local new = { name = stanza.name, attr = { xmlns = stanza.attr.xmlns, type = stanza.attr.type }, tags = tags }; + for i=1,#stanza do + local child = stanza[i]; + if type(child) == "table" then -- don't add raw text nodes + if child.name then + child = strip_stanza(child); + t_insert(tags, child); + end + t_insert(new, child); + end + end + return setmetatable(new, st.stanza_mt); +end + local push_form = dataform { { name = "FORM_TYPE"; type = "hidden"; value = "urn:xmpp:push:summary"; }; { name = "message-count"; type = "text-single"; }; @@ -207,54 +230,71 @@ -- http://xmpp.org/extensions/xep-0357.html#publishing local function handle_notify_request(stanza, node, user_push_services) - if not user_push_services or not #user_push_services then return end + local pushes = 0; + if not user_push_services or not #user_push_services then return pushes end for push_identifier, push_info in pairs(user_push_services) do + local send_push = true; -- only send push to this node when not already done for this stanza or if no stanza is given at all if stanza then if not stanza._push_notify then stanza._push_notify = {}; end if stanza._push_notify[push_identifier] then module:log("debug", "Already sent push notification for %s@%s to %s (%s)", node, module.host, push_info.jid, tostring(push_info.node)); - return; + send_push = false; end stanza._push_notify[push_identifier] = true; end - - -- increment count and save it - push_info.count = push_info.count + 1; - push_store:set_identifier(node, push_identifier, push_info); - -- construct push stanza - local stanza_id = hashes.sha256(push_identifier, true); - local push_publish = st.iq({ to = push_info.jid, from = node .. "@" .. module.host, type = "set", id = stanza_id }) - :tag("pubsub", { xmlns = "http://jabber.org/protocol/pubsub" }) - :tag("publish", { node = push_info.node }) - :tag("item") - :tag("notification", { xmlns = xmlns_push }); - local form_data = { - ["message-count"] = tostring(push_info.count); - }; - if stanza and include_sender then - form_data["last-message-sender"] = stanza.attr.from; + + if send_push then + -- increment count and save it + push_info.count = push_info.count + 1; + push_store:set_identifier(node, push_identifier, push_info); + -- construct push stanza + local stanza_id = hashes.sha256(push_identifier, true); + local push_publish = st.iq({ to = push_info.jid, from = module.host, type = "set", id = stanza_id }) + :tag("pubsub", { xmlns = "http://jabber.org/protocol/pubsub" }) + :tag("publish", { node = push_info.node }) + :tag("item") + :tag("notification", { xmlns = xmlns_push }); + local form_data = { + ["message-count"] = tostring(push_info.count); + }; + if stanza and include_sender then + form_data["last-message-sender"] = stanza.attr.from; + end + if stanza and include_body then + form_data["last-message-body"] = stanza:get_child_text("body"); + end + push_publish:add_child(push_form:form(form_data)); + if stanza and push_info.include_payload == "stripped" then + push_publish:tag("payload", { type = "stripped" }) + :add_child(strip_stanza(stanza)); + push_publish:up(); -- / payload + end + if stanza and push_info.include_payload == "full" then + push_publish:tag("payload", { type = "full" }) + :add_child(st.clone(stanza)); + push_publish:up(); -- / payload + end + push_publish:up(); -- / notification + push_publish:up(); -- / publish + push_publish:up(); -- / pubsub + if push_info.options then + push_publish:tag("publish-options"):add_child(st.deserialize(push_info.options)); + end + -- send out push + module:log("debug", "Sending push notification for %s@%s to %s (%s)", node, module.host, push_info.jid, tostring(push_info.node)); + -- module:log("debug", "PUSH STANZA: %s", tostring(push_publish)); + -- handle push errors for this node + if push_errors[push_identifier] == nil then + push_errors[push_identifier] = 0; + module:hook("iq-error/bare/"..stanza_id, handle_push_error); + module:hook("iq-result/bare/"..stanza_id, handle_push_success); + end + module:send(push_publish); + pushes = pushes + 1; end - if stanza and include_body then - form_data["last-message-body"] = stanza:get_child_text("body"); - end - push_publish:add_child(push_form:form(form_data)); - push_publish:up(); -- / notification - push_publish:up(); -- / publish - push_publish:up(); -- / pubsub - if push_info.options then - push_publish:tag("publish-options"):add_child(st.deserialize(push_info.options)); - end - -- send out push - module:log("debug", "Sending push notification for %s@%s to %s (%s)", node, module.host, push_info.jid, tostring(push_info.node)); - -- handle push errors for this node - if push_errors[push_identifier] == nil then - push_errors[push_identifier] = 0; - module:hook("iq-error/bare/"..stanza_id, handle_push_error); - module:hook("iq-result/bare/"..stanza_id, handle_push_success); - end - module:send(push_publish); end + return pushes; end -- small helper function to extract relevant push settings @@ -268,13 +308,14 @@ -- publish on offline message module:hook("message/offline/handle", function(event) local node, user_push_services = get_push_settings(event.stanza, event.origin); - return handle_notify_request(event.stanza, node, user_push_services); + module:log("debug", "Invoking cloud handle_notify_request() for offline stanza"); + handle_notify_request(event.stanza, node, user_push_services); end, 1); -- publish on unacked smacks message local function process_smacks_stanza(stanza, session) if session.push_identifier then - session.log("debug", "Invoking cloud handle_notify_request for smacks queued stanza"); + session.log("debug", "Invoking cloud handle_notify_request() for smacks queued stanza"); local user_push_services = {[session.push_identifier] = session.push_settings}; local node = get_push_settings(stanza, session); handle_notify_request(stanza, node, user_push_services); @@ -282,16 +323,28 @@ return stanza; end +local function process_smacks_queue(queue, session) + if not session.push_identifier then return; end + local user_push_services = {[session.push_identifier] = session.push_settings}; + for i=1, #queue do + local stanza = queue[i]; + local node = get_push_settings(stanza, session); + session.log("debug", "Invoking cloud handle_notify_request() for smacks queued stanza: %d", i); + if handle_notify_request(stanza, node, user_push_services) ~= 0 then + session.log("debug", "Cloud handle_notify_request() > 0, not notifying for other queued stanzas"); + return; -- only notify for one stanza in the queue, not for all in a row + end + end +end + -- smacks hibernation is started local function hibernate_session(event) local session = event.origin; local queue = event.queue; -- process unacked stanzas - for i=1,#queue do - process_smacks_stanza(queue[i], session); - end + process_smacks_queue(queue, session); -- process future unacked (hibernated) stanzas - filters.add_filter(session, "stanzas/out", process_smacks_stanza); + filters.add_filter(session, "stanzas/out", process_smacks_stanza, -990); end -- smacks hibernation is ended @@ -312,9 +365,7 @@ local session = event.origin; local queue = event.queue; -- process unacked stanzas (handle_notify_request() will only send push requests for new stanzas) - for i=1,#queue do - process_smacks_stanza(queue[i], session); - end + process_smacks_queue(queue, session); end -- archive message added @@ -344,13 +395,13 @@ end end if identifier_found then - identifier_found.log("debug", "Not notifying '%s' of new MAM stanza (session still alive)", identifier); + identifier_found.log("debug", "Not cloud notifying '%s' of new MAM stanza (session still alive)", identifier); else notify_push_sevices[identifier] = push_info; end end - return handle_notify_request(event.stanza, to, notify_push_sevices); + handle_notify_request(event.stanza, to, notify_push_sevices); end end @@ -363,26 +414,11 @@ local user = event.user; local user_push_services = push_store:get(user); local push_services = event.push_services or user_push_services; - return handle_notify_request(nil, user, push_services); + handle_notify_request(nil, user, push_services); end -- can be used by other modules to ping one or more (or all) push endpoints module:hook("cloud-notify-ping", send_ping); --- TODO: this has to be done on first connect not on offline broadcast, else the counter will be incorrect --- TODO: it seems this is already done, so this could be safely removed, couldn't it? --- module:hook("message/offline/broadcast", function(event) --- local origin = event.origin; --- local user_push_services = push_store:get(origin.username); --- if not #user_push_services then return end --- --- for _, push_info in pairs(user_push_services) do --- if push_info then --- push_info.count = 0; --- end --- end --- push_store:set(origin.username, user_push_services); --- end, 1); - module:log("info", "Module loaded"); function module.unload() if module.unhook then
--- a/mod_csi_battery_saver/README.markdown Sun Aug 27 20:46:04 2017 +0200 +++ b/mod_csi_battery_saver/README.markdown Sun Aug 27 21:11:26 2017 +0200 @@ -4,23 +4,41 @@ - 'Stage-Alpha' --- +Please use this module instead of [mod_csi_pump] if you want timestamping, +properly handled carbon copies, support for handling encrypted messages and +correctly handled smacks events. + +If smacks is used on the same server this needs at least version [f70c02c14161] +of the smacks module! There could be message reordering on resume otherwise. + Stanzas are queued in a buffer until either an "important" stanza is encountered or the buffer becomes full. Then all queued stanzas are sent at the same time. This way, nothing is lost or reordered while still allowing for power usage savings by not requiring mobile clients to bring up their radio for unimportant stanzas. -`IQ` stanzas, smacks "stanzas" and `message` stanzas containing a body are -considered important. Groupchat messages must set a subject or have -the user's username or nickname in their messages to count as "important". -`Presence` stanzas are not "important". +`IQ` stanzas, and `message` stanzas containing a body or being encypted +and all nonzas are considered important. +If the config option `csi_battery_saver_filter_muc` is set to true, +groupchat messages must set a subject or have the user's username or nickname +in their messages to count as "important", if the config is false (default), all +groupchat messages havin a body or being encrypted are considered "important". +`Presence` stanzas are always considered not "important". All buffered stanzas that allow timestamping are properly stamped to reflect their original send time, see [XEP-0203]. Use with other CSI plugins such as [mod_throttle_presence], [mod_filter_chatstates] or [mod_csi_pump] is *not* supported. -Please use this module instead of [mod_csi_pump] if you want timestamping -and properly handled carbon copies. The internal stanza buffer of this module is hardcoded to 100 stanzas. + +Configuration +============= + + Option Default Description + ---------------------------------- ---------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + `csi_battery_saver_filter_muc` false Controls whether all muc messages having a body should be considered as important (false) or only such containing the user's room nic (true). Warning: you should only set this to true if your users can live with muc messages being delayed several minutes. + + +[f70c02c14161]: //hg.prosody.im/prosody-modules/raw-file/f70c02c14161/mod_smacks/mod_smacks.lua \ No newline at end of file
--- a/mod_csi_battery_saver/mod_csi_battery_saver.lua Sun Aug 27 20:46:04 2017 +0200 +++ b/mod_csi_battery_saver/mod_csi_battery_saver.lua Sun Aug 27 21:11:26 2017 +0200 @@ -2,25 +2,29 @@ -- Copyright (C) 2017 Thilo Molitor -- +local filter_muc = module:get_option_boolean("csi_battery_saver_filter_muc", false); + module:depends"csi" -module:depends"track_muc_joins" +if filter_muc then module:depends"track_muc_joins"; end -- only depend on this module if we actually use it local s_match = string.match; local s_sub = string.sub; local jid = require "util.jid"; local new_queue = require "util.queue".new; local datetime = require "util.datetime"; +local st = require "util.stanza"; local xmlns_delay = "urn:xmpp:delay"; -- a log id for this module instance local id = s_sub(require "util.hashes".sha256(datetime.datetime(), true), 1, 4); + -- Patched version of util.stanza:find() that supports giving stanza names -- without their namespace, allowing for every namespace. local function find(self, path) local pos = 1; local len = #path + 1; - + repeat local xmlns, name, text; local char = s_sub(path, pos, pos); @@ -65,10 +69,11 @@ end return true; end - function q:flush() + function q:flush(alternative_output) + local out = alternative_output or output; local item = self:pop(); while item do - output(item, self); + out(item, self); item = self:pop(); end return true; @@ -94,7 +99,7 @@ local function is_important(stanza, session) local st_name = stanza and stanza.name or nil; - if not st_name then return false; end + if not st_name then return true; end -- nonzas are always important if st_name == "presence" then -- TODO check for MUC status codes? return false; @@ -110,21 +115,39 @@ if carbon then stanza = carbon; end -- carbon copied outgoing messages aren't important (but incoming carbon copies are!) if carbon and stanza_direction == "out" then return false; end - + local st_type = stanza.attr.type; if st_type == "headline" then return false; end + + -- We can't check for nick in encrypted groupchat messages, so let's treat them as important + -- Some clients don't set a body or an empty body for encrypted messages + + -- check omemo https://xmpp.org/extensions/inbox/omemo.html + if stanza:get_child("encrypted", "eu.siacs.conversations.axolotl") or stanza:get_child("encrypted", "urn:xmpp:omemo:0") then return true; end + + -- check xep27 pgp https://xmpp.org/extensions/xep-0027.html + if stanza:get_child("x", "jabber:x:encrypted") then return true; end + + -- check xep373 pgp (OX) https://xmpp.org/extensions/xep-0373.html + if stanza:get_child("openpgp", "urn:xmpp:openpgp:0") then return true; end + local body = stanza:get_child_text("body"); if st_type == "groupchat" then if stanza:get_child_text("subject") then return true; end - if not body then return false; end - if body:find(session.username, 1, true) then return true; end - local rooms = session.rooms_joined; - if not rooms then return false; end - local room_nick = rooms[jid.bare(stanza_direction == "in" and stanza.attr.from or stanza.attr.to)]; - if room_nick and body:find(room_nick, 1, true) then return true; end - return false; + if body == nil or body == "" then return false; end + -- body contains text, let's see if we want to process it further + if filter_muc then + if body:find(session.username, 1, true) then return true; end + local rooms = session.rooms_joined; + if not rooms then return false; end + local room_nick = rooms[jid.bare(stanza_direction == "in" and stanza.attr.from or stanza.attr.to)]; + if room_nick and body:find(room_nick, 1, true) then return true; end + return false; + else + return true; + end end return body ~= nil and body ~= ""; end @@ -134,6 +157,7 @@ module:hook("csi-client-inactive", function (event) local session = event.origin; if session.pump then + session.log("debug", "mod_csi_battery_saver(%s): Client is inactive, buffering unimportant outgoing stanzas", id); session.pump:pause(); else session.log("debug", "mod_csi_battery_saver(%s): Client is inactive the first time, initializing module for this session", id); @@ -142,19 +166,21 @@ session.pump = pump; session._pump_orig_send = session.send; function session.send(stanza) - session.log("debug", "mod_csi_battery_saver(%s): Got stanza: <%s>", id, tostring(stanza.name)); + session.log("debug", "mod_csi_battery_saver(%s): Got outgoing stanza: <%s>", id, tostring(stanza.name or stanza)); local important = is_important(stanza, session); - -- add delay stamp to unimported (buffered) stanzas that can/need be stamped + -- clone stanzas before adding delay stamp and putting them into the queue + if st.is_stanza(stanza) then stanza = st.clone(stanza); end + -- add delay stamp to unimportant (buffered) stanzas that can/need be stamped if not important and is_stamp_needed(stanza, session) then stanza = add_stamp(stanza, session); end + -- add stanza to outgoing queue and flush the buffer if needed pump:push(stanza); if important then - session.log("debug", "mod_csi_battery_saver(%s): Encountered important stanza, flushing buffer: <%s>", id, tostring(stanza.name)); + session.log("debug", "mod_csi_battery_saver(%s): Encountered important stanza, flushing buffer: <%s>", id, tostring(stanza.name or stanza)); pump:flush(); end return true; end end - session.log("debug", "mod_csi_battery_saver(%s): Client is inactive, buffering unimportant stanzas", id); end); module:hook("csi-client-active", function (event) @@ -165,12 +191,39 @@ end end); +-- clean up this session on hibernation start +module:hook("smacks-hibernation-start", function (event) + local session = event.origin; + if session.pump then + session.log("debug", "mod_csi_battery_saver(%s): Hibernation started, flushing buffer and afterwards disabling for this session", id); + session.pump:flush(); + session.send = session._pump_orig_send; + session.pump = nil; + session._pump_orig_send = nil; + end +end); + +-- clean up this session on hibernation end as well +-- but don't change resumed.send(), it is already overwritten with session.send() by the smacks module +module:hook("smacks-hibernation-end", function (event) + local session = event.resumed; + if session.pump then + session.log("debug", "mod_csi_battery_saver(%s): Hibernation ended without being started, flushing buffer and afterwards disabling for this session", id); + session.pump:flush(session.send); -- use the fresh session.send() introduced by the smacks resume + -- don't reset session.send() because this is not the send previously overwritten by this module, but a fresh one + -- session.send = session._pump_orig_send; + session.pump = nil; + session._pump_orig_send = nil; + end +end); + function module.unload() module:log("info", "%s: Unloading module, flushing all buffers", id); local host_sessions = prosody.hosts[module.host].sessions; for _, user in pairs(host_sessions) do for _, session in pairs(user.sessions) do if session.pump then + session.log("debug", "mod_csi_battery_saver(%s): Flushing buffer and restoring to original session.send()", id); session.pump:flush(); session.send = session._pump_orig_send; session.pump = nil;
--- a/mod_http_upload/README.markdown Sun Aug 27 20:46:04 2017 +0200 +++ b/mod_http_upload/README.markdown Sun Aug 27 21:11:26 2017 +0200 @@ -55,14 +55,6 @@ http_upload_quota = 1234 -- bytes ``` -### File types - -Accepted file types can be limited by MIME type: - -``` lua -http_upload_allowed_file_types = { "image/*", "text/plain" } -``` - Path ----
--- a/mod_http_upload/mod_http_upload.lua Sun Aug 27 20:46:04 2017 +0200 +++ b/mod_http_upload/mod_http_upload.lua Sun Aug 27 21:11:26 2017 +0200 @@ -31,7 +31,6 @@ local file_size_limit = module:get_option_number(module.name .. "_file_size_limit", 1024 * 1024); -- 1 MB local quota = module:get_option_number(module.name .. "_quota"); local max_age = module:get_option_number(module.name .. "_expire_after"); -local allowed_file_types = module:get_option_set(module.name .. "_allowed_file_types"); --- sanity local parser_body_limit = module:context("*"):get_option_number("http_max_content_size", 10*1024*1024); @@ -46,7 +45,6 @@ module:depends("disco"); local http_files = module:depends("http_files"); -local mime_map = module:shared("/*/http_files/mime").types; -- namespaces local namespace = "urn:xmpp:http:upload:0"; @@ -90,6 +88,7 @@ if not deleted then module:log("warn", "Could not delete expired upload %s: %s", filename, whynot or "delete failed"); end + os.remove(filename:match("^(.*)[/\\]")); return false; elseif item.time < upload_window and not lfs.attributes(filename) then return false; -- File was not uploaded or has been deleted since @@ -110,7 +109,7 @@ return sum < quota; end -local function handle_request(origin, stanza, xmlns, filename, filesize, mimetype) +local function handle_request(origin, stanza, xmlns, filename, filesize) local username, host = origin.username, origin.host; -- local clients only if origin.type ~= "c2s" then @@ -141,28 +140,6 @@ return true; end - if mime_map then - local file_ext = filename:match("%.([^.]+)$"); - if not mimetype then - mimetype = "application/octet-stream"; - if file_ext then - mimetype = mime_map[file_ext] or mimetype; - end - else - if (not file_ext and mimetype ~= "application/octet-stream") or (file_ext and mime_map[file_ext] ~= mimetype) then - origin.send(st.error_reply(stanza, "modify", "bad-request", "MIME type does not match file extension")); - return true; - end - end - end - - if allowed_file_types then - if not (allowed_file_types:contains(mimetype) or allowed_file_types:contains(mimetype:gsub("/.*", "/*"))) then - origin.send(st.error_reply(stanza, "cancel", "not-allowed", "File type not allowed")); - return true; - end - end - local reply = st.reply(stanza); reply:tag("slot", { xmlns = xmlns }); @@ -207,8 +184,7 @@ local request = stanza.tags[1]; local filename = request.attr.filename; local filesize = tonumber(request.attr.size); - local mimetype = request.attr["content-type"]; - return handle_request(origin, stanza, namespace, filename, filesize, mimetype); + return handle_request(origin, stanza, namespace, filename, filesize); end); module:hook("iq/host/"..legacy_namespace..":request", function (event) @@ -216,8 +192,7 @@ local request = stanza.tags[1]; local filename = request:get_child_text("filename"); local filesize = tonumber(request:get_child_text("size")); - local mimetype = request:get_child_text("content-type"); - return handle_request(origin, stanza, legacy_namespace, filename, filesize, mimetype); + return handle_request(origin, stanza, legacy_namespace, filename, filesize); end); -- http service @@ -281,8 +256,12 @@ local status_line = "HTTP/"..response.request.httpversion.." "..(response.status or codes[response.status_code]); local headers = response.headers; - body = body or response.body or ""; - headers.content_length = #body; + if type(body) == "string" then + headers.content_length = #body; + elseif io.type(body) == "file" then + headers.content_length = body:seek("end"); + body:close(); + end local output = { status_line }; for k,v in pairs(headers) do @@ -307,6 +286,7 @@ local function serve_head(event, path) event.response.send = send_response_sans_body; + event.response.send_file = send_response_sans_body; return serve_uploaded_files(event, path); end
--- a/mod_mam/mod_mam.lua Sun Aug 27 20:46:04 2017 +0200 +++ b/mod_mam/mod_mam.lua Sun Aug 27 21:11:26 2017 +0200 @@ -56,6 +56,8 @@ archive = module:require "fallback_archive"; end +local use_total = true; + local cleanup; -- Handle prefs. @@ -154,7 +156,7 @@ limit = qmax + 1; before = before; after = after; reverse = reverse; - total = true; + total = use_total; }); if not data then @@ -383,14 +385,18 @@ end return math.random(cleanup_interval, cleanup_interval * 2); end); +else + -- Don't ask the backend to count the potentially unbounded number of items, + -- it'll get slow. + use_total = false; end -- Stanzas sent by local clients -module:hook("pre-message/bare", c2s_message_handler, 2); -module:hook("pre-message/full", c2s_message_handler, 2); +module:hook("pre-message/bare", c2s_message_handler, 0); +module:hook("pre-message/full", c2s_message_handler, 0); -- Stanszas to local clients -module:hook("message/bare", message_handler, 2); -module:hook("message/full", message_handler, 2); +module:hook("message/bare", message_handler, 0); +module:hook("message/full", message_handler, 0); module:add_feature(xmlns_mam0); -- COMPAT with XEP-0313 v 0.1
--- a/mod_mam_muc/README.markdown Sun Aug 27 20:46:04 2017 +0200 +++ b/mod_mam_muc/README.markdown Sun Aug 27 21:11:26 2017 +0200 @@ -41,11 +41,8 @@ muc_log_all_rooms = false; -- set to true to force logging of all rooms --- This is the largest number of messages that are allowed to be retrieved in one MAM request. -max_archive_query_results = 20; - -- This is the largest number of messages that are allowed to be retrieved when joining a room. -max_history_messages = 1000; +max_history_messages = 20; ``` Compatibility
--- a/mod_mam_muc/mod_mam_muc.lua Sun Aug 27 20:46:04 2017 +0200 +++ b/mod_mam_muc/mod_mam_muc.lua Sun Aug 27 21:11:26 2017 +0200 @@ -12,7 +12,8 @@ local xmlns_delay = "urn:xmpp:delay"; local xmlns_forward = "urn:xmpp:forward:0"; local xmlns_st_id = "urn:xmpp:sid:0"; -local muc_form_enable_logging = "muc#roomconfig_enablelogging" +local xmlns_muc_user = "http://jabber.org/protocol/muc#user"; +local muc_form_enable = "muc#roomconfig_enablearchiving" local st = require "util.stanza"; local rsm = require "util.rsm"; @@ -40,8 +41,13 @@ local time_now = os.time; local m_min = math.min; local timestamp, timestamp_parse = require "util.datetime".datetime, require "util.datetime".parse; -local max_history_length = module:get_option_number("max_history_messages", 1000); -local default_max_items, max_max_items = 20, module:get_option_number("max_archive_query_results", max_history_length); + +local default_history_length = 20; +local max_history_length = module:get_option_number("max_history_messages", math.huge); + +local function get_historylength(room) + return math.min(room._data.history_length or default_history_length, max_history_length); +end local log_all_rooms = module:get_option_boolean("muc_log_all_rooms", false); local log_by_default = module:get_option_boolean("muc_log_by_default", true); @@ -61,11 +67,11 @@ return false; end -local function logging_enabled(room) +local function archiving_enabled(room) if log_all_rooms then return true; end - local enabled = room._data.logging; + local enabled = room._data.archiving; if enabled == nil then return log_by_default; end @@ -78,7 +84,7 @@ if not new_muc then -- 0.10 or older module:hook("muc-room-created", function (event) local room = event.room; - if logging_enabled(room) then + if archiving_enabled(room) then room.send_history = send_history; room.save_to_history = save_to_history; end @@ -86,7 +92,7 @@ function module.load() for room in each_room() do - if logging_enabled(room) then + if archiving_enabled(room) then room.send_history = send_history; room.save_to_history = save_to_history; end @@ -107,21 +113,21 @@ local room, form = event.room, event.form; table.insert(form, { - name = muc_form_enable_logging, + name = muc_form_enable, type = "boolean", - label = "Enable Logging?", - value = logging_enabled(room), + label = "Enable archiving?", + value = archiving_enabled(room), } ); end); module:hook("muc-config-submitted", function(event) local room, fields, changed = event.room, event.fields, event.changed; - local new = fields[muc_form_enable_logging]; - if new ~= room._data.logging then - room._data.logging = new; + local new = fields[muc_form_enable]; + if new ~= room._data.archiving then + room._data.archiving = new; if type(changed) == "table" then - changed[muc_form_enable_logging] = true; + changed[muc_form_enable] = true; else event.changed = true; end @@ -198,9 +204,14 @@ qstart, qend = vstart, vend; end + module:log("debug", "Archive query id %s from %s until %s)", + tostring(qid), + qstart and timestamp(qstart) or "the dawn of time", + qend and timestamp(qend) or "now"); + -- RSM stuff local qset = rsm.get(query); - local qmax = m_min(qset and qset.max or default_max_items, max_max_items); + local qmax = m_min(qset and qset.max or 20, 20); local reverse = qset and qset.before or false; local before, after = qset and qset.before, qset and qset.after; @@ -212,7 +223,6 @@ limit = qmax + 1; before = before; after = after; reverse = reverse; - total = true; with = "message<groupchat"; }); @@ -220,7 +230,6 @@ origin.send(st.error_reply(stanza, "cancel", "internal-server-error")); return true; end - local total = tonumber(err); local msg_reply_attr = { to = stanza.attr.from, from = stanza.attr.to }; @@ -241,6 +250,14 @@ :tag("forwarded", { xmlns = xmlns_forward }) :tag("delay", { xmlns = xmlns_delay, stamp = timestamp(when) }):up(); + if room:get_whois() ~= "anyone" then + item:maptags(function (tag) + if tag.name == "x" and tag.attr.xmlns == xmlns_muc_user then + return nil; + end + return tag; + end); + end if not is_stanza(item) then item = st.deserialize(item); end @@ -270,22 +287,22 @@ origin.send(st.reply(stanza) :tag("fin", { xmlns = xmlns_mam, queryid = qid, complete = complete }) :add_child(rsm.generate { - first = first, last = last, count = total })); + first = first, last = last })); return true; end); module:hook("muc-get-history", function (event) local room = event.room; - if not logging_enabled(room) then return end + if not archiving_enabled(room) then return end local room_jid = room.jid; - local maxstanzas = event.maxstanzas; + local maxstanzas = event.maxstanzas or math.huge; local maxchars = event.maxchars; local since = event.since; local to = event.to; -- Load all the data! local query = { - limit = m_min(maxstanzas or 20, max_history_length); + limit = math.min(maxstanzas, get_historylength(room)); start = since; reverse = true; with = "message<groupchat"; @@ -303,6 +320,14 @@ item.attr.to = to; item:tag("delay", { xmlns = "urn:xmpp:delay", from = room_jid, stamp = timestamp(when) }):up(); -- XEP-0203 item:tag("stanza-id", { xmlns = xmlns_st_id, by = room_jid, id = id }):up(); + if room:get_whois() ~= "anyone" then + item:maptags(function (tag) + if tag.name == "x" and tag.attr.xmlns == xmlns_muc_user then + return nil; + end + return tag; + end); + end if maxchars then local chars = #tostring(item); if maxchars - chars < 0 then @@ -362,11 +387,25 @@ and jid_prep(tag.attr.by) == self.jid then return nil; end + if tag.name == "x" and tag.attr.xmlns == xmlns_muc_user then + return nil; + end return tag; end); + local stored_stanza = stanza; + + if stanza.name == "message" and self:get_whois() == "anyone" then + stored_stanza = st.clone(stanza); + 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 })); + end + -- Policy check - if not logging_enabled(self) then return end -- Don't log + if not archiving_enabled(self) then return end -- Don't log -- And stash it local with = stanza.name @@ -374,7 +413,7 @@ with = with .. "<" .. stanza.attr.type end - local id = archive:append(room_node, nil, stanza, time_now(), with); + local id = archive:append(room_node, nil, stored_stanza, time_now(), with); if id then stanza:add_direct_child(st.stanza("stanza-id", { xmlns = xmlns_st_id, by = self.jid, id = id }));
--- a/mod_muc_log/README.markdown Sun Aug 27 20:46:04 2017 +0200 +++ b/mod_muc_log/README.markdown Sun Aug 27 21:11:26 2017 +0200 @@ -39,7 +39,6 @@ 0.7 Works 0.8 Works 0.9 Works - 0.6 Works ----- ------- **Note** that per-room configuration only works in 0.9+.
--- a/mod_register_redirect/mod_register_redirect.lua Sun Aug 27 20:46:04 2017 +0200 +++ b/mod_register_redirect/mod_register_redirect.lua Sun Aug 27 21:11:26 2017 +0200 @@ -8,7 +8,7 @@ local st = require "util.stanza" local cman = configmanager -local ip_wl = module:get_option_set("registration_whitelist", { "127.0.0.1" }) +local ip_wl = module:get_option_set("registration_whitelist", { "127.0.0.1", "::1" }) local url = module:get_option_string("registration_url", nil) local inst_text = module:get_option_string("registration_text", nil) local oob = module:get_option_boolean("registration_oob", true)
--- a/mod_register_web/mod_register_web.lua Sun Aug 27 20:46:04 2017 +0200 +++ b/mod_register_web/mod_register_web.lua Sun Aug 27 21:11:26 2017 +0200 @@ -1,6 +1,7 @@ local captcha_options = module:get_option("captcha_options", {}); local nodeprep = require "util.encodings".stringprep.nodeprep; local usermanager = require "core.usermanager"; +local datamanager = require "util.datamanager"; local http = require "net.http"; local path_sep = package.config:sub(1,1); local json = require "util.json".decode; @@ -39,12 +40,12 @@ function generate_captcha(display_options) return recaptcha_tpl.apply(setmetatable({ - recaptcha_display_error = display_options and display_options.recaptcha_error - and ("&error="..display_options.recaptcha_error) or ""; - }, { - __index = function (t, k) - if captcha_options[k] then return captcha_options[k]; end - module:log("error", "Missing parameter from captcha_options: %s", k); + recaptcha_display_error = display_options and display_options.recaptcha_error + and ("&error="..display_options.recaptcha_error) or ""; + }, { + __index = function (t, k) + if captcha_options[k] then return captcha_options[k]; end + module:log("error", "Missing parameter from captcha_options: %s", k); end })); end @@ -128,6 +129,9 @@ if not registering.allowed then return nil, "Registration not allowed"; end + if form.confirm_password ~= form.password then + return nil, "Passwords don't match"; + end local ok, err = usermanager.create_user(prepped_username, form.password, module.host); if ok then local extra_data = {};
--- a/mod_register_web/templates/register.html Sun Aug 27 20:46:04 2017 +0200 +++ b/mod_register_web/templates/register.html Sun Aug 27 21:11:26 2017 +0200 @@ -18,6 +18,10 @@ <th>Password:</th> <td><input name="password" required type="password"></td> </tr> + <tr> + <th>Confirm Password:</th> + <td><input name="confirm_password" required type="password"></td> + </tr> {captcha} <tr> <td colspan="2"><input type="submit" value="Register!"></td>
--- a/mod_secure_interfaces/mod_secure_interfaces.lua Sun Aug 27 20:46:04 2017 +0200 +++ b/mod_secure_interfaces/mod_secure_interfaces.lua Sun Aug 27 21:11:26 2017 +0200 @@ -1,13 +1,18 @@ -local secure_interfaces = module:get_option_set("secure_interfaces", { "127.0.0.1" }); +local secure_interfaces = module:get_option_set("secure_interfaces", { "127.0.0.1", "::1" }); module:hook("stream-features", function (event) local session = event.origin; if session.type ~= "c2s_unauthed" then return; end local socket = session.conn:socket(); - if not socket.getsockname then return; end + if not socket.getsockname then + module:log("debug", "Unable to determine local address of incoming connection"); + return; + end local localip = socket:getsockname(); if secure_interfaces:contains(localip) then - module:log("debug", "Marking session from %s as secure", session.ip or "[?]"); + module:log("debug", "Marking session from %s to %s as secure", session.ip or "[?]", localip); session.secure = true; + else + module:log("debug", "Not marking session from %s to %s as secure", session.ip or "[?]", localip); end end, 2500);
--- a/mod_smacks/mod_smacks.lua Sun Aug 27 20:46:04 2017 +0200 +++ b/mod_smacks/mod_smacks.lua Sun Aug 27 21:11:26 2017 +0200 @@ -152,12 +152,12 @@ local function request_ack_if_needed(session, force) local queue = session.outgoing_stanza_queue; - if session.awaiting_ack == nil then + if session.awaiting_ack == nil and not session.hibernating then if (#queue > max_unacked_stanzas and session.last_queue_count ~= #queue) or force then session.log("debug", "Queuing <r> (in a moment)"); session.awaiting_ack = false; session.awaiting_ack_timer = stoppable_timer(1e-06, function () - if not session.awaiting_ack then + if not session.awaiting_ack and not session.hibernating then session.log("debug", "Sending <r> (inside timer, before send)"); (session.sends2s or session.send)(st.stanza("r", { xmlns = session.smacks })) session.log("debug", "Sending <r> (inside timer, after send)"); @@ -170,15 +170,17 @@ end end); end - -- Trigger "smacks-ack-delayed"-event if we added new (ackable) stanzas to the outgoing queue - -- and there isn't already a timer for this event running. - -- If we wouldn't do this, stanzas added to the queue after the first "smacks-ack-delayed"-event - -- would not trigger this event (again). - if #queue > max_unacked_stanzas and session.awaiting_ack and session.delayed_ack_timer == nil then - session.log("debug", "Calling delayed_ack_function directly (still waiting for ack)"); - delayed_ack_function(session); - end end + + -- Trigger "smacks-ack-delayed"-event if we added new (ackable) stanzas to the outgoing queue + -- and there isn't already a timer for this event running. + -- If we wouldn't do this, stanzas added to the queue after the first "smacks-ack-delayed"-event + -- would not trigger this event (again). + if #queue > max_unacked_stanzas and session.awaiting_ack and session.delayed_ack_timer == nil then + session.log("debug", "Calling delayed_ack_function directly (still waiting for ack)"); + delayed_ack_function(session); + end + session.last_queue_count = #queue; end @@ -507,7 +509,6 @@ -- Ok, we need to re-send any stanzas that the client didn't see -- ...they are what is now left in the outgoing stanza queue local queue = original_session.outgoing_stanza_queue; - module:fire_event("smacks-hibernation-end", {origin = session, resumed = original_session, queue = queue}); original_session.log("debug", "#queue = %d", #queue); for i=1,#queue do original_session.send(queue[i]); @@ -517,6 +518,7 @@ session.log("warn", "Tried to send stanza on old session migrated by smacks resume (maybe there is a bug?): %s", tostring(stanza)); return false; end + module:fire_event("smacks-hibernation-end", {origin = session, resumed = original_session, queue = queue}); request_ack_if_needed(original_session, true); else module:log("warn", "Client %s@%s[%s] tried to resume stream for %s@%s[%s]",
--- a/mod_webpresence/README.markdown Sun Aug 27 20:46:04 2017 +0200 +++ b/mod_webpresence/README.markdown Sun Aug 27 21:11:26 2017 +0200 @@ -50,6 +50,8 @@ ============= ----- ------- + trunk Works + 0.10 Works 0.9 Works 0.8 Works 0.7 Works @@ -59,7 +61,6 @@ Todo ==== -- JSON? - Display PEP information (maybe a new plugin?) - More (free) iconsets - Internal/external image generator (GD, ImageMagick)