comparison mod_cloud_notify/mod_cloud_notify.lua @ 3108:cfcb020bcd1d

mod_cloud_notify: inform mod_smacks of first push in hibernated state
author tmolitor <thilo@eightysoft.de>
date Fri, 08 Jun 2018 17:39:07 +0200
parents 1ea6861b533f
children 74aa35aeb08a
comparison
equal deleted inserted replaced
3107:f703cc6e72df 3108:cfcb020bcd1d
125 services[push_identifier] = data; 125 services[push_identifier] = data;
126 return self:set(user, services); 126 return self:set(user, services);
127 end 127 end
128 return api; 128 return api;
129 end)(); 129 end)();
130
130 131
131 -- Forward declarations, as both functions need to reference each other 132 -- Forward declarations, as both functions need to reference each other
132 local handle_push_success, handle_push_error; 133 local handle_push_success, handle_push_error;
133 134
134 function handle_push_error(event) 135 function handle_push_error(event)
154 if host_sessions[node] then 155 if host_sessions[node] then
155 for _, session in pairs(host_sessions[node].sessions) do 156 for _, session in pairs(host_sessions[node].sessions) do
156 if session.push_identifier == push_identifier then 157 if session.push_identifier == push_identifier then
157 session.push_identifier = nil; 158 session.push_identifier = nil;
158 session.push_settings = nil; 159 session.push_settings = nil;
160 session.first_hibernated_push = nil;
159 end 161 end
160 end 162 end
161 end 163 end
162 -- save changed global config 164 -- save changed global config
163 changed = true; 165 changed = true;
239 if not ok then 241 if not ok then
240 origin.send(st.error_reply(stanza, "wait", "internal-server-error")); 242 origin.send(st.error_reply(stanza, "wait", "internal-server-error"));
241 else 243 else
242 origin.push_identifier = push_identifier; 244 origin.push_identifier = push_identifier;
243 origin.push_settings = push_service; 245 origin.push_settings = push_service;
246 origin.first_hibernated_push = nil;
244 origin.log("info", "Push notifications enabled for %s (%s)", tostring(stanza.attr.from), tostring(origin.push_identifier)); 247 origin.log("info", "Push notifications enabled for %s (%s)", tostring(stanza.attr.from), tostring(origin.push_identifier));
245 origin.send(st.reply(stanza)); 248 origin.send(st.reply(stanza));
246 end 249 end
247 return true; 250 return true;
248 end 251 end
262 if push_info.jid == push_jid and (not push_node or push_info.node == push_node) then 265 if push_info.jid == push_jid and (not push_node or push_info.node == push_node) then
263 origin.log("info", "Push notifications disabled (%s)", tostring(key)); 266 origin.log("info", "Push notifications disabled (%s)", tostring(key));
264 if origin.push_identifier == key then 267 if origin.push_identifier == key then
265 origin.push_identifier = nil; 268 origin.push_identifier = nil;
266 origin.push_settings = nil; 269 origin.push_settings = nil;
270 origin.first_hibernated_push = nil;
267 end 271 end
268 user_push_services[key] = nil; 272 user_push_services[key] = nil;
269 push_errors[key] = nil; 273 push_errors[key] = nil;
270 if module.unhook then 274 if module.unhook then
271 module:unhook("iq-error/host/"..key, handle_push_error); 275 module:unhook("iq-error/host/"..key, handle_push_error);
362 { name = "last-message-sender"; type = "jid-single"; }; 366 { name = "last-message-sender"; type = "jid-single"; };
363 { name = "last-message-body"; type = "text-single"; }; 367 { name = "last-message-body"; type = "text-single"; };
364 }; 368 };
365 369
366 -- http://xmpp.org/extensions/xep-0357.html#publishing 370 -- http://xmpp.org/extensions/xep-0357.html#publishing
367 local function handle_notify_request(stanza, node, user_push_services) 371 local function handle_notify_request(stanza, node, user_push_services, log_push_decline)
368 local pushes = 0; 372 local pushes = 0;
369 if not user_push_services or next(user_push_services) == nil then return pushes end 373 if not user_push_services or next(user_push_services) == nil then return pushes end
370 374
371 for push_identifier, push_info in pairs(user_push_services) do 375 for push_identifier, push_info in pairs(user_push_services) do
372 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 376 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
373 if stanza then 377 if stanza then
374 if not stanza._push_notify then stanza._push_notify = {}; end 378 if not stanza._push_notify then stanza._push_notify = {}; end
375 if stanza._push_notify[push_identifier] then 379 if stanza._push_notify[push_identifier] then
376 module:log("debug", "Already sent push notification for %s@%s to %s (%s)", node, module.host, push_info.jid, tostring(push_info.node)); 380 if log_push_decline then
381 module:log("debug", "Already sent push notification for %s@%s to %s (%s)", node, module.host, push_info.jid, tostring(push_info.node));
382 end
377 send_push = false; 383 send_push = false;
378 end 384 end
379 stanza._push_notify[push_identifier] = true; 385 stanza._push_notify[push_identifier] = true;
380 end 386 end
381 387
394 if stanza and include_sender then 400 if stanza and include_sender then
395 form_data["last-message-sender"] = stanza.attr.from; 401 form_data["last-message-sender"] = stanza.attr.from;
396 end 402 end
397 if stanza and include_body then 403 if stanza and include_body then
398 form_data["last-message-body"] = stanza:get_child_text("body"); 404 form_data["last-message-body"] = stanza:get_child_text("body");
399 elseif stanza and dummy_body ~= "" and is_important(stanza) then 405 elseif stanza and dummy_body and is_important(stanza) then
400 form_data["last-message-body"] = dummy_body; 406 form_data["last-message-body"] = tostring(dummy_body);
401 end 407 end
402 push_publish:add_child(push_form:form(form_data)); 408 push_publish:add_child(push_form:form(form_data));
403 push_publish:up(); -- / notification 409 push_publish:up(); -- / notification
404 push_publish:up(); -- / publish 410 push_publish:up(); -- / publish
405 push_publish:up(); -- / pubsub 411 push_publish:up(); -- / pubsub
406 if push_info.options then 412 if push_info.options then
407 push_publish:tag("publish-options"):add_child(st.deserialize(push_info.options)); 413 push_publish:tag("publish-options"):add_child(st.deserialize(push_info.options));
408 end 414 end
409 -- send out push 415 -- send out push
410 module:log("debug", "Sending push notification for %s@%s to %s (%s)", node, module.host, push_info.jid, tostring(push_info.node)); 416 module:log("debug", "Sending%s push notification for %s@%s to %s (%s)", form_data["last-message-body"] and " important" or "", node, module.host, push_info.jid, tostring(push_info.node));
411 -- module:log("debug", "PUSH STANZA: %s", tostring(push_publish)); 417 -- module:log("debug", "PUSH STANZA: %s", tostring(push_publish));
412 -- handle push errors for this node 418 -- handle push errors for this node
413 if push_errors[push_identifier] == nil then 419 if push_errors[push_identifier] == nil then
414 push_errors[push_identifier] = 0; 420 push_errors[push_identifier] = 0;
415 module:hook("iq-error/host/"..stanza_id, handle_push_error); 421 module:hook("iq-error/host/"..stanza_id, handle_push_error);
433 439
434 -- publish on offline message 440 -- publish on offline message
435 module:hook("message/offline/handle", function(event) 441 module:hook("message/offline/handle", function(event)
436 local node, user_push_services = get_push_settings(event.stanza, event.origin); 442 local node, user_push_services = get_push_settings(event.stanza, event.origin);
437 module:log("debug", "Invoking cloud handle_notify_request() for offline stanza"); 443 module:log("debug", "Invoking cloud handle_notify_request() for offline stanza");
438 handle_notify_request(event.stanza, node, user_push_services); 444 handle_notify_request(event.stanza, node, user_push_services, true);
439 end, 1); 445 end, 1);
440 446
441 -- publish on unacked smacks message 447 -- publish on unacked smacks message
442 local function process_smacks_stanza(stanza, session) 448 local function process_smacks_stanza(stanza, session)
443 if session.push_identifier then 449 if session.push_identifier then
444 session.log("debug", "Invoking cloud handle_notify_request() for smacks queued stanza"); 450 session.log("debug", "Invoking cloud handle_notify_request() for smacks queued stanza");
445 local user_push_services = {[session.push_identifier] = session.push_settings}; 451 local user_push_services = {[session.push_identifier] = session.push_settings};
446 local node = get_push_settings(stanza, session); 452 local node = get_push_settings(stanza, session);
447 handle_notify_request(stanza, node, user_push_services); 453 if handle_notify_request(stanza, node, user_push_services, true) ~= 0 then
454 if session.hibernating and not session.first_hibernated_push then
455 -- if important stanzas are treated differently (pushed with last-message-body field set to dummy string)
456 -- and the message was important (e.g. had a last-message-body field) OR if we treat all pushes equally,
457 -- then record the time of first push in the session for the smack module which will extend its hibernation
458 -- timeout based on the value of session.first_hibernated_push
459 if not dummy_body or (dummy_body and is_important(stanza)) then
460 session.first_hibernated_push = os_time();
461 end
462 end
463 end
448 end 464 end
449 return stanza; 465 return stanza;
450 end 466 end
451 467
452 local function process_smacks_queue(queue, session) 468 local function process_smacks_queue(queue, session)
453 if not session.push_identifier then return; end 469 if not session.push_identifier then return; end
454 local user_push_services = {[session.push_identifier] = session.push_settings}; 470 local user_push_services = {[session.push_identifier] = session.push_settings};
471 local notified = { unimportant = false; important = false }
455 for i=1, #queue do 472 for i=1, #queue do
456 local stanza = queue[i]; 473 local stanza = queue[i];
457 local node = get_push_settings(stanza, session); 474 local node = get_push_settings(stanza, session);
458 session.log("debug", "Invoking cloud handle_notify_request() for smacks queued stanza: %d", i); 475 stanza_type = "unimportant"
459 if handle_notify_request(stanza, node, user_push_services) ~= 0 then 476 if dummy_body and is_important(stanza) then stanza_type = "important"; end
460 session.log("debug", "Cloud handle_notify_request() > 0, not notifying for other queued stanzas"); 477 if not notified[stanza_type] then -- only notify if we didn't try to push for this stanza type already
461 return; -- only notify for one stanza in the queue, not for all in a row 478 -- session.log("debug", "Invoking cloud handle_notify_request() for smacks queued stanza: %d", i);
479 if handle_notify_request(stanza, node, user_push_services, false) ~= 0 then
480 if session.hibernating and not session.first_hibernated_push then
481 -- if important stanzas are treated differently (pushed with last-message-body field set to dummy string)
482 -- and the message was important (e.g. had a last-message-body field) OR if we treat all pushes equally,
483 -- then record the time of first push in the session for the smack module which will extend its hibernation
484 -- timeout based on the value of session.first_hibernated_push
485 if not dummy_body or (dummy_body and is_important(stanza)) then
486 session.first_hibernated_push = os_time();
487 end
488 end
489 session.log("debug", "Cloud handle_notify_request() > 0, not notifying for other queued stanzas of type %s", stanza_type);
490 notified[stanza_type] = true
491 end
462 end 492 end
463 end 493 end
464 end 494 end
465 495
466 -- smacks hibernation is started 496 -- smacks hibernation is started
467 local function hibernate_session(event) 497 local function hibernate_session(event)
468 local session = event.origin; 498 local session = event.origin;
469 local queue = event.queue; 499 local queue = event.queue;
500 session.first_hibernated_push = nil;
470 -- process unacked stanzas 501 -- process unacked stanzas
471 process_smacks_queue(queue, session); 502 process_smacks_queue(queue, session);
472 -- process future unacked (hibernated) stanzas 503 -- process future unacked (hibernated) stanzas
473 filters.add_filter(session, "stanzas/out", process_smacks_stanza, -990); 504 filters.add_filter(session, "stanzas/out", process_smacks_stanza, -990);
474 end 505 end
476 -- smacks hibernation is ended 507 -- smacks hibernation is ended
477 local function restore_session(event) 508 local function restore_session(event)
478 local session = event.resumed; 509 local session = event.resumed;
479 if session then -- older smacks module versions send only the "intermediate" session in event.session and no session.resumed one 510 if session then -- older smacks module versions send only the "intermediate" session in event.session and no session.resumed one
480 filters.remove_filter(session, "stanzas/out", process_smacks_stanza); 511 filters.remove_filter(session, "stanzas/out", process_smacks_stanza);
512 session.first_hibernated_push = nil;
481 end 513 end
482 end 514 end
483 515
484 -- smacks ack is delayed 516 -- smacks ack is delayed
485 local function ack_delayed(event) 517 local function ack_delayed(event)
503 if event.for_user == to then 535 if event.for_user == to then
504 local user_push_services = push_store:get(to); 536 local user_push_services = push_store:get(to);
505 if next(user_push_services) == nil then return end 537 if next(user_push_services) == nil then return end
506 538
507 -- only notify nodes with no active sessions (smacks is counted as active and handled separate) 539 -- only notify nodes with no active sessions (smacks is counted as active and handled separate)
508 local notify_push_sevices = {}; 540 local notify_push_services = {};
509 for identifier, push_info in pairs(user_push_services) do 541 for identifier, push_info in pairs(user_push_services) do
510 local identifier_found = nil; 542 local identifier_found = nil;
511 for _, session in pairs(user_session) do 543 for _, session in pairs(user_session) do
512 -- module:log("debug", "searching for '%s': identifier '%s' for session %s", tostring(identifier), tostring(session.push_identifier), tostring(session.full_jid)); 544 -- module:log("debug", "searching for '%s': identifier '%s' for session %s", tostring(identifier), tostring(session.push_identifier), tostring(session.full_jid));
513 if session.push_identifier == identifier then 545 if session.push_identifier == identifier then
516 end 548 end
517 end 549 end
518 if identifier_found then 550 if identifier_found then
519 identifier_found.log("debug", "Not cloud notifying '%s' of new MAM stanza (session still alive)", identifier); 551 identifier_found.log("debug", "Not cloud notifying '%s' of new MAM stanza (session still alive)", identifier);
520 else 552 else
521 notify_push_sevices[identifier] = push_info; 553 notify_push_services[identifier] = push_info;
522 end 554 end
523 end 555 end
524 556
525 handle_notify_request(event.stanza, to, notify_push_sevices); 557 handle_notify_request(event.stanza, to, notify_push_services, true);
526 end 558 end
527 end 559 end
528 560
529 module:hook("smacks-hibernation-start", hibernate_session); 561 module:hook("smacks-hibernation-start", hibernate_session);
530 module:hook("smacks-hibernation-end", restore_session); 562 module:hook("smacks-hibernation-end", restore_session);
533 565
534 local function send_ping(event) 566 local function send_ping(event)
535 local user = event.user; 567 local user = event.user;
536 local user_push_services = push_store:get(user); 568 local user_push_services = push_store:get(user);
537 local push_services = event.push_services or user_push_services; 569 local push_services = event.push_services or user_push_services;
538 handle_notify_request(nil, user, push_services); 570 handle_notify_request(nil, user, push_services, true);
539 end 571 end
540 -- can be used by other modules to ping one or more (or all) push endpoints 572 -- can be used by other modules to ping one or more (or all) push endpoints
541 module:hook("cloud-notify-ping", send_ping); 573 module:hook("cloud-notify-ping", send_ping);
542 574
543 module:log("info", "Module loaded"); 575 module:log("info", "Module loaded");