Comparison

plugins/muc/muc.lib.lua @ 7409:9a3ce6da3256

MUC: Split out handling of normal (un)available presence into its own method
author Kim Alvefur <zash@zash.se>
date Thu, 28 Apr 2016 22:53:22 +0200
parent 7406:92755ca737c3
child 7410:45f543c82893
comparison
equal deleted inserted replaced
7408:cf53081ce767 7409:9a3ce6da3256
382 event.origin.send(reply:tag("x", {xmlns = "http://jabber.org/protocol/muc"})); 382 event.origin.send(reply:tag("x", {xmlns = "http://jabber.org/protocol/muc"}));
383 return true; 383 return true;
384 end 384 end
385 end, -10); 385 end, -10);
386 386
387 function room_mt:handle_normal_presence(origin, stanza)
388 local type = stanza.attr.type;
389 local real_jid = stanza.attr.from;
390 local bare_jid = jid_bare(real_jid);
391 local orig_occupant, dest_occupant;
392 local is_new_room = next(self._affiliations) == nil;
393 if is_new_room then
394 if type == "unavailable" then return true; end -- Unavailable from someone not in the room
395 if module:fire_event("muc-room-pre-create", {
396 room = self;
397 origin = origin;
398 stanza = stanza;
399 }) then return true; end
400 else
401 orig_occupant = self:get_occupant_by_real_jid(real_jid);
402 if type == "unavailable" and orig_occupant == nil then return true; end -- Unavailable from someone not in the room
403 end
404 local is_first_dest_session;
405 if type == "unavailable" then -- luacheck: ignore 542
406 -- FIXME Why the empty if branch?
407 -- dest_occupant = nil
408 elseif orig_occupant and orig_occupant.nick == stanza.attr.to then -- Just a presence update
409 log("debug", "presence update for %s from session %s", orig_occupant.nick, real_jid);
410 dest_occupant = orig_occupant;
411 else
412 local dest_jid = stanza.attr.to;
413 dest_occupant = self:get_occupant_by_nick(dest_jid);
414 if dest_occupant == nil then
415 log("debug", "no occupant found for %s; creating new occupant object for %s", dest_jid, real_jid);
416 is_first_dest_session = true;
417 dest_occupant = self:new_occupant(bare_jid, dest_jid);
418 else
419 is_first_dest_session = false;
420 end
421 end
422 local is_last_orig_session;
423 if orig_occupant ~= nil then
424 -- Is there are least 2 sessions?
425 local iter, ob, last = orig_occupant:each_session();
426 is_last_orig_session = iter(ob, iter(ob, last)) == nil;
427 end
428
429 -- TODO Handle these cases sensibly
430 local muc_x = stanza:get_child("x", "http://jabber.org/protocol/muc");
431 if orig_occupant == nil and not muc_x then
432 module:log("debug", "Join without <x>, possibly desynced");
433 elseif orig_occupant ~= nil and muc_x then
434 module:log("debug", "Presence update with <x>, possibly desynced");
435 end
436
437 local event, event_name = {
438 room = self;
439 origin = origin;
440 stanza = stanza;
441 is_first_session = is_first_dest_session;
442 is_last_session = is_last_orig_session;
443 };
444 if orig_occupant == nil then
445 event_name = "muc-occupant-pre-join";
446 event.is_new_room = is_new_room;
447 event.occupant = dest_occupant;
448 elseif dest_occupant == nil then
449 event_name = "muc-occupant-pre-leave";
450 event.occupant = orig_occupant;
451 else
452 event_name = "muc-occupant-pre-change";
453 event.orig_occupant = orig_occupant;
454 event.dest_occupant = dest_occupant;
455 end
456 if module:fire_event(event_name, event) then return true; end
457
458 -- Check for nick conflicts
459 if dest_occupant ~= nil and not is_first_dest_session and bare_jid ~= jid_bare(dest_occupant.bare_jid) then -- new nick or has different bare real jid
460 log("debug", "%s couldn't join due to nick conflict: %s", real_jid, dest_occupant.nick);
461 local reply = st.error_reply(stanza, "cancel", "conflict"):up();
462 reply.tags[1].attr.code = "409";
463 origin.send(reply:tag("x", {xmlns = "http://jabber.org/protocol/muc"}));
464 return true;
465 end
466
467 -- Send presence stanza about original occupant
468 if orig_occupant ~= nil and orig_occupant ~= dest_occupant then
469 local orig_x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user";});
470 local dest_nick;
471 if dest_occupant == nil then -- Session is leaving
472 log("debug", "session %s is leaving occupant %s", real_jid, orig_occupant.nick);
473 if is_last_orig_session then
474 orig_occupant.role = nil;
475 end
476 orig_occupant:set_session(real_jid, stanza);
477 else
478 log("debug", "session %s is changing from occupant %s to %s", real_jid, orig_occupant.nick, dest_occupant.nick);
479 local generated_unavail = st.presence {from = orig_occupant.nick, to = real_jid, type = "unavailable"};
480 orig_occupant:set_session(real_jid, generated_unavail);
481 dest_nick = select(3, jid_split(dest_occupant.nick));
482 if not is_first_dest_session then -- User is swapping into another pre-existing session
483 log("debug", "session %s is swapping into multisession %s, showing it leave.", real_jid, dest_occupant.nick);
484 -- Show the other session leaving
485 local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user";})
486 :tag("status"):text("you are joining pre-existing session " .. dest_nick):up();
487 add_item(x, self:get_affiliation(bare_jid), "none");
488 local pr = st.presence{from = dest_occupant.nick, to = real_jid, type = "unavailable"}
489 :add_child(x);
490 self:route_stanza(pr);
491 end
492 if is_first_dest_session and is_last_orig_session then -- Normal nick change
493 log("debug", "no sessions in %s left; publically marking as nick change", orig_occupant.nick);
494 orig_x:tag("status", {code = "303";}):up();
495 else -- The session itself always needs to see a nick change
496 -- don't want to get our old nick's available presence,
497 -- so remove our session from there, and manually generate an unavailable
498 orig_occupant:remove_session(real_jid);
499 log("debug", "generating nick change for %s", real_jid);
500 local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user";});
501 -- self:build_item_list(orig_occupant, x, false, dest_nick); -- COMPAT: clients get confused if they see other items besides their own
502 add_item(x, self:get_affiliation(bare_jid), orig_occupant.role, real_jid, dest_nick);
503 x:tag("status", {code = "303";}):up();
504 x:tag("status", {code = "110";}):up();
505 self:route_stanza(generated_unavail:add_child(x));
506 dest_nick = nil; -- set dest_nick to nil; so general populance doesn't see it for whole orig_occupant
507 end
508 end
509 self:save_occupant(orig_occupant);
510 self:publicise_occupant_status(orig_occupant, orig_x, dest_nick);
511
512 if is_last_orig_session then
513 module:fire_event("muc-occupant-left", {
514 room = self;
515 nick = orig_occupant.nick;
516 occupant = orig_occupant;
517 origin = origin;
518 stanza = stanza;
519 });
520 end
521 end
522
523 if dest_occupant ~= nil then
524 dest_occupant:set_session(real_jid, stanza);
525 local dest_x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user";});
526 if is_new_room then
527 dest_x:tag("status", {code = "201"}):up();
528 end
529 if orig_occupant == nil and self:get_whois() == "anyone" then
530 dest_x:tag("status", {code = "100"}):up();
531 end
532 self:save_occupant(dest_occupant);
533
534 if orig_occupant == nil then
535 -- Send occupant list to newly joined user
536 self:send_occupant_list(real_jid, function(nick, occupant) -- luacheck: ignore 212
537 -- Don't include self
538 return occupant:get_presence(real_jid) == nil;
539 end)
540 end
541 self:publicise_occupant_status(dest_occupant, dest_x);
542
543 if orig_occupant ~= nil and orig_occupant ~= dest_occupant and not is_last_orig_session then -- If user is swapping and wasn't last original session
544 log("debug", "session %s split nicks; showing %s rejoining", real_jid, orig_occupant.nick);
545 -- Show the original nick joining again
546 local pr = st.clone(orig_occupant:get_presence());
547 pr.attr.to = real_jid;
548 local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user";});
549 self:build_item_list(orig_occupant, x, false);
550 -- TODO: new status code to inform client this was the multi-session it left?
551 pr:add_child(x);
552 self:route_stanza(pr);
553 end
554
555 if orig_occupant == nil then
556 if is_first_dest_session then
557 module:fire_event("muc-occupant-joined", {
558 room = self;
559 nick = dest_occupant.nick;
560 occupant = dest_occupant;
561 stanza = stanza;
562 origin = origin;
563 });
564 end
565 module:fire_event("muc-occupant-session-new", {
566 room = self;
567 nick = dest_occupant.nick;
568 occupant = dest_occupant;
569 stanza = stanza;
570 origin = origin;
571 jid = real_jid;
572 });
573 end
574 end
575 return true;
576 end
577
387 function room_mt:handle_presence_to_occupant(origin, stanza) 578 function room_mt:handle_presence_to_occupant(origin, stanza)
388 local type = stanza.attr.type; 579 local type = stanza.attr.type;
389 if type == "error" then -- error, kick em out! 580 if type == "error" then -- error, kick em out!
390 return self:handle_kickable(origin, stanza) 581 return self:handle_kickable(origin, stanza)
391 elseif type == nil or type == "unavailable" then 582 elseif type == nil or type == "unavailable" then
392 local real_jid = stanza.attr.from; 583 return self:handle_normal_presence(origin, stanza);
393 local bare_jid = jid_bare(real_jid);
394 local orig_occupant, dest_occupant;
395 local is_new_room = next(self._affiliations) == nil;
396 if is_new_room then
397 if type == "unavailable" then return true; end -- Unavailable from someone not in the room
398 if module:fire_event("muc-room-pre-create", {
399 room = self;
400 origin = origin;
401 stanza = stanza;
402 }) then return true; end
403 else
404 orig_occupant = self:get_occupant_by_real_jid(real_jid);
405 if type == "unavailable" and orig_occupant == nil then return true; end -- Unavailable from someone not in the room
406 end
407 local is_first_dest_session;
408 if type == "unavailable" then -- luacheck: ignore 542
409 -- FIXME Why the empty if branch?
410 -- dest_occupant = nil
411 elseif orig_occupant and orig_occupant.nick == stanza.attr.to then -- Just a presence update
412 log("debug", "presence update for %s from session %s", orig_occupant.nick, real_jid);
413 dest_occupant = orig_occupant;
414 else
415 local dest_jid = stanza.attr.to;
416 dest_occupant = self:get_occupant_by_nick(dest_jid);
417 if dest_occupant == nil then
418 log("debug", "no occupant found for %s; creating new occupant object for %s", dest_jid, real_jid);
419 is_first_dest_session = true;
420 dest_occupant = self:new_occupant(bare_jid, dest_jid);
421 else
422 is_first_dest_session = false;
423 end
424 end
425 local is_last_orig_session;
426 if orig_occupant ~= nil then
427 -- Is there are least 2 sessions?
428 local iter, ob, last = orig_occupant:each_session();
429 is_last_orig_session = iter(ob, iter(ob, last)) == nil;
430 end
431
432 -- TODO Handle these cases sensibly
433 local muc_x = stanza:get_child("x", "http://jabber.org/protocol/muc");
434 if orig_occupant == nil and not muc_x then
435 module:log("debug", "Join without <x>, possibly desynced");
436 elseif orig_occupant ~= nil and muc_x then
437 module:log("debug", "Presence update with <x>, possibly desynced");
438 end
439
440 local event, event_name = {
441 room = self;
442 origin = origin;
443 stanza = stanza;
444 is_first_session = is_first_dest_session;
445 is_last_session = is_last_orig_session;
446 };
447 if orig_occupant == nil then
448 event_name = "muc-occupant-pre-join";
449 event.is_new_room = is_new_room;
450 event.occupant = dest_occupant;
451 elseif dest_occupant == nil then
452 event_name = "muc-occupant-pre-leave";
453 event.occupant = orig_occupant;
454 else
455 event_name = "muc-occupant-pre-change";
456 event.orig_occupant = orig_occupant;
457 event.dest_occupant = dest_occupant;
458 end
459 if module:fire_event(event_name, event) then return true; end
460
461 -- Check for nick conflicts
462 if dest_occupant ~= nil and not is_first_dest_session and bare_jid ~= jid_bare(dest_occupant.bare_jid) then -- new nick or has different bare real jid
463 log("debug", "%s couldn't join due to nick conflict: %s", real_jid, dest_occupant.nick);
464 local reply = st.error_reply(stanza, "cancel", "conflict"):up();
465 reply.tags[1].attr.code = "409";
466 origin.send(reply:tag("x", {xmlns = "http://jabber.org/protocol/muc"}));
467 return true;
468 end
469
470 -- Send presence stanza about original occupant
471 if orig_occupant ~= nil and orig_occupant ~= dest_occupant then
472 local orig_x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user";});
473 local dest_nick;
474 if dest_occupant == nil then -- Session is leaving
475 log("debug", "session %s is leaving occupant %s", real_jid, orig_occupant.nick);
476 if is_last_orig_session then
477 orig_occupant.role = nil;
478 end
479 orig_occupant:set_session(real_jid, stanza);
480 else
481 log("debug", "session %s is changing from occupant %s to %s", real_jid, orig_occupant.nick, dest_occupant.nick);
482 local generated_unavail = st.presence {from = orig_occupant.nick, to = real_jid, type = "unavailable"};
483 orig_occupant:set_session(real_jid, generated_unavail);
484 dest_nick = select(3, jid_split(dest_occupant.nick));
485 if not is_first_dest_session then -- User is swapping into another pre-existing session
486 log("debug", "session %s is swapping into multisession %s, showing it leave.", real_jid, dest_occupant.nick);
487 -- Show the other session leaving
488 local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user";})
489 :tag("status"):text("you are joining pre-existing session " .. dest_nick):up();
490 add_item(x, self:get_affiliation(bare_jid), "none");
491 local pr = st.presence{from = dest_occupant.nick, to = real_jid, type = "unavailable"}
492 :add_child(x);
493 self:route_stanza(pr);
494 end
495 if is_first_dest_session and is_last_orig_session then -- Normal nick change
496 log("debug", "no sessions in %s left; publically marking as nick change", orig_occupant.nick);
497 orig_x:tag("status", {code = "303";}):up();
498 else -- The session itself always needs to see a nick change
499 -- don't want to get our old nick's available presence,
500 -- so remove our session from there, and manually generate an unavailable
501 orig_occupant:remove_session(real_jid);
502 log("debug", "generating nick change for %s", real_jid);
503 local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user";});
504 -- self:build_item_list(orig_occupant, x, false, dest_nick); -- COMPAT: clients get confused if they see other items besides their own
505 add_item(x, self:get_affiliation(bare_jid), orig_occupant.role, real_jid, dest_nick);
506 x:tag("status", {code = "303";}):up();
507 x:tag("status", {code = "110";}):up();
508 self:route_stanza(generated_unavail:add_child(x));
509 dest_nick = nil; -- set dest_nick to nil; so general populance doesn't see it for whole orig_occupant
510 end
511 end
512 self:save_occupant(orig_occupant);
513 self:publicise_occupant_status(orig_occupant, orig_x, dest_nick);
514
515 if is_last_orig_session then
516 module:fire_event("muc-occupant-left", {
517 room = self;
518 nick = orig_occupant.nick;
519 occupant = orig_occupant;
520 origin = origin;
521 stanza = stanza;
522 });
523 end
524 end
525
526 if dest_occupant ~= nil then
527 dest_occupant:set_session(real_jid, stanza);
528 local dest_x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user";});
529 if is_new_room then
530 dest_x:tag("status", {code = "201"}):up();
531 end
532 if orig_occupant == nil and self:get_whois() == "anyone" then
533 dest_x:tag("status", {code = "100"}):up();
534 end
535 self:save_occupant(dest_occupant);
536
537 if orig_occupant == nil then
538 -- Send occupant list to newly joined user
539 self:send_occupant_list(real_jid, function(nick, occupant) -- luacheck: ignore 212
540 -- Don't include self
541 return occupant:get_presence(real_jid) == nil;
542 end)
543 end
544 self:publicise_occupant_status(dest_occupant, dest_x);
545
546 if orig_occupant ~= nil and orig_occupant ~= dest_occupant and not is_last_orig_session then -- If user is swapping and wasn't last original session
547 log("debug", "session %s split nicks; showing %s rejoining", real_jid, orig_occupant.nick);
548 -- Show the original nick joining again
549 local pr = st.clone(orig_occupant:get_presence());
550 pr.attr.to = real_jid;
551 local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user";});
552 self:build_item_list(orig_occupant, x, false);
553 -- TODO: new status code to inform client this was the multi-session it left?
554 pr:add_child(x);
555 self:route_stanza(pr);
556 end
557
558 if orig_occupant == nil then
559 if is_first_dest_session then
560 module:fire_event("muc-occupant-joined", {
561 room = self;
562 nick = dest_occupant.nick;
563 occupant = dest_occupant;
564 stanza = stanza;
565 origin = origin;
566 });
567 end
568 module:fire_event("muc-occupant-session-new", {
569 room = self;
570 nick = dest_occupant.nick;
571 occupant = dest_occupant;
572 stanza = stanza;
573 origin = origin;
574 jid = real_jid;
575 });
576 end
577 end
578 elseif type ~= 'result' then -- bad type 584 elseif type ~= 'result' then -- bad type
579 if type ~= 'visible' and type ~= 'invisible' then -- COMPAT ejabberd can broadcast or forward XEP-0018 presences 585 if type ~= 'visible' and type ~= 'invisible' then -- COMPAT ejabberd can broadcast or forward XEP-0018 presences
580 origin.send(st.error_reply(stanza, "modify", "bad-request")); -- FIXME correct error? 586 origin.send(st.error_reply(stanza, "modify", "bad-request")); -- FIXME correct error?
581 end 587 end
582 end 588 end