Software /
code /
prosody
Comparison
plugins/muc/muc.lib.lua @ 11245:43b43e7156b8
MUC: Add support for presence probes (fixes #1535)
The following patch allows Prosody to respond to `probe` presences and send out the probed occupant's current presence.
This is based on line 17.3 in XEP-0045:
A MUC service MAY handle presence probes sent to the room JID <room@service> or an occupant JID <room@service/nick>
(e.g, these might be sent by an occupant's home server to determine if the room is still online or to synchronize
presence information if the user or the user's server has gone offline temporarily or has started sharing presence again,
as for instance when Stanza Interception and Filtering Technology (XEP-0273) is used).
author | JC Brand <jc@opkode.com> |
---|---|
date | Sun, 19 Apr 2020 21:49:45 +0200 |
parent | 11236:b1d7027be61e |
child | 11246:ab189e707705 |
comparison
equal
deleted
inserted
replaced
11244:8a1f88241cb1 | 11245:43b43e7156b8 |
---|---|
214 elseif whois == "moderators" then | 214 elseif whois == "moderators" then |
215 return valid_roles[occupant.role or "none"] >= valid_roles.moderator; | 215 return valid_roles[occupant.role or "none"] >= valid_roles.moderator; |
216 end | 216 end |
217 end | 217 end |
218 | 218 |
219 | |
219 -- Broadcasts an occupant's presence to the whole room | 220 -- Broadcasts an occupant's presence to the whole room |
220 -- Takes the x element that goes into the stanzas | 221 -- Takes the x element that goes into the stanzas |
221 function room_mt:publicise_occupant_status(occupant, x, nick, actor, reason, prev_role, force_unavailable) | 222 function room_mt:publicise_occupant_status(occupant, x, nick, actor, reason, prev_role, force_unavailable, recipient) |
222 local base_x = x.base or x; | 223 local base_x = x.base or x; |
223 -- Build real jid and (optionally) occupant jid template presences | 224 -- Build real jid and (optionally) occupant jid template presences |
224 local base_presence do | 225 local base_presence do |
225 -- Try to use main jid's presence | 226 -- Try to use main jid's presence |
226 local pr = occupant:get_presence(); | 227 local pr = occupant:get_presence(); |
236 room = self; stanza = base_presence; x = base_x; | 237 room = self; stanza = base_presence; x = base_x; |
237 occupant = occupant; nick = nick; actor = actor; | 238 occupant = occupant; nick = nick; actor = actor; |
238 reason = reason; | 239 reason = reason; |
239 } | 240 } |
240 module:fire_event("muc-build-occupant-presence", event); | 241 module:fire_event("muc-build-occupant-presence", event); |
241 module:fire_event("muc-broadcast-presence", event); | 242 if not recipient then |
243 module:fire_event("muc-broadcast-presence", event); | |
244 end | |
242 | 245 |
243 -- Allow muc-broadcast-presence listeners to change things | 246 -- Allow muc-broadcast-presence listeners to change things |
244 nick = event.nick; | 247 nick = event.nick; |
245 actor = event.actor; | 248 actor = event.actor; |
246 reason = event.reason; | 249 reason = event.reason; |
279 self_x = st.clone(x.self or base_x); | 282 self_x = st.clone(x.self or base_x); |
280 self:build_item_list(occupant, self_x, false, nick, actor_nick, nil, reason); | 283 self:build_item_list(occupant, self_x, false, nick, actor_nick, nil, reason); |
281 self_p = st.clone(base_presence):add_child(self_x); | 284 self_p = st.clone(base_presence):add_child(self_x); |
282 end | 285 end |
283 | 286 |
287 local function get_p(rec_occupant) | |
288 local pr; | |
289 if can_see_real_jids(whois, rec_occupant) then | |
290 pr = get_full_p(); | |
291 elseif occupant.bare_jid == rec_occupant.bare_jid then | |
292 pr = self_p; | |
293 else | |
294 pr = get_anon_p(); | |
295 end | |
296 return pr | |
297 end | |
298 | |
299 if recipient then | |
300 return self:route_to_occupant(recipient, get_p(recipient)); | |
301 end | |
302 | |
284 local broadcast_roles = self:get_presence_broadcast(); | 303 local broadcast_roles = self:get_presence_broadcast(); |
285 | |
286 -- General populace | 304 -- General populace |
287 for occupant_nick, n_occupant in self:each_occupant() do | 305 for occupant_nick, n_occupant in self:each_occupant() do |
288 if occupant_nick ~= occupant.nick then | 306 if occupant_nick ~= occupant.nick then |
289 local pr; | 307 local pr = get_p(n_occupant); |
290 if can_see_real_jids(whois, n_occupant) then | |
291 pr = get_full_p(); | |
292 elseif occupant.bare_jid == n_occupant.bare_jid then | |
293 pr = self_p; | |
294 else | |
295 pr = get_anon_p(); | |
296 end | |
297 if broadcast_roles[occupant.role or "none"] or force_unavailable then | 308 if broadcast_roles[occupant.role or "none"] or force_unavailable then |
298 self:route_to_occupant(n_occupant, pr); | 309 self:route_to_occupant(n_occupant, pr); |
299 elseif prev_role and broadcast_roles[prev_role] then | 310 elseif prev_role and broadcast_roles[prev_role] then |
300 pr.attr.type = 'unavailable'; | 311 pr.attr.type = 'unavailable'; |
301 self:route_to_occupant(n_occupant, pr); | 312 self:route_to_occupant(n_occupant, pr); |
321 end | 332 end |
322 end | 333 end |
323 | 334 |
324 function room_mt:send_occupant_list(to, filter) | 335 function room_mt:send_occupant_list(to, filter) |
325 local to_bare = jid_bare(to); | 336 local to_bare = jid_bare(to); |
326 local is_anonymous = false; | |
327 local whois = self:get_whois(); | |
328 local broadcast_roles = self:get_presence_broadcast(); | 337 local broadcast_roles = self:get_presence_broadcast(); |
329 if whois ~= "anyone" then | 338 local is_anonymous = self:is_anonymous_for(to); |
330 local affiliation = self:get_affiliation(to); | |
331 if affiliation ~= "admin" and affiliation ~= "owner" then | |
332 local occupant = self:get_occupant_by_real_jid(to); | |
333 if not (occupant and can_see_real_jids(whois, occupant)) then | |
334 is_anonymous = true; | |
335 end | |
336 end | |
337 end | |
338 local broadcast_bare_jids = {}; -- Track which bare JIDs we have sent presence for | 339 local broadcast_bare_jids = {}; -- Track which bare JIDs we have sent presence for |
339 for occupant_jid, occupant in self:each_occupant() do | 340 for occupant_jid, occupant in self:each_occupant() do |
340 broadcast_bare_jids[occupant.bare_jid] = true; | 341 broadcast_bare_jids[occupant.bare_jid] = true; |
341 if filter == nil or filter(occupant_jid, occupant) then | 342 if filter == nil or filter(occupant_jid, occupant) then |
342 local x = st.stanza("x", {xmlns='http://jabber.org/protocol/muc#user'}); | 343 local x = st.stanza("x", {xmlns='http://jabber.org/protocol/muc#user'}); |
547 origin = origin; | 548 origin = origin; |
548 }); | 549 }); |
549 return true; | 550 return true; |
550 end | 551 end |
551 | 552 |
553 | |
554 function room_mt:is_anonymous_for(jid) | |
555 local is_anonymous = false; | |
556 local whois = self:get_whois(); | |
557 if whois ~= "anyone" then | |
558 local affiliation = self:get_affiliation(jid); | |
559 if affiliation ~= "admin" and affiliation ~= "owner" then | |
560 local occupant = self:get_occupant_by_real_jid(jid); | |
561 if not (occupant and can_see_real_jids(whois, occupant)) then | |
562 is_anonymous = true; | |
563 end | |
564 end | |
565 end | |
566 return is_anonymous; | |
567 end | |
568 | |
569 | |
570 function room_mt:build_unavailable_presence(from_muc_jid, to_jid) | |
571 local nick = jid_resource(from_muc_jid); | |
572 local from_jid = self:get_registered_jid(nick); | |
573 if (not from_jid) then | |
574 module:log("debug", "Received presence probe for unavailable nickname that's not registered"); | |
575 return; | |
576 end | |
577 local is_anonymous = self:is_anonymous_for(to_jid); | |
578 local affiliation = self:get_affiliation(from_jid) or "none"; | |
579 local pr = st.presence({ to = to_jid, from = from_muc_jid, type = "unavailable" }) | |
580 :tag("x", { xmlns = 'http://jabber.org/protocol/muc#user' }) | |
581 :tag("item", { | |
582 affiliation = affiliation; | |
583 role = "none"; | |
584 nick = nick; | |
585 jid = not is_anonymous and from_jid or nil }):up() | |
586 :up(); | |
587 | |
588 local x = pr:get_child("x", "http://jabber.org/protocol/muc"); | |
589 local event = { | |
590 room = self; stanza = pr; x = x; | |
591 bare_jid = from_jid; | |
592 nick = nick; | |
593 } | |
594 module:fire_event("muc-build-occupant-presence", event); | |
595 return event.stanza; | |
596 end | |
597 | |
598 | |
552 function room_mt:handle_normal_presence(origin, stanza) | 599 function room_mt:handle_normal_presence(origin, stanza) |
553 local type = stanza.attr.type; | 600 local type = stanza.attr.type; |
554 local real_jid = stanza.attr.from; | 601 local real_jid = stanza.attr.from; |
555 local bare_jid = jid_bare(real_jid); | 602 local bare_jid = jid_bare(real_jid); |
556 local orig_occupant = self:get_occupant_by_real_jid(real_jid); | 603 local orig_occupant = self:get_occupant_by_real_jid(real_jid); |
566 local is_first_dest_session; | 613 local is_first_dest_session; |
567 local dest_occupant; | 614 local dest_occupant; |
568 if type == "unavailable" then | 615 if type == "unavailable" then |
569 if orig_occupant == nil then return true; end -- Unavailable from someone not in the room | 616 if orig_occupant == nil then return true; end -- Unavailable from someone not in the room |
570 -- dest_occupant = nil | 617 -- dest_occupant = nil |
618 elseif type == "probe" then | |
619 local occupant = self:get_occupant_by_nick(stanza.attr.to); | |
620 if occupant == nil then | |
621 local from_muc_jid = stanza.attr.to; | |
622 local to_jid = real_jid; | |
623 local pr = self:build_unavailable_presence(from_muc_jid, to_jid); | |
624 if pr then | |
625 self:route_stanza(pr); | |
626 end | |
627 return true; | |
628 end | |
629 local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user"}); | |
630 self:publicise_occupant_status(occupant, x, nil, nil, nil, nil, false, orig_occupant); | |
631 return true; | |
571 elseif orig_occupant and orig_occupant.nick == stanza.attr.to then -- Just a presence update | 632 elseif orig_occupant and orig_occupant.nick == stanza.attr.to then -- Just a presence update |
572 log("debug", "presence update for %s from session %s", orig_occupant.nick, real_jid); | 633 log("debug", "presence update for %s from session %s", orig_occupant.nick, real_jid); |
573 dest_occupant = orig_occupant; | 634 dest_occupant = orig_occupant; |
574 else | 635 else |
575 local dest_jid = stanza.attr.to; | 636 local dest_jid = stanza.attr.to; |
745 | 806 |
746 function room_mt:handle_presence_to_occupant(origin, stanza) | 807 function room_mt:handle_presence_to_occupant(origin, stanza) |
747 local type = stanza.attr.type; | 808 local type = stanza.attr.type; |
748 if type == "error" then -- error, kick em out! | 809 if type == "error" then -- error, kick em out! |
749 return self:handle_kickable(origin, stanza) | 810 return self:handle_kickable(origin, stanza) |
750 elseif type == nil or type == "unavailable" then | 811 elseif type == nil or type == "unavailable" or type == "probe" then |
751 return self:handle_normal_presence(origin, stanza); | 812 return self:handle_normal_presence(origin, stanza); |
752 elseif type ~= 'result' then -- bad type | 813 elseif type ~= 'result' then -- bad type |
753 if type ~= 'visible' and type ~= 'invisible' then -- COMPAT ejabberd can broadcast or forward XEP-0018 presences | 814 if type ~= 'visible' and type ~= 'invisible' then -- COMPAT ejabberd can broadcast or forward XEP-0018 presences |
754 origin.send(st.error_reply(stanza, "modify", "bad-request", nil, self.jid)); -- FIXME correct error? | 815 origin.send(st.error_reply(stanza, "modify", "bad-request", nil, self.jid)); -- FIXME correct error? |
755 end | 816 end |