MUC: Be more careful with deserialization of rooms, fix case where a bare jid has...
[prosody.git] / plugins / muc / muc.lib.lua
index b289b8ce3f0319e3bfc5242529475154d91cb87d..ce6c57840819c30010c87ccc5c18323e2064bcb1 100644 (file)
@@ -196,7 +196,7 @@ function room_mt:publicise_occupant_status(occupant, base_x, nick, actor, reason
        local base_presence do
                -- Try to use main jid's presence
                local pr = occupant:get_presence();
-               if pr and (pr.attr.type ~= "unavailable" or occupant.role == nil) then
+               if pr and (pr.attr.type ~= "unavailable" and occupant.role ~= nil) then
                        base_presence = st.clone(pr);
                else -- user is leaving but didn't send a leave presence. make one for them
                        base_presence = st.presence {from = occupant.nick; type = "unavailable";};
@@ -709,7 +709,7 @@ function room_mt:process_form(origin, stanza)
                end
                event.field, event.value = nil, nil;
 
-               self:save(true);
+               self:save();
                origin.send(st.reply(stanza));
 
                if next(event.status_codes) then
@@ -737,7 +737,6 @@ function room_mt:clear(x)
                occupants_updated[occupant] = true;
        end
        for occupant in pairs(occupants_updated) do
-               occupant:set_session(occupant.jid, st.presence({type="unavailable"}), true);
                self:publicise_occupant_status(occupant, x);
                module:fire_event("muc-occupant-left", { room = self; nick = occupant.nick; occupant = occupant;});
        end
@@ -1160,7 +1159,7 @@ function room_mt:set_affiliation(actor, jid, affiliation, reason)
                end
        end
 
-       self:save(true);
+       self:save();
 
        module:fire_event("muc-set-affiliation", {
                room = self;
@@ -1184,7 +1183,7 @@ function room_mt:set_role(actor, occupant_jid, role, reason)
        if not actor then return nil, "modify", "not-acceptable"; end
 
        local occupant = self:get_occupant_by_nick(occupant_jid);
-       if not occupant then return nil, "modify", "not-acceptable"; end
+       if not occupant then return nil, "modify", "item-not-found"; end
 
        if valid_roles[role or "none"] == nil then
                return nil, "modify", "not-acceptable";
@@ -1244,18 +1243,86 @@ function _M.new_room(jid, config)
        }, room_mt);
 end
 
-function room_mt:freeze()
-       return {
-               jid = self.jid;
+function room_mt:freeze(live)
+       local frozen = {
+               _jid = self.jid;
                _data = self._data;
-               _affiliations = self._affiliations;
-       }
+       };
+       for user, affiliation in pairs(self._affiliations) do
+               frozen[user] = affiliation;
+       end
+       if live then
+               for nick, occupant in self:each_occupant() do
+                       frozen[nick] = {
+                               bare_jid = occupant.bare_jid;
+                               role = occupant.role;
+                               jid = occupant.jid;
+                       }
+                       for jid, presence in occupant:each_session() do
+                               frozen[jid] = st.preserialize(presence);
+                       end
+               end
+       end
+       return frozen;
 end
 
 function _M.restore_room(frozen)
-       local room_jid = frozen.jid;
+       -- COMPAT
+       if frozen.jid and frozen._affiliations then
+               local room = _M.new_room(frozen.jid, frozen._data);
+               room._affiliations = frozen._affiliations;
+               return room;
+       end
+
+       local room_jid = frozen._jid;
        local room = _M.new_room(room_jid, frozen._data);
-       room._affiliations = frozen._affiliations;
+
+       local occupants = {};
+       local occupant_sessions = {};
+       local room_name, room_host = jid_split(room_jid);
+       for jid, data in pairs(frozen) do
+               local node, host, resource = jid_split(jid);
+               if node or host:sub(1,1) ~= "_" then
+                       if not resource and type(data) == "string" then
+                               -- bare jid: affiliation
+                               room._affiliations[jid] = data;
+                       elseif host == room_host and node == room_name and resource then
+                               -- full room jid: bare real jid and role
+                               local bare_jid = data.bare_jid;
+                               local   occupant = occupant_lib.new(bare_jid, jid);
+                               occupant.jid = data.jid;
+                               occupant.role = data.role;
+                               occupants[bare_jid] = occupant;
+                               local sessions = occupant_sessions[bare_jid];
+                               if sessions then
+                                       for full_jid, presence in pairs(sessions) do
+                                               occupant:set_session(full_jid, presence);
+                                       end
+                               end
+                               occupant_sessions[bare_jid] = nil;
+                       else
+                               -- full user jid: presence
+                               local presence = st.deserialize(data);
+                               local bare_jid = jid_bare(jid);
+                               local occupant = occupants[bare_jid];
+                               local sessions = occupant_sessions[bare_jid];
+                               if occupant then
+                                       occupant:set_session(jid, presence);
+                               elseif sessions then
+                                       sessions[jid] = presence;
+                               else
+                                       occupant_sessions[bare_jid] = {
+                                               [jid] = presence;
+                                       };
+                               end
+                       end
+               end
+       end
+
+       for _, occupant in pairs(occupants) do
+               room:save_occupant(occupant);
+       end
+
        return room;
 end