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
parents 75b137cf869a
children 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);