Software /
code /
prosody-modules
Comparison
mod_cloud_notify/mod_cloud_notify.lua @ 2736:fff185e7ab73
mod_cloud_notify: Implement the "stripped stanzas" proposal.
See https://mail.jabber.org/pipermail/standards/2017-July/033089.html
author | tmolitor <thilo@eightysoft.de> |
---|---|
date | Mon, 14 Aug 2017 23:31:05 +0200 |
parent | 2714:75b137cf869a |
child | 2749:9756211fcbe3 |
comparison
equal
deleted
inserted
replaced
2735:b5fae17e4403 | 2736:fff185e7ab73 |
---|---|
2 -- Copyright (C) 2015-2016 Kim Alvefur | 2 -- Copyright (C) 2015-2016 Kim Alvefur |
3 -- Copyright (C) 2017 Thilo Molitor | 3 -- Copyright (C) 2017 Thilo Molitor |
4 -- | 4 -- |
5 -- This file is MIT/X11 licensed. | 5 -- This file is MIT/X11 licensed. |
6 | 6 |
7 local t_insert = table.insert; | |
7 local st = require"util.stanza"; | 8 local st = require"util.stanza"; |
8 local jid = require"util.jid"; | 9 local jid = require"util.jid"; |
9 local dataform = require"util.dataforms".new; | 10 local dataform = require"util.dataforms".new; |
10 local filters = require"util.filters"; | 11 local filters = require"util.filters"; |
11 local hashes = require"util.hashes"; | 12 local hashes = require"util.hashes"; |
108 | 109 |
109 for push_identifier, _ in pairs(user_push_services) do | 110 for push_identifier, _ in pairs(user_push_services) do |
110 if hashes.sha256(push_identifier, true) == stanza.attr.id then | 111 if hashes.sha256(push_identifier, true) == stanza.attr.id then |
111 if user_push_services[push_identifier] and user_push_services[push_identifier].jid == from and push_errors[push_identifier] > 0 then | 112 if user_push_services[push_identifier] and user_push_services[push_identifier].jid == from and push_errors[push_identifier] > 0 then |
112 push_errors[push_identifier] = 0; | 113 push_errors[push_identifier] = 0; |
113 module:log("debug", "Push succeeded, error count for identifier '%s' is now at %s", push_identifier, tostring(push_errors[push_identifier])); | 114 module:log("debug", "Push succeeded, error count for identifier '%s' is now at %s again", push_identifier, tostring(push_errors[push_identifier])); |
114 end | 115 end |
115 end | 116 end |
116 end | 117 end |
117 return true; | 118 return true; |
118 end | 119 end |
130 origin.log("debug", "Attempting to enable push notifications"); | 131 origin.log("debug", "Attempting to enable push notifications"); |
131 -- MUST contain a 'jid' attribute of the XMPP Push Service being enabled | 132 -- MUST contain a 'jid' attribute of the XMPP Push Service being enabled |
132 local push_jid = enable.attr.jid; | 133 local push_jid = enable.attr.jid; |
133 -- SHOULD contain a 'node' attribute | 134 -- SHOULD contain a 'node' attribute |
134 local push_node = enable.attr.node; | 135 local push_node = enable.attr.node; |
136 -- CAN contain a 'include_payload' attribute | |
137 local include_payload = enable.attr.include_payload; | |
135 if not push_jid then | 138 if not push_jid then |
136 origin.log("debug", "Push notification enable request missing the 'jid' field"); | 139 origin.log("debug", "Push notification enable request missing the 'jid' field"); |
137 origin.send(st.error_reply(stanza, "modify", "bad-request", "Missing jid")); | 140 origin.send(st.error_reply(stanza, "modify", "bad-request", "Missing jid")); |
138 return true; | 141 return true; |
139 end | 142 end |
144 end | 147 end |
145 local push_identifier = push_jid .. "<" .. (push_node or ""); | 148 local push_identifier = push_jid .. "<" .. (push_node or ""); |
146 local push_service = { | 149 local push_service = { |
147 jid = push_jid; | 150 jid = push_jid; |
148 node = push_node; | 151 node = push_node; |
152 include_payload = include_payload; | |
149 count = 0; | 153 count = 0; |
150 options = publish_options and st.preserialize(publish_options); | 154 options = publish_options and st.preserialize(publish_options); |
151 }; | 155 }; |
152 local ok = push_store:set_identifier(origin.username, push_identifier, push_service); | 156 local ok = push_store:set_identifier(origin.username, push_identifier, push_service); |
153 if not ok then | 157 if not ok then |
194 origin.send(st.reply(stanza)); | 198 origin.send(st.reply(stanza)); |
195 end | 199 end |
196 return true; | 200 return true; |
197 end | 201 end |
198 module:hook("iq-set/self/"..xmlns_push..":disable", push_disable); | 202 module:hook("iq-set/self/"..xmlns_push..":disable", push_disable); |
203 | |
204 -- clone a stanza and strip it | |
205 local function strip_stanza(stanza) | |
206 local tags = {}; | |
207 local new = { name = stanza.name, attr = { xmlns = stanza.attr.xmlns, type = stanza.attr.type }, tags = tags }; | |
208 for i=1,#stanza do | |
209 local child = stanza[i]; | |
210 if type(child) == "table" then -- don't add raw text nodes | |
211 if child.name then | |
212 child = strip_stanza(child); | |
213 t_insert(tags, child); | |
214 end | |
215 t_insert(new, child); | |
216 end | |
217 end | |
218 return setmetatable(new, st.stanza_mt); | |
219 end | |
199 | 220 |
200 local push_form = dataform { | 221 local push_form = dataform { |
201 { name = "FORM_TYPE"; type = "hidden"; value = "urn:xmpp:push:summary"; }; | 222 { name = "FORM_TYPE"; type = "hidden"; value = "urn:xmpp:push:summary"; }; |
202 { name = "message-count"; type = "text-single"; }; | 223 { name = "message-count"; type = "text-single"; }; |
203 { name = "pending-subscription-count"; type = "text-single"; }; | 224 { name = "pending-subscription-count"; type = "text-single"; }; |
240 end | 261 end |
241 if stanza and include_body then | 262 if stanza and include_body then |
242 form_data["last-message-body"] = stanza:get_child_text("body"); | 263 form_data["last-message-body"] = stanza:get_child_text("body"); |
243 end | 264 end |
244 push_publish:add_child(push_form:form(form_data)); | 265 push_publish:add_child(push_form:form(form_data)); |
266 if stanza and push_info.include_payload == "stripped" then | |
267 push_publish:tag("payload", { type = "stripped" }) | |
268 :add_child(strip_stanza(stanza)); | |
269 push_publish:up(); -- / payload | |
270 end | |
271 if stanza and push_info.include_payload == "full" then | |
272 push_publish:tag("payload", { type = "full" }) | |
273 :add_child(st.clone(stanza)); | |
274 push_publish:up(); -- / payload | |
275 end | |
245 push_publish:up(); -- / notification | 276 push_publish:up(); -- / notification |
246 push_publish:up(); -- / publish | 277 push_publish:up(); -- / publish |
247 push_publish:up(); -- / pubsub | 278 push_publish:up(); -- / pubsub |
248 if push_info.options then | 279 if push_info.options then |
249 push_publish:tag("publish-options"):add_child(st.deserialize(push_info.options)); | 280 push_publish:tag("publish-options"):add_child(st.deserialize(push_info.options)); |
250 end | 281 end |
251 -- send out push | 282 -- send out push |
252 module:log("debug", "Sending push notification for %s@%s to %s (%s)", node, module.host, push_info.jid, tostring(push_info.node)); | 283 module:log("debug", "Sending push notification for %s@%s to %s (%s)", node, module.host, push_info.jid, tostring(push_info.node)); |
284 -- module:log("debug", "PUSH STANZA: %s", tostring(push_publish)); | |
253 -- handle push errors for this node | 285 -- handle push errors for this node |
254 if push_errors[push_identifier] == nil then | 286 if push_errors[push_identifier] == nil then |
255 push_errors[push_identifier] = 0; | 287 push_errors[push_identifier] = 0; |
256 module:hook("iq-error/bare/"..stanza_id, handle_push_error); | 288 module:hook("iq-error/bare/"..stanza_id, handle_push_error); |
257 module:hook("iq-result/bare/"..stanza_id, handle_push_success); | 289 module:hook("iq-result/bare/"..stanza_id, handle_push_success); |
272 end | 304 end |
273 | 305 |
274 -- publish on offline message | 306 -- publish on offline message |
275 module:hook("message/offline/handle", function(event) | 307 module:hook("message/offline/handle", function(event) |
276 local node, user_push_services = get_push_settings(event.stanza, event.origin); | 308 local node, user_push_services = get_push_settings(event.stanza, event.origin); |
309 module:log("debug", "Invoking cloud handle_notify_request() for offline stanza"); | |
277 handle_notify_request(event.stanza, node, user_push_services); | 310 handle_notify_request(event.stanza, node, user_push_services); |
278 end, 1); | 311 end, 1); |
279 | 312 |
280 -- publish on unacked smacks message | 313 -- publish on unacked smacks message |
281 local function process_smacks_stanza(stanza, session) | 314 local function process_smacks_stanza(stanza, session) |
307 local session = event.origin; | 340 local session = event.origin; |
308 local queue = event.queue; | 341 local queue = event.queue; |
309 -- process unacked stanzas | 342 -- process unacked stanzas |
310 process_smacks_queue(queue, session); | 343 process_smacks_queue(queue, session); |
311 -- process future unacked (hibernated) stanzas | 344 -- process future unacked (hibernated) stanzas |
312 filters.add_filter(session, "stanzas/out", process_smacks_stanza); | 345 filters.add_filter(session, "stanzas/out", process_smacks_stanza, -990); |
313 end | 346 end |
314 | 347 |
315 -- smacks hibernation is ended | 348 -- smacks hibernation is ended |
316 local function restore_session(event) | 349 local function restore_session(event) |
317 local session = event.resumed; | 350 local session = event.resumed; |
382 handle_notify_request(nil, user, push_services); | 415 handle_notify_request(nil, user, push_services); |
383 end | 416 end |
384 -- can be used by other modules to ping one or more (or all) push endpoints | 417 -- can be used by other modules to ping one or more (or all) push endpoints |
385 module:hook("cloud-notify-ping", send_ping); | 418 module:hook("cloud-notify-ping", send_ping); |
386 | 419 |
387 -- TODO: this has to be done on first connect not on offline broadcast, else the counter will be incorrect | |
388 -- TODO: it seems this is already done, so this could be safely removed, couldn't it? | |
389 -- module:hook("message/offline/broadcast", function(event) | |
390 -- local origin = event.origin; | |
391 -- local user_push_services = push_store:get(origin.username); | |
392 -- if not #user_push_services then return end | |
393 -- | |
394 -- for _, push_info in pairs(user_push_services) do | |
395 -- if push_info then | |
396 -- push_info.count = 0; | |
397 -- end | |
398 -- end | |
399 -- push_store:set(origin.username, user_push_services); | |
400 -- end, 1); | |
401 | |
402 module:log("info", "Module loaded"); | 420 module:log("info", "Module loaded"); |
403 function module.unload() | 421 function module.unload() |
404 if module.unhook then | 422 if module.unhook then |
405 module:unhook("account-disco-info", account_dico_info); | 423 module:unhook("account-disco-info", account_dico_info); |
406 module:unhook("iq-set/self/"..xmlns_push..":enable", push_enable); | 424 module:unhook("iq-set/self/"..xmlns_push..":enable", push_enable); |