Mercurial > prosody-modules
comparison mod_cloud_notify/mod_cloud_notify.lua @ 2625:8c6562f16496
mod_cloud_notify: Fixed error in push deduplication
Make handling of node ids and push_identifiers more error resilient.
Also add some better debug output.
| author | tmolitor <thilo@eightysoft.de> |
|---|---|
| date | Sat, 18 Mar 2017 00:20:04 +0100 |
| parents | 6ab46ff685d0 |
| children | 0f1421af7f6a |
comparison
equal
deleted
inserted
replaced
| 2624:c110b6bfe5d1 | 2625:8c6562f16496 |
|---|---|
| 5 -- This file is MIT/X11 licensed. | 5 -- This file is MIT/X11 licensed. |
| 6 | 6 |
| 7 local st = require"util.stanza"; | 7 local st = require"util.stanza"; |
| 8 local jid = require"util.jid"; | 8 local jid = require"util.jid"; |
| 9 local dataform = require"util.dataforms".new; | 9 local dataform = require"util.dataforms".new; |
| 10 local filters = require "util.filters"; | 10 local filters = require"util.filters"; |
| 11 local hashes = require"util.hashes"; | |
| 11 | 12 |
| 12 local xmlns_push = "urn:xmpp:push:0"; | 13 local xmlns_push = "urn:xmpp:push:0"; |
| 13 | 14 |
| 14 -- configuration | 15 -- configuration |
| 15 local include_body = module:get_option_boolean("push_notification_with_body", false); | 16 local include_body = module:get_option_boolean("push_notification_with_body", false); |
| 55 end)(); | 56 end)(); |
| 56 | 57 |
| 57 local function handle_push_error(event) | 58 local function handle_push_error(event) |
| 58 local stanza = event.stanza; | 59 local stanza = event.stanza; |
| 59 local error_type, condition = stanza:get_error(); | 60 local error_type, condition = stanza:get_error(); |
| 60 local push_identifier = stanza.attr.id; | |
| 61 local node = jid.split(stanza.attr.to); | 61 local node = jid.split(stanza.attr.to); |
| 62 local from = stanza.attr.from; | 62 local from = stanza.attr.from; |
| 63 local user_push_services = push_store:get(node); | 63 local user_push_services = push_store:get(node); |
| 64 | 64 |
| 65 if user_push_services[push_identifier] and user_push_services[push_identifier].jid == from and error_type ~= "wait" then | 65 for push_identifier, _ in pairs(user_push_services) do |
| 66 push_errors[push_identifier] = push_errors[push_identifier] + 1; | 66 if hashes.sha256(push_identifier, true) == stanza.attr.id then |
| 67 module:log("info", "Got error of type '%s' (%s) for identifier '%s':" | 67 if user_push_services[push_identifier] and user_push_services[push_identifier].jid == from and error_type ~= "wait" then |
| 68 .."error count for this identifier is now at %s", error_type, condition, push_identifier, | 68 push_errors[push_identifier] = push_errors[push_identifier] + 1; |
| 69 tostring(push_errors[push_identifier])); | 69 module:log("info", "Got error of type '%s' (%s) for identifier '%s': " |
| 70 if push_errors[push_identifier] >= max_push_errors then | 70 .."error count for this identifier is now at %s", error_type, condition, push_identifier, |
| 71 module:log("warn", "Disabling push notifications for identifier '%s'", push_identifier); | 71 tostring(push_errors[push_identifier])); |
| 72 -- remove push settings from sessions | 72 if push_errors[push_identifier] >= max_push_errors then |
| 73 for _, session in pairs(host_sessions[node].sessions) do | 73 module:log("warn", "Disabling push notifications for identifier '%s'", push_identifier); |
| 74 if session.push_identifier == push_identifier then | 74 -- remove push settings from sessions |
| 75 session.push_identifier = nil; | 75 for _, session in pairs(host_sessions[node].sessions) do |
| 76 session.push_settings = nil; | 76 if session.push_identifier == push_identifier then |
| 77 session.push_identifier = nil; | |
| 78 session.push_settings = nil; | |
| 79 end | |
| 80 end | |
| 81 -- save changed global config | |
| 82 push_store:set_identifier(node, push_identifier, nil); | |
| 83 push_errors[push_identifier] = nil; | |
| 84 -- unhook iq handlers for this identifier | |
| 85 module:unhook("iq-error/bare/"..hashes.sha256(push_identifier, true), handle_push_error); | |
| 86 module:unhook("iq-result/bare/"..hashes.sha256(push_identifier, true), handle_push_success); | |
| 77 end | 87 end |
| 78 end | 88 elseif user_push_services[push_identifier] and user_push_services[push_identifier].jid == from and error_type == "wait" then |
| 79 -- save changed global config | 89 module:log("debug", "Got error of type '%s' (%s) for identifier '%s': " |
| 80 push_store:set_identifier(node, push_identifier, nil); | 90 .."NOT increasing error count for this identifier", error_type, condition, push_identifier); |
| 81 push_errors[push_identifier] = nil; | 91 end |
| 82 -- unhook iq handlers for this identifier | |
| 83 module:unhook("iq-error/bare/"..push_identifier, handle_push_error); | |
| 84 module:unhook("iq-result/bare/"..push_identifier, handle_push_success); | |
| 85 end | 92 end |
| 86 end | 93 end |
| 87 return true; | 94 return true; |
| 88 end | 95 end |
| 89 | 96 |
| 90 local function handle_push_success(event) | 97 local function handle_push_success(event) |
| 91 local stanza = event.stanza; | 98 local stanza = event.stanza; |
| 92 local push_identifier = stanza.attr.id; | |
| 93 local node = jid.split(stanza.attr.to); | 99 local node = jid.split(stanza.attr.to); |
| 94 local from = stanza.attr.from; | 100 local from = stanza.attr.from; |
| 95 local user_push_services = push_store:get(node); | 101 local user_push_services = push_store:get(node); |
| 96 | 102 |
| 97 if user_push_services[push_identifier] and user_push_services[push_identifier].jid == from and push_errors[push_identifier] then | 103 for push_identifier, _ in pairs(user_push_services) do |
| 98 push_errors[push_identifier] = 0; | 104 if hashes.sha256(push_identifier, true) == stanza.attr.id then |
| 99 module:log("debug", "Push succeeded, error count for identifier '%s' is now at %s", push_identifier, tostring(push_errors[push_identifier])); | 105 if user_push_services[push_identifier] and user_push_services[push_identifier].jid == from and push_errors[push_identifier] then |
| 106 push_errors[push_identifier] = 0; | |
| 107 module:log("debug", "Push succeeded, error count for identifier '%s' is now at %s", push_identifier, tostring(push_errors[push_identifier])); | |
| 108 end | |
| 109 end | |
| 100 end | 110 end |
| 101 return true; | 111 return true; |
| 102 end | 112 end |
| 103 | 113 |
| 104 -- http://xmpp.org/extensions/xep-0357.html#disco | 114 -- http://xmpp.org/extensions/xep-0357.html#disco |
| 137 if not ok then | 147 if not ok then |
| 138 origin.send(st.error_reply(stanza, "wait", "internal-server-error")); | 148 origin.send(st.error_reply(stanza, "wait", "internal-server-error")); |
| 139 else | 149 else |
| 140 origin.push_identifier = push_identifier; | 150 origin.push_identifier = push_identifier; |
| 141 origin.push_settings = push_service; | 151 origin.push_settings = push_service; |
| 142 origin.log("info", "Push notifications enabled (%s)", tostring(origin.push_identifier)); | 152 origin.log("info", "Push notifications enabled for %s (%s)", tostring(stanza.attr.from), tostring(origin.push_identifier)); |
| 143 origin.send(st.reply(stanza)); | 153 origin.send(st.reply(stanza)); |
| 144 end | 154 end |
| 145 return true; | 155 return true; |
| 146 end | 156 end |
| 147 module:hook("iq-set/self/"..xmlns_push..":enable", push_enable); | 157 module:hook("iq-set/self/"..xmlns_push..":enable", push_enable); |
| 189 | 199 |
| 190 -- http://xmpp.org/extensions/xep-0357.html#publishing | 200 -- http://xmpp.org/extensions/xep-0357.html#publishing |
| 191 local function handle_notify_request(stanza, node, user_push_services) | 201 local function handle_notify_request(stanza, node, user_push_services) |
| 192 if not user_push_services or not #user_push_services then return end | 202 if not user_push_services or not #user_push_services then return end |
| 193 | 203 |
| 194 if stanza and stanza._notify then | |
| 195 module:log("debug", "Already sent push notification to %s@%s for this stanza, not doing it again", node, module.host); | |
| 196 return; | |
| 197 end | |
| 198 if stanza then | |
| 199 stanza._notify = true; | |
| 200 end | |
| 201 | |
| 202 for push_identifier, push_info in pairs(user_push_services) do | 204 for push_identifier, push_info in pairs(user_push_services) do |
| 205 if stanza then | |
| 206 if not stanza._push_notify then stanza._push_notify = {}; end | |
| 207 if stanza._push_notify[push_identifier] then | |
| 208 module:log("debug", "Already sent push notification for %s@%s to %s (%s)", node, module.host, push_info.jid, tostring(push_info.node)); | |
| 209 return; | |
| 210 end | |
| 211 stanza._push_notify[push_identifier] = true; | |
| 212 end | |
| 213 | |
| 203 -- increment count and save it | 214 -- increment count and save it |
| 204 push_info.count = push_info.count + 1; | 215 push_info.count = push_info.count + 1; |
| 205 push_store:set_identifier(node, push_identifier, push_info); | 216 push_store:set_identifier(node, push_identifier, push_info); |
| 206 -- construct push stanza | 217 -- construct push stanza |
| 207 local push_publish = st.iq({ to = push_info.jid, from = node .. "@" .. module.host, type = "set", id = push_identifier }) | 218 local push_publish = st.iq({ to = push_info.jid, from = node .. "@" .. module.host, type = "set", id = hashes.sha256(push_identifier, true) }) |
| 208 :tag("pubsub", { xmlns = "http://jabber.org/protocol/pubsub" }) | 219 :tag("pubsub", { xmlns = "http://jabber.org/protocol/pubsub" }) |
| 209 :tag("publish", { node = push_info.node }) | 220 :tag("publish", { node = push_info.node }) |
| 210 :tag("item") | 221 :tag("item") |
| 211 :tag("notification", { xmlns = xmlns_push }); | 222 :tag("notification", { xmlns = xmlns_push }); |
| 212 local form_data = { | 223 local form_data = { |
| 228 -- send out push | 239 -- send out push |
| 229 module:log("debug", "Sending push notification for %s@%s to %s (%s)", node, module.host, push_info.jid, tostring(push_info.node)); | 240 module:log("debug", "Sending push notification for %s@%s to %s (%s)", node, module.host, push_info.jid, tostring(push_info.node)); |
| 230 -- handle push errors for this node | 241 -- handle push errors for this node |
| 231 if push_errors[push_identifier] == nil then | 242 if push_errors[push_identifier] == nil then |
| 232 push_errors[push_identifier] = 0; | 243 push_errors[push_identifier] = 0; |
| 233 module:hook("iq-error/bare/"..push_identifier, handle_push_error); | 244 module:hook("iq-error/bare/"..hashes.sha256(push_identifier, true), handle_push_error); |
| 234 module:hook("iq-result/bare/"..push_identifier, handle_push_success); | 245 module:hook("iq-result/bare/"..hashes.sha256(push_identifier, true), handle_push_success); |
| 235 end | 246 end |
| 236 module:send(push_publish); | 247 module:send(push_publish); |
| 237 end | 248 end |
| 238 end | 249 end |
| 239 | 250 |
| 252 end, 1); | 263 end, 1); |
| 253 | 264 |
| 254 -- publish on unacked smacks message | 265 -- publish on unacked smacks message |
| 255 local function process_smacks_stanza(stanza, session) | 266 local function process_smacks_stanza(stanza, session) |
| 256 if session.push_identifier then | 267 if session.push_identifier then |
| 257 session.log("debug", "Invoking cloud handle_notify_request for smacks queued stanza..."); | 268 session.log("debug", "Invoking cloud handle_notify_request for smacks queued stanza"); |
| 258 local user_push_services = {[session.push_identifier] = session.push_settings}; | 269 local user_push_services = {[session.push_identifier] = session.push_settings}; |
| 259 local node = get_push_settings(stanza, session); | 270 local node = get_push_settings(stanza, session); |
| 260 handle_notify_request(stanza, node, user_push_services); | 271 handle_notify_request(stanza, node, user_push_services); |
| 261 end | 272 end |
| 262 return stanza; | 273 return stanza; |
| 361 -- end | 372 -- end |
| 362 -- end | 373 -- end |
| 363 -- push_store:set(origin.username, user_push_services); | 374 -- push_store:set(origin.username, user_push_services); |
| 364 -- end, 1); | 375 -- end, 1); |
| 365 | 376 |
| 377 module:log("info", "Module loaded"); | |
| 366 function module.unload() | 378 function module.unload() |
| 367 module:unhook("account-disco-info", account_dico_info); | 379 module:unhook("account-disco-info", account_dico_info); |
| 368 module:unhook("iq-set/self/"..xmlns_push..":enable", push_enable); | 380 module:unhook("iq-set/self/"..xmlns_push..":enable", push_enable); |
| 369 module:unhook("iq-set/self/"..xmlns_push..":disable", push_disable); | 381 module:unhook("iq-set/self/"..xmlns_push..":disable", push_disable); |
| 370 | 382 |
| 373 module:unhook("smacks-ack-delayed", ack_delayed); | 385 module:unhook("smacks-ack-delayed", ack_delayed); |
| 374 module:unhook("archive-message-added", archive_message_added); | 386 module:unhook("archive-message-added", archive_message_added); |
| 375 module:unhook("cloud-notify-ping", send_ping); | 387 module:unhook("cloud-notify-ping", send_ping); |
| 376 | 388 |
| 377 for push_identifier, _ in pairs(push_errors) do | 389 for push_identifier, _ in pairs(push_errors) do |
| 378 module:hook("iq-error/bare/"..push_identifier, handle_push_error); | 390 module:hook("iq-error/bare/"..hashes.sha256(push_identifier, true), handle_push_error); |
| 379 module:hook("iq-result/bare/"..push_identifier, handle_push_success); | 391 module:hook("iq-result/bare/"..hashes.sha256(push_identifier, true), handle_push_success); |
| 380 end | 392 end |
| 381 end | 393 |
| 394 module:log("info", "Module unloaded"); | |
| 395 end |
