diff --git a/debian/patches/0001-mod_mam_disco.path b/debian/patches/0001-mod_mam_disco.path index fe3f0bd..5a60e35 100644 --- a/debian/patches/0001-mod_mam_disco.path +++ b/debian/patches/0001-mod_mam_disco.path @@ -39,10 +39,12 @@ Change-Id: Ib79b6f2f60f71a80030bbd12b2eb893b60ac025d 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 61b4388..f8a2321 100644 --- a/plugins/mod_mam/mod_mam.lua +++ b/plugins/mod_mam/mod_mam.lua -@@ -393,6 +393,8 @@ module:hook("message/bare", message_hand - module:hook("message/full", message_handler, 0); +@@ -423,6 +423,8 @@ module:hook("message/bare", message_handler, priority); + module:hook("message/full", message_handler, priority); module:add_feature(xmlns_mam0); -- COMPAT with XEP-0313 v 0.1 +module:add_feature(xmlns_mam1); -- COMPAT with XEP-0313 v 0.5 diff --git a/debian/patches/0002-TT-8297-mam-implement-archive-to-DB.patch b/debian/patches/0002-TT-8297-mam-implement-archive-to-DB.patch index 59051ba..1941d95 100644 --- a/debian/patches/0002-TT-8297-mam-implement-archive-to-DB.patch +++ b/debian/patches/0002-TT-8297-mam-implement-archive-to-DB.patch @@ -1,20 +1,21 @@ -From 0dd1a7404c27b4a9352f196d52462b7ae6237590 Mon Sep 17 00:00:00 2001 From: Victor Seva Date: Tue, 3 Jan 2017 17:14:49 +0100 Subject: [PATCH] TT#8297 mam: implement archive to DB Change-Id: If9644ac66fdd257a430cfb7fa185230c77cd783e + +Change-Id: I587debda7ed78cc51088bd4a5ba96c878db45e6f --- - plugins/mod_mam/mod_mam.lua | 17 +--- + plugins/mod_mam/mod_mam.lua | 16 +--- plugins/mod_mam/sipwise_archive.lib.lua | 165 ++++++++++++++++++++++++++++++++ - 2 files changed, 166 insertions(+), 16 deletions(-) + 2 files changed, 166 insertions(+), 15 deletions(-) create mode 100644 plugins/mod_mam/sipwise_archive.lib.lua diff --git a/plugins/mod_mam/mod_mam.lua b/plugins/mod_mam/mod_mam.lua -index a4aa014..13e0a0b 100644 +index f8a2321..faff0dd 100644 --- a/plugins/mod_mam/mod_mam.lua +++ b/plugins/mod_mam/mod_mam.lua -@@ -40,22 +40,7 @@ if global_default_policy ~= "roster" then +@@ -40,21 +40,7 @@ if global_default_policy ~= "roster" then global_default_policy = module:get_option_boolean("default_archive_policy", global_default_policy); end @@ -33,11 +34,10 @@ index a4aa014..13e0a0b 100644 - module:log("info", "Using in-memory fallback archive driver"); - archive = module:require "fallback_archive"; -end -- +local archive = module:require "sipwise_archive"; - local cleanup; - -- Handle prefs. + local use_total = true; + diff --git a/plugins/mod_mam/sipwise_archive.lib.lua b/plugins/mod_mam/sipwise_archive.lib.lua new file mode 100644 index 0000000..f772ea6 @@ -209,6 +209,3 @@ index 0000000..f772ea6 +end + +return archive_store; --- -2.11.0 - diff --git a/debian/patches/0003-TT-9303-mod_mam-sipwise_archive.lib-fix-crash-at-fin.patch b/debian/patches/0003-TT-9303-mod_mam-sipwise_archive.lib-fix-crash-at-fin.patch index 3c6bba2..0126b7d 100644 --- a/debian/patches/0003-TT-9303-mod_mam-sipwise_archive.lib-fix-crash-at-fin.patch +++ b/debian/patches/0003-TT-9303-mod_mam-sipwise_archive.lib-fix-crash-at-fin.patch @@ -1,4 +1,3 @@ -From 9e93fb65101c48a62fec403541216f99e24a121a Mon Sep 17 00:00:00 2001 From: Victor Seva Date: Thu, 19 Jan 2017 16:25:24 +0100 Subject: [PATCH] TT#9303 mod_mam: sipwise_archive.lib fix crash at find() @@ -38,6 +37,3 @@ index f772ea6..087c306 100644 res = engine:select(select_key_query, key); local out = {}; for row in res do --- -2.11.0 - diff --git a/debian/patches/0004-TT-9311-mod_mam-sipwise_archive.lib-full-username.patch b/debian/patches/0004-TT-9311-mod_mam-sipwise_archive.lib-full-username.patch index 3edd3b5..e51073c 100644 --- a/debian/patches/0004-TT-9311-mod_mam-sipwise_archive.lib-full-username.patch +++ b/debian/patches/0004-TT-9311-mod_mam-sipwise_archive.lib-full-username.patch @@ -1,4 +1,3 @@ -From b17aec94a0563c5fab58ed350c1e32787ae3f7fe Mon Sep 17 00:00:00 2001 From: Victor Seva Date: Thu, 19 Jan 2017 16:38:26 +0100 Subject: [PATCH] TT#9311 mod_mam/sipwise_archive.lib: full username @@ -62,6 +61,3 @@ index 087c306..46166f8 100644 engine.conn:commit(); return true; end --- -2.11.0 - diff --git a/debian/patches/0005-TT-9317-mod_mam-don-t-store-bodyless-chat-messages.patch b/debian/patches/0005-TT-9317-mod_mam-don-t-store-bodyless-chat-messages.patch index 7e997bc..c6f68b3 100644 --- a/debian/patches/0005-TT-9317-mod_mam-don-t-store-bodyless-chat-messages.patch +++ b/debian/patches/0005-TT-9317-mod_mam-don-t-store-bodyless-chat-messages.patch @@ -1,4 +1,3 @@ -From 9e01a7a10fc1cd8a2d7dc6fe65bdccccf90f8668 Mon Sep 17 00:00:00 2001 From: Victor Seva Date: Thu, 19 Jan 2017 17:13:12 +0100 Subject: [PATCH] TT#9317 mod_mam: don't store bodyless chat messages @@ -16,12 +15,12 @@ Change-Id: I590b1fb9bd95afdce6a117778052d11d8102f718 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/plugins/mod_mam/mod_mam.lua b/plugins/mod_mam/mod_mam.lua -index 13e0a0b..8d8ef62 100644 +index faff0dd..759a5e2 100644 --- a/plugins/mod_mam/mod_mam.lua +++ b/plugins/mod_mam/mod_mam.lua -@@ -269,19 +269,30 @@ local function message_handler(event, c2s) - return tag; - end); +@@ -285,19 +285,30 @@ local function message_handler(event, c2s) + event.stanza = stanza; + end - -- We store chat messages or normal messages that have a body - if not(orig_type == "chat" or (orig_type == "normal" and stanza:get_child("body")) ) then @@ -56,6 +55,3 @@ index 13e0a0b..8d8ef62 100644 end -- Check with the users preferences --- -2.11.0 - diff --git a/debian/patches/0007-TT-27653-mod_mam-sipwise_archive.lib-fix-uuid-trailing-garbage.patch b/debian/patches/0007-TT-27653-mod_mam-sipwise_archive.lib-fix-uuid-trailing-garbage.patch index 637ed93..3a58af1 100644 --- a/debian/patches/0007-TT-27653-mod_mam-sipwise_archive.lib-fix-uuid-trailing-garbage.patch +++ b/debian/patches/0007-TT-27653-mod_mam-sipwise_archive.lib-fix-uuid-trailing-garbage.patch @@ -1,6 +1,16 @@ +From: Sipwise Development Team +Date: Wed, 11 Dec 2019 11:15:14 +0100 +Subject: TT-27653-mod_mam-sipwise_archive.lib-fix-uuid-trailing-garbage + +--- + plugins/mod_mam/sipwise_archive.lib.lua | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/plugins/mod_mam/sipwise_archive.lib.lua b/plugins/mod_mam/sipwise_archive.lib.lua +index e58f593..15acb33 100644 --- a/plugins/mod_mam/sipwise_archive.lib.lua +++ b/plugins/mod_mam/sipwise_archive.lib.lua -@@ -56,7 +56,7 @@ +@@ -56,7 +56,7 @@ local function load_db(query, _params) res = engine:select(query, unpack(_params)); local out = {}; for row in res do diff --git a/debian/patches/0008-TT-35053-mod_mam-sipwise_archive.lib-fix-retrieving-incoming.patch b/debian/patches/0008-TT-35053-mod_mam-sipwise_archive.lib-fix-retrieving-incoming.patch index 8678c6b..eeda378 100644 --- a/debian/patches/0008-TT-35053-mod_mam-sipwise_archive.lib-fix-retrieving-incoming.patch +++ b/debian/patches/0008-TT-35053-mod_mam-sipwise_archive.lib-fix-retrieving-incoming.patch @@ -1,6 +1,16 @@ +From: Sipwise Development Team +Date: Wed, 11 Dec 2019 11:15:14 +0100 +Subject: TT-35053-mod_mam-sipwise_archive.lib-fix-retrieving-incoming + +--- + plugins/mod_mam/sipwise_archive.lib.lua | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/plugins/mod_mam/sipwise_archive.lib.lua b/plugins/mod_mam/sipwise_archive.lib.lua +index 15acb33..533df4f 100644 --- a/plugins/mod_mam/sipwise_archive.lib.lua +++ b/plugins/mod_mam/sipwise_archive.lib.lua -@@ -36,7 +36,7 @@ +@@ -36,7 +36,7 @@ WHERE `key` = UuidToBin(?); local select_query_base=[[ SELECT UuidFromBin(`key`),`stanza`,`epoch`,`with` FROM `sipwise_mam` @@ -9,7 +19,7 @@ ]] -- Reconnect to DB if necessary -@@ -100,7 +100,7 @@ +@@ -100,7 +100,7 @@ function archive_store:find(username, query) local qstart, qend, qwith = -math.huge, math.huge; local qlimit, qid; local db_query = select_query_base; @@ -18,7 +28,7 @@ local i, values = 0; if query then -@@ -118,7 +118,8 @@ +@@ -118,7 +118,8 @@ function archive_store:find(username, query) end if qwith then diff --git a/debian/patches/0009-TT-35604-mod_sipwise_pushd.lib-add-silent-push b/debian/patches/0009-TT-35604-mod_sipwise_pushd.lib-add-silent-push index 28c5cf3..044fd36 100644 --- a/debian/patches/0009-TT-35604-mod_sipwise_pushd.lib-add-silent-push +++ b/debian/patches/0009-TT-35604-mod_sipwise_pushd.lib-add-silent-push @@ -1,6 +1,16 @@ +From: Sipwise Development Team +Date: Wed, 11 Dec 2019 11:15:14 +0100 +Subject: TT-35604-mod_sipwise_pushd.lib-add-silent-push + +--- + plugins/mod_sipwise_pushd.lua | 58 ++++++++++++++++++++++++++++++++++++------- + 1 file changed, 49 insertions(+), 9 deletions(-) + +diff --git a/plugins/mod_sipwise_pushd.lua b/plugins/mod_sipwise_pushd.lua +index e29453a..229a2dd 100644 --- a/plugins/mod_sipwise_pushd.lua +++ b/plugins/mod_sipwise_pushd.lua -@@ -61,6 +61,18 @@ +@@ -61,6 +61,18 @@ SELECT vp.attribute, vup.value FROM provisioning.voip_preferences vp WHERE vp.attribute = 'mobile_push_enable' AND vd.domain = ?; ]]; @@ -19,7 +29,7 @@ local engine; -- luacheck: ignore request -@@ -74,6 +86,21 @@ +@@ -74,6 +86,21 @@ local function process_response(response, code, request) end end @@ -41,7 +51,7 @@ local function push_enable(username, domain) -- Reconnect to DB if necessary if not engine.conn:ping() then -@@ -303,20 +330,25 @@ +@@ -303,20 +330,25 @@ local function handle_offline(event) msg.data_sender_name = tostring(caller_info.display_name); return msg; end @@ -74,7 +84,7 @@ if stanza.attr.type == 'groupchat' then msg.data_type = 'groupchat' caller_jid = get_muc_caller(stanza.attr.from); -@@ -325,6 +357,7 @@ +@@ -325,6 +357,7 @@ local function handle_offline(event) caller_info = get_caller_info(caller_jid, caller_defaults) or caller_defaults; muc = get_muc_info(stanza, caller_info); @@ -82,7 +92,7 @@ else msg.data_type = 'message'; caller_jid = format("%s@%s", -@@ -340,13 +373,19 @@ +@@ -340,13 +373,19 @@ local function handle_offline(event) caller_info = get_caller_info(caller_jid, caller_defaults) or caller_defaults; muc = get_muc_info(stanza, caller_info); @@ -103,7 +113,7 @@ end return msg; end -@@ -445,6 +484,7 @@ +@@ -445,6 +484,7 @@ local function handle_muc_offline(event, room_jid) end local function handle_msg(event) @@ -111,7 +121,7 @@ local stanza = event.stanza; local room_jid = stanza.attr.to; -@@ -529,5 +569,5 @@ +@@ -529,5 +569,5 @@ function module.load() sql_config = module:get_option("auth_sql", sql_config); engine = mod_sql:create_engine(sql_config); engine:execute("SET NAMES 'utf8' COLLATE 'utf8_bin';"); diff --git a/debian/patches/mod_auth_sql.path b/debian/patches/mod_auth_sql.path index ee57cfd..0ea177c 100644 --- a/debian/patches/mod_auth_sql.path +++ b/debian/patches/mod_auth_sql.path @@ -1,7 +1,13 @@ From: Andreas Granig -Date: Wed Aug 7 22:34:34 2013 +0200 +Date: Wed, 7 Aug 2013 22:34:34 +0200 Subject: sipwise custom auth + --- + plugins/mod_auth_sql/mod_auth_sql.lua | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/plugins/mod_auth_sql/mod_auth_sql.lua b/plugins/mod_auth_sql/mod_auth_sql.lua +index 2947a91..8ad8f4a 100644 --- a/plugins/mod_auth_sql/mod_auth_sql.lua +++ b/plugins/mod_auth_sql/mod_auth_sql.lua @@ -71,7 +71,7 @@ local function getsql(sql, ...) diff --git a/plugins/mod_blocking/mod_blocking.lua b/plugins/mod_blocking/mod_blocking.lua index afe0d2f..092de6f 100644 --- a/plugins/mod_blocking/mod_blocking.lua +++ b/plugins/mod_blocking/mod_blocking.lua @@ -113,7 +113,7 @@ local function send_push_iqs(username, host, command_type, jids) end for resource, session in pairs(prosody.bare_sessions[bare_jid].sessions) do - local iq_push_stanza = st.iq({ type = "set", to = bare_jid.."/"..resource }); + local iq_push_stanza = st.iq({ type = "set", to = bare_jid.."/"..resource, id = "blocking-push" }); iq_push_stanza:add_child(stanza_content); session.send(iq_push_stanza); end diff --git a/plugins/mod_carbons/README.markdown b/plugins/mod_carbons/README.markdown index 1778134..3d966fd 100644 --- a/plugins/mod_carbons/README.markdown +++ b/plugins/mod_carbons/README.markdown @@ -1,6 +1,6 @@ --- labels: -- 'Stage-Beta' +- 'Stage-Merged' summary: Message Carbons ... diff --git a/plugins/mod_carbons_adhoc/mod_carbons_adhoc.lua b/plugins/mod_carbons_adhoc/mod_carbons_adhoc.lua index 5fa64f8..406a0b0 100644 --- a/plugins/mod_carbons_adhoc/mod_carbons_adhoc.lua +++ b/plugins/mod_carbons_adhoc/mod_carbons_adhoc.lua @@ -15,8 +15,6 @@ local xmlns_carbons_v0 = "urn:xmpp:carbons:0"; local bare_sessions = bare_sessions; local function adhoc_status(self, data, state) - local result; - local bare_jid = jid_bare(data.from); local user_sessions = bare_sessions[bare_jid]; diff --git a/plugins/mod_csi/README.markdown b/plugins/mod_csi/README.markdown index bc748eb..1ed4eef 100644 --- a/plugins/mod_csi/README.markdown +++ b/plugins/mod_csi/README.markdown @@ -1,22 +1,20 @@ --- summary: Client State Indication support +labels: +- 'Stage-Merged' ... Introduction ============ -This module implements [Client State +This module implements [XEP-0352: Client State Indication](http://xmpp.org/extensions/xep-0352.html), a way for mobile clients to tell the server that they are sitting in someones pocket and would rather not get some less urgent things pushed to it. -However this module does not do anything by itself. Deciding what things -are considered "less urgent" is left to other modules. - -- [mod\_throttle\_presence](/mod_throttle_presence.html) supresses - presence updates -- [mod\_filter\_chatstates](/mod_filter_chatstates.html) removes chat - states (*Someone is typing...*) +This module has been merged into Prosody 0.11. Please see the +[mod_csi documentation](https://prosody.im/doc/modules/mod_csi) for more +information about how it is used. Configuration ============= @@ -30,3 +28,7 @@ Compatibility ----- ------- 0.9 Works ----- ------- + 0.10 Works + ----- ------- + 0.11 Works (included) + ----- ------- diff --git a/plugins/mod_csi/mod_csi.lua b/plugins/mod_csi/mod_csi.lua index 03f052b..84476ca 100644 --- a/plugins/mod_csi/mod_csi.lua +++ b/plugins/mod_csi/mod_csi.lua @@ -11,6 +11,7 @@ end); function refire_event(name) return function (event) if event.origin.username then + event.origin.state = event.stanza.name; module:fire_event(name, event); return true; end diff --git a/plugins/mod_mam/README.markdown b/plugins/mod_mam/README.markdown index bba0708..5de1096 100644 --- a/plugins/mod_mam/README.markdown +++ b/plugins/mod_mam/README.markdown @@ -1,6 +1,6 @@ --- labels: -- 'Stage-Beta' +- 'Stage-Merged' summary: 'XEP-0313: Message Archive Management' ... diff --git a/plugins/mod_mam/mamprefs.lib.lua b/plugins/mod_mam/mamprefs.lib.lua index 9f2b8ae..cd54e0f 100644 --- a/plugins/mod_mam/mamprefs.lib.lua +++ b/plugins/mod_mam/mamprefs.lib.lua @@ -24,9 +24,11 @@ local prefs = module:open_store(archive_store .. "_prefs"); local function get_prefs(user) local user_sessions = sessions[user]; local user_prefs = user_sessions and user_sessions.archive_prefs - if not user_prefs and user_sessions then + if not user_prefs then user_prefs = prefs:get(user); - user_sessions.archive_prefs = user_prefs; + if user_sessions then + user_sessions.archive_prefs = user_prefs; + end end return user_prefs or { [false] = global_default_policy }; end diff --git a/plugins/mod_mam/mod_mam.lua b/plugins/mod_mam/mod_mam.lua index 0801b09..61b4388 100644 --- a/plugins/mod_mam/mod_mam.lua +++ b/plugins/mod_mam/mod_mam.lua @@ -56,8 +56,17 @@ if archive.name == "null" or not archive.find then archive = module:require "fallback_archive"; end +local use_total = true; + local cleanup; +local function schedule_cleanup(username) + if cleanup and not cleanup[username] then + table.insert(cleanup, username); + cleanup[username] = true; + end +end + -- Handle prefs. local function handle_prefs(event) local origin, stanza = event.origin, event.stanza; @@ -111,7 +120,9 @@ local function handle_mam_query(event) local query = stanza.tags[1]; local qid = query.attr.queryid; - if cleanup then cleanup[origin.username] = true; end + origin.mam_requested = true; + + schedule_cleanup(origin.username); -- Search query parameters local qwith, qstart, qend; @@ -154,7 +165,7 @@ local function handle_mam_query(event) limit = qmax + 1; before = before; after = after; reverse = reverse; - total = true; + total = use_total; }); if not data then @@ -274,15 +285,19 @@ local function message_handler(event, c2s) local with = jid_bare(c2s and orig_to or orig_from); -- Filter out that claim to be from us - stanza:maptags(function (tag) - if tag.name == "stanza-id" and tag.attr.xmlns == xmlns_st_id then - local by_user, by_host, res = jid_prepped_split(tag.attr.by); - if not res and by_host == module.host and by_user == store_user then - return nil; + if stanza:get_child("stanza-id", xmlns_st_id) then + stanza = st.clone(stanza); + stanza:maptags(function (tag) + if tag.name == "stanza-id" and tag.attr.xmlns == xmlns_st_id then + local by_user, by_host, res = jid_prepped_split(tag.attr.by); + if not res and by_host == module.host and by_user == store_user then + return nil; + end end - end - return tag; - end); + return tag; + end); + event.stanza = stanza; + end -- We store chat messages or normal messages that have a body if not(orig_type == "chat" or (orig_type == "normal" and stanza:get_child("body")) ) then @@ -306,9 +321,11 @@ local function message_handler(event, c2s) -- And stash it local ok = archive:append(store_user, nil, stanza, time_now(), with); if ok then + local clone_for_other_handlers = st.clone(stanza); local id = ok; - stanza:tag("stanza-id", { xmlns = xmlns_st_id, by = store_user.."@"..host, id = id }):up(); - if cleanup then cleanup[store_user] = true; end + clone_for_other_handlers:tag("stanza-id", { xmlns = xmlns_st_id, by = store_user.."@"..host, id = id }):up(); + event.stanza = clone_for_other_handlers; + schedule_cleanup(store_user); module:fire_event("archive-message-added", { origin = origin, stanza = stanza, for_user = store_user, id = id }); end else @@ -322,6 +339,7 @@ end local function strip_stanza_id(event) local strip_by = jid_bare(event.origin.full_jid); + event.stanza = st.clone(event.stanza); event.stanza:maptags(function(tag) if not ( tag.attr.xmlns == xmlns_st_id and tag.attr.by == strip_by ) then return tag; @@ -361,13 +379,13 @@ if cleanup_after ~= "never" then pcall(function () -- If this works, then we schedule cleanup for all known users on startup for user in um.users(module.host) do - cleanup[user] = true; + schedule_cleanup(user); end end); -- At odd intervals, delete old messages for one user module:add_timer(math.random(10, 60), function() - local user = next(cleanup); + local user = table.remove(cleanup, 1); if user then module:log("debug", "Removing old messages for user %q", user); local ok, err = archive:delete(user, { ["end"] = os.time() - cleanup_after; }) @@ -383,14 +401,26 @@ if cleanup_after ~= "never" then 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, 0); -module:hook("pre-message/full", c2s_message_handler, 0); +local priority = 0.075 +assert(priority < 0.1, "priority must be after mod_firewall"); +assert(priority > 0.05, "priority must be before mod_carbons"); +assert(priority > 0.01, "priority must be before strip_stanza_id"); +module:hook("pre-message/bare", c2s_message_handler, priority); +module:hook("pre-message/full", c2s_message_handler, priority); -- Stanszas to local clients -module:hook("message/bare", message_handler, 0); -module:hook("message/full", message_handler, 0); +priority = 0.075 +assert(priority > 0, "priority must be before mod_message"); +assert(priority < 0.1, "priority must be after mod_firewall"); +assert(priority > 0.05, "priority must be before mod_carbons"); +module:hook("message/bare", message_handler, priority); +module:hook("message/full", message_handler, priority); module:add_feature(xmlns_mam0); -- COMPAT with XEP-0313 v 0.1 diff --git a/plugins/mod_smacks/mod_smacks.lua b/plugins/mod_smacks/mod_smacks.lua index a01199a..3c7add7 100644 --- a/plugins/mod_smacks/mod_smacks.lua +++ b/plugins/mod_smacks/mod_smacks.lua @@ -5,7 +5,7 @@ -- Copyright (C) 2012-2015 Kim Alvefur -- Copyright (C) 2012 Thijs Alkemade -- Copyright (C) 2014 Florian Zeitz --- Copyright (C) 2016-2017 Thilo Molitor +-- Copyright (C) 2016-2019 Thilo Molitor -- -- This project is MIT/X11 licensed. Please see the -- COPYING file in the source package for more information. @@ -15,9 +15,11 @@ local st = require "util.stanza"; local dep = require "util.dependencies"; local cache = dep.softreq("util.cache"); -- only available in prosody 0.10+ local uuid_generate = require "util.uuid".generate; +local jid = require "util.jid"; local t_insert, t_remove = table.insert, table.remove; local math_min = math.min; +local math_max = math.max; local os_time = os.time; local tonumber, tostring = tonumber, tostring; local add_filter = require "util.filters".add_filter; @@ -42,6 +44,9 @@ local max_old_sessions = module:get_option_number("smacks_max_old_sessions", 10) local core_process_stanza = prosody.core_process_stanza; local sessionmanager = require"core.sessionmanager"; +assert(max_hibernated_sessions > 0, "smacks_max_hibernated_sessions must be greater than 0"); +assert(max_old_sessions > 0, "smacks_old_sessions must be greater than 0"); + local c2s_sessions = module:shared("/*/c2s/sessions"); local function init_session_cache(max_entries, evict_callback) @@ -61,7 +66,7 @@ local function init_session_cache(max_entries, evict_callback) end; }; end - + -- use per user limited cache for prosody >= 0.10 local stores = {}; return { @@ -99,17 +104,19 @@ end); local function stoppable_timer(delay, callback) local stopped = false; + local timer = module:add_timer(delay, function (t) + if stopped then return; end + return callback(t); + end); + if timer and timer.stop then return timer; end -- new prosody api includes stop() function return { stop = function () stopped = true end; - module:add_timer(delay, function (t) - if stopped then return; end - return callback(t); - end); + timer; }; end local function delayed_ack_function(session) - -- fire event only if configured to do so and our session is not hibernated or destroyed + -- fire event only if configured to do so and our session is not already hibernated or destroyed if delayed_ack_timeout > 0 and session.awaiting_ack and not session.hibernating and not session.destroyed then session.log("debug", "Firing event 'smacks-ack-delayed', queue = %d", @@ -150,18 +157,28 @@ module:hook("s2s-stream-features", end end); -local function request_ack_if_needed(session, force) +local function request_ack_if_needed(session, force, reason) local queue = session.outgoing_stanza_queue; - if session.awaiting_ack == nil then - if (#queue > max_unacked_stanzas and session.last_queue_count ~= #queue) or force then - session.log("debug", "Queuing (in a moment)"); + local expected_h = session.last_acknowledged_stanza + #queue; + -- session.log("debug", "*** SMACKS(1) ***: awaiting_ack=%s, hibernating=%s", tostring(session.awaiting_ack), tostring(session.hibernating)); + if session.awaiting_ack == nil and not session.hibernating then + -- this check of last_requested_h prevents ack-loops if missbehaving clients report wrong + -- stanza counts. it is set when an is really sent (e.g. inside timer), preventing any + -- further requests until a higher h-value would be expected. + -- session.log("debug", "*** SMACKS(2) ***: #queue=%s, max_unacked_stanzas=%s, expected_h=%s, last_requested_h=%s", tostring(#queue), tostring(max_unacked_stanzas), tostring(expected_h), tostring(session.last_requested_h)); + if (#queue > max_unacked_stanzas and expected_h ~= session.last_requested_h) or force then + session.log("debug", "Queuing (in a moment) from %s - #queue=%d", reason, #queue); session.awaiting_ack = false; session.awaiting_ack_timer = stoppable_timer(1e-06, function () - if not session.awaiting_ack then - session.log("debug", "Sending (inside timer, before send)"); + -- session.log("debug", "*** SMACKS(3) ***: awaiting_ack=%s, hibernating=%s", tostring(session.awaiting_ack), tostring(session.hibernating)); + -- only request ack if needed and our session is not already hibernated or destroyed + if not session.awaiting_ack and not session.hibernating and not session.destroyed then + session.log("debug", "Sending (inside timer, before send) from %s - #queue=%d", reason, #queue); (session.sends2s or session.send)(st.stanza("r", { xmlns = session.smacks })) - session.log("debug", "Sending (inside timer, after send)"); session.awaiting_ack = true; + -- expected_h could be lower than this expression e.g. more stanzas added to the queue meanwhile) + session.last_requested_h = session.last_acknowledged_stanza + #queue; + session.log("debug", "Sending (inside timer, after send) from %s - #queue=%d", reason, #queue); if not session.delayed_ack_timer then session.delayed_ack_timer = stoppable_timer(delayed_ack_timeout, function() delayed_ack_function(session); @@ -171,7 +188,7 @@ local function request_ack_if_needed(session, force) 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 @@ -180,8 +197,6 @@ local function request_ack_if_needed(session, force) session.log("debug", "Calling delayed_ack_function directly (still waiting for ack)"); delayed_ack_function(session); end - - session.last_queue_count = #queue; end local function outgoing_stanza_filter(stanza, session) @@ -192,16 +207,20 @@ local function outgoing_stanza_filter(stanza, session) cached_stanza._cached = true; if cached_stanza and cached_stanza.name ~= "iq" and cached_stanza:get_child("delay", xmlns_delay) == nil then - cached_stanza = cached_stanza:tag("delay", { xmlns = xmlns_delay, from = session.host, stamp = datetime.datetime()}); + cached_stanza = cached_stanza:tag("delay", { + xmlns = xmlns_delay, + from = jid.bare(session.full_jid or session.host), + stamp = datetime.datetime() + }); end queue[#queue+1] = cached_stanza; - session.log("debug", "#queue = %d", #queue); if session.hibernating then session.log("debug", "hibernating, stanza queued"); + module:fire_event("smacks-hibernation-stanza-queued", {origin = session, queue = queue, stanza = cached_stanza}); return nil; end - request_ack_if_needed(session, false); + request_ack_if_needed(session, false, "outgoing_stanza_filter"); end return stanza; end @@ -231,7 +250,7 @@ local function wrap_session_out(session, resume) end -- send out last ack as per revision 1.5.2 of XEP-0198 if session.smacks and session.conn then - (session.sends2s or session.send)(st.stanza("a", { xmlns = session.smacks, h = tostring(session.handled_stanza_count) })); + (session.sends2s or session.send)(st.stanza("a", { xmlns = session.smacks, h = string.format("%d", session.handled_stanza_count) })); end return session_close(...); end @@ -273,7 +292,7 @@ function handle_enable(session, stanza, xmlns_sm) session_registry.set(session.username, resume_token, session); session.resumption_token = resume_token; end - (session.sends2s or session.send)(st.stanza("enabled", { xmlns = xmlns_sm, id = resume_token, resume = resume })); + (session.sends2s or session.send)(st.stanza("enabled", { xmlns = xmlns_sm, id = resume_token, resume = resume, max = tostring(resume_timeout) })); return true; end module:hook_stanza(xmlns_sm2, "enable", function (session, stanza) return handle_enable(session, stanza, xmlns_sm2); end, 100); @@ -317,7 +336,12 @@ function handle_r(origin, stanza, xmlns_sm) end module:log("debug", "Received ack request, acking for %d", origin.handled_stanza_count); -- Reply with - (origin.sends2s or origin.send)(st.stanza("a", { xmlns = xmlns_sm, h = tostring(origin.handled_stanza_count) })); + (origin.sends2s or origin.send)(st.stanza("a", { xmlns = xmlns_sm, h = string.format("%d", origin.handled_stanza_count) })); + -- piggyback our own ack request if needed (see request_ack_if_needed() for explanation of last_requested_h) + local expected_h = origin.last_acknowledged_stanza + #origin.outgoing_stanza_queue; + if #origin.outgoing_stanza_queue > 0 and expected_h ~= origin.last_requested_h then + request_ack_if_needed(origin, true, "piggybacked by handle_r"); + end return true; end module:hook_stanza(xmlns_sm2, "r", function (origin, stanza) return handle_r(origin, stanza, xmlns_sm2); end); @@ -338,6 +362,7 @@ function handle_a(origin, stanza) local h = tonumber(stanza.attr.h); if not h then origin:close{ condition = "invalid-xml"; text = "Missing or invalid 'h' attribute"; }; + return; end local handled_stanza_count = h-origin.last_acknowledged_stanza; local queue = origin.outgoing_stanza_queue; @@ -349,12 +374,15 @@ function handle_a(origin, stanza) origin.log("debug", "Q item %d: %s", i, tostring(queue[i])); end end + for i=1,math_min(handled_stanza_count,#queue) do - t_remove(origin.outgoing_stanza_queue, 1); + local handled_stanza = t_remove(origin.outgoing_stanza_queue, 1); + module:fire_event("delivery/success", { session = origin, stanza = handled_stanza }); end + origin.log("debug", "#queue = %d", #queue); origin.last_acknowledged_stanza = origin.last_acknowledged_stanza + handled_stanza_count; - request_ack_if_needed(origin, false) + request_ack_if_needed(origin, false, "handle_a") return true; end module:hook_stanza(xmlns_sm2, "a", handle_a); @@ -371,12 +399,14 @@ function handle_unacked_stanzas(session) if #queue > 0 then session.outgoing_stanza_queue = {}; for i=1,#queue do - local reply = st.reply(queue[i]); - if reply.attr.to ~= session.full_jid then - reply.attr.type = "error"; - reply:tag("error", error_attr) - :tag("recipient-unavailable", {xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas"}); - core_process_stanza(session, reply); + if not module:fire_event("delivery/failure", { session = session, stanza = queue[i] }) then + local reply = st.reply(queue[i]); + if reply.attr.to ~= session.full_jid then + reply.attr.type = "error"; + reply:tag("error", error_attr) + :tag("recipient-unavailable", {xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas"}); + core_process_stanza(session, reply); + end end end end @@ -388,7 +418,7 @@ module:hook("pre-resource-unbind", function (event) if not session.resumption_token then local queue = session.outgoing_stanza_queue; if #queue > 0 then - session.log("warn", "Destroying session with %d unacked stanzas", #queue); + session.log("debug", "Destroying session with %d unacked stanzas", #queue); handle_unacked_stanzas(session); end else @@ -410,6 +440,19 @@ module:hook("pre-resource-unbind", function (event) -- Check the hibernate time still matches what we think it is, -- otherwise the session resumed and re-hibernated. and session.hibernating == hibernate_time then + -- wait longer if the timeout isn't reached because push was enabled for this session + -- session.first_hibernated_push is the starting point for hibernation timeouts of those push enabled clients + -- wait for an additional resume_timeout seconds if no push occured since hibernation at all + local current_time = os_time(); + local timeout_start = math_max(session.hibernating, session.first_hibernated_push or session.hibernating); + if session.push_identifier ~= nil and not session.first_hibernated_push then + session.log("debug", "No push happened since hibernation started, hibernating session for up to %d extra seconds", resume_timeout); + return resume_timeout; + end + if current_time-timeout_start < resume_timeout and session.push_identifier ~= nil then + session.log("debug", "A push happened since hibernation started, hibernating session for up to %d extra seconds", current_time-timeout_start); + return current_time-timeout_start; -- time left to wait + end session.log("debug", "Destroying session for hibernating too long"); session_registry.set(session.username, session.resumption_token, nil); -- save only actual h value and username/host (for security) @@ -448,6 +491,10 @@ end module:hook("s2sout-destroyed", handle_s2s_destroyed); module:hook("s2sin-destroyed", handle_s2s_destroyed); +local function get_session_id(session) + return session.id or (tostring(session):match("[a-f0-9]+$")); +end + function handle_resume(session, stanza, xmlns_sm) if session.full_jid then session.log("warn", "Tried to resume after resource binding"); @@ -465,7 +512,7 @@ function handle_resume(session, stanza, xmlns_sm) if old_session and session.username == old_session.username and session.host == old_session.host and old_session.h then - session.send(st.stanza("failed", { xmlns = xmlns_sm, h = tostring(old_session.h) }) + session.send(st.stanza("failed", { xmlns = xmlns_sm, h = string.format("%d", old_session.h) }) :tag("item-not-found", { xmlns = xmlns_errors }) ); else @@ -475,10 +522,11 @@ function handle_resume(session, stanza, xmlns_sm) end; elseif session.username == original_session.username and session.host == original_session.host then - session.log("debug", "mod_smacks resuming existing session..."); + session.log("debug", "mod_smacks resuming existing session %s...", get_session_id(original_session)); + original_session.log("debug", "mod_smacks session resumed from %s...", get_session_id(session)); -- TODO: All this should move to sessionmanager (e.g. session:replace(new_session)) if original_session.conn then - session.log("debug", "mod_smacks closing an old connection for this session"); + original_session.log("debug", "mod_smacks closing an old connection for this session"); local conn = original_session.conn; c2s_sessions[conn] = nil; conn:close(); @@ -493,6 +541,8 @@ function handle_resume(session, stanza, xmlns_sm) original_session.stream = session.stream; original_session.secure = session.secure; original_session.hibernating = nil; + session.log = original_session.log; + session.type = original_session.type; wrap_session(original_session, true); -- Inform xmppstream of the new session (passed to its callbacks) original_session.stream:set_session(original_session); @@ -500,7 +550,7 @@ function handle_resume(session, stanza, xmlns_sm) c2s_sessions[session.conn] = original_session; original_session.send(st.stanza("resumed", { xmlns = xmlns_sm, - h = original_session.handled_stanza_count, previd = id })); + h = string.format("%d", original_session.handled_stanza_count), previd = id })); -- Fake an with the h of the from the client original_session:dispatch_stanza(st.stanza("a", { xmlns = xmlns_sm, @@ -509,7 +559,6 @@ function handle_resume(session, stanza, xmlns_sm) -- 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]); @@ -519,7 +568,8 @@ function handle_resume(session, stanza, xmlns_sm) 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 - request_ack_if_needed(original_session, true); + module:fire_event("smacks-hibernation-end", {origin = session, resumed = original_session, queue = queue}); + request_ack_if_needed(original_session, true, "handle_resume"); else module:log("warn", "Client %s@%s[%s] tried to resume stream for %s@%s[%s]", session.username or "?", session.host or "?", session.type, @@ -546,7 +596,6 @@ local function handle_read_timeout(event) return false; -- Kick the session end session.log("debug", "Sending (read timeout)"); - session.awaiting_ack = false; (session.sends2s or session.send)(st.stanza("r", { xmlns = session.smacks })); session.awaiting_ack = true; if not session.delayed_ack_timer then diff --git a/prosody-modules.revision b/prosody-modules.revision index 07aec10..35a22f9 100644 --- a/prosody-modules.revision +++ b/prosody-modules.revision @@ -1 +1 @@ -9b43b7fc3558 +0ae28bf0c546