plugins/muc/muc.lib: Fix getting a list of occupants by role (it was sending presence...
[prosody.git] / plugins / muc / occupant.lib.lua
1 local next = next;
2 local pairs = pairs;
3 local setmetatable = setmetatable;
4 local st = require "util.stanza";
5
6 local get_filtered_presence do
7         local presence_filters = {
8                 ["http://jabber.org/protocol/muc"] = true;
9                 ["http://jabber.org/protocol/muc#user"] = true;
10         }
11         local function presence_filter(tag)
12                 if presence_filters[tag.attr.xmlns] then
13                         return nil;
14                 end
15                 return tag;
16         end
17         function get_filtered_presence(stanza)
18                 return st.clone(stanza):maptags(presence_filter);
19         end
20 end
21
22 local occupant_mt = {};
23 occupant_mt.__index = occupant_mt;
24
25 local function new_occupant(bare_real_jid, nick)
26         return setmetatable({
27                 bare_jid = bare_real_jid;
28                 nick = nick; -- in-room jid
29                 sessions = {}; -- hash from real_jid to presence stanzas. stanzas should not be modified
30                 role = nil;
31                 jid = nil; -- Primary session
32         }, occupant_mt);
33 end
34
35 -- Deep copy an occupant
36 local function copy_occupant(occupant)
37         local sessions = {};
38         for full_jid, presence_stanza in pairs(occupant.sessions) do
39                 -- Don't keep unavailable presences, as they'll accumulate; unless they're the primary session
40                 if presence_stanza.attr.type ~= "unavailable" or full_jid == occupant.jid then
41                         sessions[full_jid] = presence_stanza;
42                 end
43         end
44         return setmetatable({
45                 bare_jid = occupant.bare_jid;
46                 nick = occupant.nick;
47                 sessions = sessions;
48                 role = occupant.role;
49                 jid = occupant.jid;
50         }, occupant_mt);
51 end
52
53 -- finds another session to be the primary (there might not be one)
54 function occupant_mt:choose_new_primary()
55         for jid, pr in self:each_session() do
56                 if pr.attr.type ~= "unavailable" then
57                         return jid;
58                 end
59         end
60         return nil;
61 end
62
63 function occupant_mt:set_session(real_jid, presence_stanza, replace_primary)
64         local pr = get_filtered_presence(presence_stanza);
65         pr.attr.from = self.nick;
66         pr.attr.to = real_jid;
67
68         self.sessions[real_jid] = pr;
69         if replace_primary then
70                 self.jid = real_jid;
71         elseif self.jid == nil or (pr.attr.type == "unavailable" and self.jid == real_jid) then
72                 -- Only leave an unavailable presence as primary when there are no other options
73                 self.jid = self:choose_new_primary() or real_jid;
74         end
75 end
76
77 function occupant_mt:remove_session(real_jid)
78         -- Delete original session
79         self.sessions[real_jid] = nil;
80         if self.jid == real_jid then
81                 self.jid = self:choose_new_primary();
82         end
83 end
84
85 function occupant_mt:each_session()
86         return pairs(self.sessions)
87 end
88
89 function occupant_mt:get_presence(real_jid)
90         return self.sessions[real_jid or self.jid]
91 end
92
93 return {
94         new = new_occupant;
95         copy = copy_occupant;
96         mt = occupant_mt;
97 }