mod_dialback: Move d-w-d after to/from validation
[prosody.git] / plugins / muc / muc.lib.lua
index 483b0812b0d7e538c90a64b435f9bda0938158c6..8cf8d882e5895962a2a3e779267a52e9d6cc4e46 100644 (file)
@@ -1,7 +1,7 @@
 -- Prosody IM
 -- Copyright (C) 2008-2010 Matthew Wild
 -- Copyright (C) 2008-2010 Waqas Hussain
--- 
+--
 -- This project is MIT/X11 licensed. Please see the
 -- COPYING file in the source package for more information.
 --
@@ -107,18 +107,21 @@ function room_mt:broadcast_message(stanza, historic)
        end
        stanza.attr.to = to;
        if historic then -- add to history
-               local history = self._data['history'];
-               if not history then history = {}; self._data['history'] = history; end
-               stanza = st.clone(stanza);
-               stanza.attr.to = "";
-               local stamp = datetime.datetime();
-               stanza:tag("delay", {xmlns = "urn:xmpp:delay", from = muc_domain, stamp = stamp}):up(); -- XEP-0203
-               stanza:tag("x", {xmlns = "jabber:x:delay", from = muc_domain, stamp = datetime.legacy()}):up(); -- XEP-0091 (deprecated)
-               local entry = { stanza = stanza, stamp = stamp };
-               t_insert(history, entry);
-               while #history > (self._data.history_length or default_history_length) do t_remove(history, 1) end
+               return self:save_to_history(stanza)
        end
 end
+function room_mt:save_to_history(stanza)
+       local history = self._data['history'];
+       if not history then history = {}; self._data['history'] = history; end
+       stanza = st.clone(stanza);
+       stanza.attr.to = "";
+       local stamp = datetime.datetime();
+       stanza:tag("delay", {xmlns = "urn:xmpp:delay", from = muc_domain, stamp = stamp}):up(); -- XEP-0203
+       stanza:tag("x", {xmlns = "jabber:x:delay", from = muc_domain, stamp = datetime.legacy()}):up(); -- XEP-0091 (deprecated)
+       local entry = { stanza = stanza, stamp = stamp };
+       t_insert(history, entry);
+       while #history > (self._data.history_length or default_history_length) do t_remove(history, 1) end
+end
 function room_mt:broadcast_except_nick(stanza, nick)
        for rnick, occupant in pairs(self._occupants) do
                if rnick ~= nick then
@@ -147,10 +150,10 @@ function room_mt:send_history(to, stanza)
        if history then
                local x_tag = stanza and stanza:get_child("x", "http://jabber.org/protocol/muc");
                local history_tag = x_tag and x_tag:get_child("history", "http://jabber.org/protocol/muc");
-               
+
                local maxchars = history_tag and tonumber(history_tag.attr.maxchars);
                if maxchars then maxchars = math.floor(maxchars); end
-               
+
                local maxstanzas = math.floor(history_tag and tonumber(history_tag.attr.maxstanzas) or #history);
                if not history_tag then maxstanzas = 20; end
 
@@ -163,7 +166,7 @@ function room_mt:send_history(to, stanza)
 
                local n = 0;
                local charcount = 0;
-               
+
                for i=#history,1,-1 do
                        local entry = history[i];
                        if maxchars then
@@ -184,6 +187,8 @@ function room_mt:send_history(to, stanza)
                        self:_route_stanza(msg);
                end
        end
+end
+function room_mt:send_subject(to)
        if self._data['subject'] then
                self:_route_stanza(st.message({type='groupchat', from=self._data['subject_from'] or self.jid, to=to}):tag("subject"):text(self._data['subject']));
        end
@@ -202,7 +207,7 @@ function room_mt:get_disco_info(stanza)
                :tag("feature", {var=self._data.whois ~= "anyone" and "muc_semianonymous" or "muc_nonanonymous"}):up()
                :add_child(dataform.new({
                        { name = "FORM_TYPE", type = "hidden", value = "http://jabber.org/protocol/muc#roominfo" },
-                       { name = "muc#roominfo_description", label = "Description"},
+                       { name = "muc#roominfo_description", label = "Description", value = "" },
                        { name = "muc#roominfo_occupants", label = "Number of occupants", value = tostring(count) }
                }):form({["muc#roominfo_description"] = self:get_description()}, 'result'))
        ;
@@ -351,7 +356,7 @@ local function construct_stanza_id(room, stanza)
        local from_nick = room._jid_nick[from_jid];
        local occupant = room._occupants[to_nick];
        local to_jid = occupant.jid;
-       
+
        return from_nick, to_jid, base64.encode(to_jid.."\0"..stanza.attr.id.."\0"..md5(from_jid));
 end
 local function deconstruct_stanza_id(room, stanza)
@@ -480,6 +485,12 @@ function room_mt:handle_to_occupant(origin, stanza) -- PM, vCards, etc
                                        log("debug", "%s joining as %s", from, to);
                                        if not next(self._affiliations) then -- new room, no owners
                                                self._affiliations[jid_bare(from)] = "owner";
+                                               if self.locked and not stanza:get_child("x", "http://jabber.org/protocol/muc") then
+                                                       self.locked = nil; -- Older groupchat protocol doesn't lock
+                                               end
+                                       elseif self.locked then -- Deny entry
+                                               origin.send(st.error_reply(stanza, "cancel", "item-not-found"));
+                                               return;
                                        end
                                        local affiliation = self:get_affiliation(from);
                                        local role = self:get_default_role(affiliation)
@@ -501,9 +512,13 @@ function room_mt:handle_to_occupant(origin, stanza) -- PM, vCards, etc
                                                if self._data.whois == 'anyone' then
                                                        pr:tag("status", {code='100'}):up();
                                                end
+                                               if self.locked then
+                                                       pr:tag("status", {code='201'}):up();
+                                               end
                                                pr.attr.to = from;
                                                self:_route_stanza(pr);
                                                self:send_history(from, stanza);
+                                               self:send_subject(from);
                                        elseif not affiliation then -- registration required for entering members-only room
                                                local reply = st.error_reply(stanza, "auth", "registration-required"):up();
                                                reply.tags[1].attr.code = "407";
@@ -555,6 +570,7 @@ function room_mt:handle_to_occupant(origin, stanza) -- PM, vCards, etc
                                end
                                stanza.attr.from, stanza.attr.to, stanza.attr.id = from, to, id;
                        else -- message
+                               stanza:tag("x", { xmlns = "http://jabber.org/protocol/muc#user" }):up();
                                stanza.attr.from = current_nick;
                                for jid in pairs(o_data.sessions) do
                                        stanza.attr.to = jid;
@@ -688,6 +704,10 @@ function room_mt:process_form(origin, stanza)
        handle_option("password", "muc#roomconfig_roomsecret");
 
        if self.save then self:save(true); end
+       if self.locked then
+               module:fire_event("muc-room-unlocked", { room = self });
+               self.locked = nil;
+       end
        origin.send(st.reply(stanza));
 
        if next(changed) then
@@ -1012,7 +1032,7 @@ function room_mt:can_set_role(actor_jid, occupant_jid, role)
        if actor_jid == true then return true; end
 
        local actor = self._occupants[self._jid_nick[actor_jid]];
-       if actor.role == "moderator" then
+       if actor and actor.role == "moderator" then
                if occupant.affiliation ~= "owner" and occupant.affiliation ~= "admin" then
                        if actor.affiliation == "owner" or actor.affiliation == "admin" then
                                return true;