# HG changeset patch # User Matthew Wild # Date 1527712433 -3600 # Node ID 463505cc75d584c5a0b0a735a36d60ec4b18a437 # Parent bc8558bbc7977c83f53252e73c296d2d29afea61 MUC: Revert unstable MUC commits since 0.10.1 These have caused too many issue reports to be included in the stable branch at this time. Affected issues: #345, #397 Reverted commits: dcd53a565c01 6d4b0895f76d 1b10802a770e 564e897f0790 a7221ada9368 aaff40ec7001 05a3275b6873 c2b99fa134b3 8da11142fabf diff -r bc8558bbc797 -r 463505cc75d5 plugins/muc/muc.lib.lua --- a/plugins/muc/muc.lib.lua Wed May 30 21:02:09 2018 +0100 +++ b/plugins/muc/muc.lib.lua Wed May 30 21:33:53 2018 +0100 @@ -171,7 +171,7 @@ 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 = self._data.default_history_messages; end + if not history_tag then maxstanzas = 20; end local seconds = history_tag and tonumber(history_tag.attr.seconds); if seconds then seconds = datetime.datetime(os.time() - math.floor(seconds)); end @@ -359,20 +359,6 @@ self._data.history_length = length; end --- Fix for clients who don't support XEP-0045 correctly --- Default number of history messages the room returns -function room_mt:get_defaulthistorymessages() - return self._data.default_history_messages or default_history_length; -end -function room_mt:set_defaulthistorymessages(number) - number = math.min(tonumber(number) or default_history_length, self._data.history_length or default_history_length); - if number == default_history_length then - number = nil; - end - self._data.default_history_messages = number; -end - - local valid_whois = { moderators = true, anyone = true }; @@ -712,12 +698,6 @@ type = 'text-single', label = 'Maximum Number of History Messages Returned by Room', value = tostring(self:get_historylength()) - }, - { - name = 'muc#roomconfig_defaulthistorymessages', - type = 'text-single', - label = 'Default Number of History Messages Returned by Room', - value = tostring(self:get_defaulthistorymessages()) } }); return module:fire_event("muc-config-form", { room = self, actor = actor, form = form }) or form; @@ -766,7 +746,6 @@ handle_option("public", "muc#roomconfig_publicroom"); handle_option("changesubject", "muc#roomconfig_changesubject"); handle_option("historylength", "muc#roomconfig_historylength"); - handle_option("defaulthistorymessages", "muc#roomconfig_defaulthistorymessages"); handle_option("whois", "muc#roomconfig_whois", valid_whois); handle_option("password", "muc#roomconfig_roomsecret"); @@ -823,16 +802,10 @@ local affiliation = self:get_affiliation(actor); local current_nick = self._jid_nick[actor]; local role = current_nick and self._occupants[current_nick].role or self:get_default_role(affiliation); - if type == "set" then - local at_least_one_item_provided = false; - local callback = function() origin.send(st.reply(stanza)); end - - -- Gather all changes to affiliations and roles - local jid_affiliation = {}; - local jidnick_role = {}; - for item in stanza.tags[1]:childtags("item") do - at_least_one_item_provided = true; - + local item = stanza.tags[1].tags[1]; + if item and item.name == "item" then + if type == "set" then + local callback = function() origin.send(st.reply(stanza)); end if item.attr.jid then -- Validate provided JID item.attr.jid = jid_prep(item.attr.jid); if not item.attr.jid then @@ -847,55 +820,17 @@ local nick = self._jid_nick[item.attr.jid]; if nick then item.attr.nick = select(3, jid_split(nick)); end end - local reason = item.tags[1] and item.tags[1].name == "reason" and #item.tags[1] == 1 and item.tags[1][1]; if item.attr.affiliation and item.attr.jid and not item.attr.role then - jid_affiliation[item.attr.jid] = { ["affiliation"] = item.attr.affiliation, ["reason"] = reason }; + local success, errtype, err = self:set_affiliation(actor, item.attr.jid, item.attr.affiliation, callback, reason); + if not success then origin.send(st.error_reply(stanza, errtype, err)); end elseif item.attr.role and item.attr.nick and not item.attr.affiliation then - jidnick_role[self.jid.."/"..item.attr.nick] = { ["role"] = item.attr.role, ["reason"] = reason }; + local success, errtype, err = self:set_role(actor, self.jid.."/"..item.attr.nick, item.attr.role, callback, reason); + if not success then origin.send(st.error_reply(stanza, errtype, err)); end else origin.send(st.error_reply(stanza, "cancel", "bad-request")); - return; end - end - - if not at_least_one_item_provided then - origin.send(st.error_reply(stanza, "cancel", "bad-request")); - return; - else - local can_set_affiliations, errtype_aff, err_aff = self:can_set_affiliations(actor, jid_affiliation) - local can_set_roles, errtype_role, err_role = self:can_set_roles(actor, jidnick_role) - - if can_set_affiliations and can_set_roles then - local nb_affiliation_changes = 0; - for _ in pairs(jid_affiliation) do nb_affiliation_changes = nb_affiliation_changes + 1; end - local nb_role_changes = 0; - for _ in pairs(jidnick_role) do nb_role_changes = nb_role_changes + 1; end - - if nb_affiliation_changes > 0 and nb_role_changes > 0 then - origin.send(st.error_reply(stanza, "cancel", "bad-request")); - end - if nb_affiliation_changes > 0 then - self:set_affiliations(actor, jid_affiliation, callback); - end - if nb_role_changes > 0 then - self:set_roles(actor, jidnick_role, callback); - end - else - if not can_set_affiliations then - origin.send(st.error_reply(stanza, errtype_aff, err_aff)); - elseif not can_set_roles then - origin.send(st.error_reply(stanza, errtype_role, err_role)); - else - origin.send(st.error_reply(stanza, "cancel", "bad-request")); - end - - return; - end - end - elseif type == "get" then - local item = stanza.tags[1].tags[1]; - if item and item.name == "item" then + elseif type == "get" then local _aff = item.attr.affiliation; local _rol = item.attr.role; if _aff and not _rol then @@ -933,9 +868,9 @@ else origin.send(st.error_reply(stanza, "cancel", "bad-request")); end - else - origin.send(st.error_reply(stanza, "cancel", "bad-request")); end + elseif type == "set" or type == "get" then + origin.send(st.error_reply(stanza, "cancel", "bad-request")); end elseif xmlns == "http://jabber.org/protocol/muc#owner" and (type == "get" or type == "set") and stanza.tags[1].name == "query" then if self:get_affiliation(stanza.attr.from) ~= "owner" then @@ -1063,132 +998,82 @@ if not result and self._affiliations[host] == "outcast" then result = "outcast"; end -- host banned return result; end ---- Checks whether the given affiliation changes in jid_affiliation can be applied by actor. --- Note: Empty tables can always be applied and won't have any effect. -function room_mt:can_set_affiliations(actor, jid_affiliation) - local actor_affiliation; - if actor ~= true then - actor_affiliation = self:get_affiliation(actor); +function room_mt:set_affiliation(actor, jid, affiliation, callback, reason) + jid = jid_bare(jid); + if affiliation == "none" then affiliation = nil; end + if affiliation and affiliation ~= "outcast" and affiliation ~= "owner" and affiliation ~= "admin" and affiliation ~= "member" then + return nil, "modify", "not-acceptable"; end - - -- First let's see if there are any problems with the affiliations given - -- in jid_affiliation - for jid, value in pairs(jid_affiliation) do - local affiliation = value["affiliation"]; - - if jid ~= jid_bare(jid) then - return false, "modify", "not-acceptable"; + if actor ~= true then + local actor_affiliation = self:get_affiliation(actor); + local target_affiliation = self:get_affiliation(jid); + if target_affiliation == affiliation then -- no change, shortcut + if callback then callback(); end + return true; end - jid = jid_bare(jid); - if affiliation == "none" then affiliation = nil; end - if affiliation and affiliation ~= "outcast" and affiliation ~= "owner" and affiliation ~= "admin" and affiliation ~= "member" then - return false, "modify", "not-acceptable"; - end - - local target_affiliation = self:get_affiliation(jid); - if target_affiliation == affiliation then - -- no change, no error checking necessary - else - if actor ~= true and actor_affiliation ~= "owner" then - if affiliation == "owner" or affiliation == "admin" or actor_affiliation ~= "admin" or target_affiliation == "owner" or target_affiliation == "admin" then - return false, "cancel", "not-allowed"; - end - elseif target_affiliation == "owner" and jid_bare(actor) == jid then -- self change - local is_last = true; - for j, aff in pairs(self._affiliations) do if j ~= jid and aff == "owner" then is_last = false; break; end end - if is_last then - return false, "cancel", "conflict"; - end + if actor_affiliation ~= "owner" then + if affiliation == "owner" or affiliation == "admin" or actor_affiliation ~= "admin" or target_affiliation == "owner" or target_affiliation == "admin" then + return nil, "cancel", "not-allowed"; + end + elseif target_affiliation == "owner" and jid_bare(actor) == jid then -- self change + local is_last = true; + for j, aff in pairs(self._affiliations) do if j ~= jid and aff == "owner" then is_last = false; break; end end + if is_last then + return nil, "cancel", "conflict"; end end end - - return true; -end ---- Updates the room affiliations by applying the ones given here. --- Takes the affiliations given in jid_affiliation and applies them to --- the room, overwriting a potentially existing affiliation for any given --- jid. --- @param jid_affiliation A table associating a jid with a table consisting --- of two subkeys: `affilation` and `reason`. The jids --- within must not be malformed. -function room_mt:set_affiliations(actor, jid_affiliation, callback) - local can_set, err_type, err_condition = self:can_set_affiliations(actor, jid_affiliation) - - if not can_set then - return false, err_type, err_condition; + self._affiliations[jid] = affiliation; + local role = self:get_default_role(affiliation); + local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user"}) + :tag("item", {affiliation=affiliation or "none", role=role or "none"}) + :tag("reason"):text(reason or ""):up() + :up(); + local presence_type = nil; + if not role then -- getting kicked + presence_type = "unavailable"; + if affiliation == "outcast" then + x:tag("status", {code="301"}):up(); -- banned + else + x:tag("status", {code="321"}):up(); -- affiliation change + end end -- Your own presence should have status 110 + local self_x = st.clone(x); + self_x:tag("status", {code="110"}); local modified_nicks = {}; - local nb_modified_nicks = 0; - -- Now we can be sure that jid_affiliation causes no problems - -- We can actually set them - for jid, value in pairs(jid_affiliation) do - local affiliation = value["affiliation"]; - local reason = value["reason"]; - - self._affiliations[jid] = affiliation; - local role = self:get_default_role(affiliation); - local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user"}) - :tag("item", {affiliation=affiliation or "none", role=role or "none"}) - :tag("reason"):text(reason or ""):up() - :up(); - local self_x = st.clone(x); - self_x:tag("status", {code="110"}):up(); - local presence_type = nil; - if not role then -- getting kicked - presence_type = "unavailable"; - if affiliation == "outcast" then - -- banned - x:tag("status", {code="301"}):up(); - self_x:tag("status", {code="301"}):up(); + for nick, occupant in pairs(self._occupants) do + if jid_bare(occupant.jid) == jid then + if not role then -- getting kicked + self._occupants[nick] = nil; else - -- affiliation change - x:tag("status", {code="321"}):up(); - self_x:tag("status", {code="321"}):up(); + occupant.affiliation, occupant.role = affiliation, role; end - end - for nick, occupant in pairs(self._occupants) do - if jid_bare(occupant.jid) == jid then - if not role then -- getting kicked - self._occupants[nick] = nil; - else - occupant.affiliation, occupant.role = affiliation, role; + for jid,pres in pairs(occupant.sessions) do -- remove for all sessions of the nick + if not role then self._jid_nick[jid] = nil; end + local p = st.clone(pres); + p.attr.from = nick; + p.attr.type = presence_type; + p.attr.to = jid; + if occupant.jid == jid then + -- Broadcast this presence to everyone else later, with the public variant + local bp = st.clone(p); + bp:add_child(x); + modified_nicks[nick] = bp; end - for jid,pres in pairs(occupant.sessions) do -- remove for all sessions of the nick - if not role then self._jid_nick[jid] = nil; end - local p = st.clone(pres); - p.attr.from = nick; - p.attr.type = presence_type; - p.attr.to = jid; - self:_route_stanza(p); - if occupant.jid == jid then - -- Broadcast this presence to everyone else later, with the public variant - local bp = st.clone(p); - bp:add_child(x); - modified_nicks[nick] = bp; - nb_modified_nicks = nb_modified_nicks + 1; - end - p:add_child(self_x); - self:_route_stanza(p); - end + p:add_child(self_x); + self:_route_stanza(p); end end end - - if nb_modified_nicks > 0 then - if self.save then self:save(); end - if callback then callback(); end - end + if self.save then self:save(); end + if callback then callback(); end for nick,p in pairs(modified_nicks) do p.attr.from = nick; self:broadcast_except_nick(p, nick); end return true; end -function room_mt:set_affiliation(actor, jid, affiliation, callback, reason) - return self.set_affiliations(actor, { [jid] = { ["affiliation"] = affiliation, ["reason"] = reason } }, callback) -end function room_mt:get_role(nick) local session = self._occupants[nick]; @@ -1212,82 +1097,45 @@ end return nil, "cancel", "not-allowed"; end - ---- Checks whether the given role changes in jidnick_role can be applied by actor. --- Note: Empty tables can always be applied and won't have any effect. -function room_mt:can_set_roles(actor, jidnick_role) - for jidnick, role_info in pairs(jidnick_role) do - local role = role_info["role"]; - if role == "none" then role = nil; end - if role and role ~= "moderator" and role ~= "participant" and role ~= "visitor" then return false, "modify", "not-acceptable"; end - local can_set, err_type, err_condition = self:can_set_role(actor, jidnick, role) - if not can_set then - return false, err_type, err_condition; - end - end - - return true; -end - ---- Updates the room roles by applying the ones given here. --- Takes the roles given in jidnick_role and applies them to --- the room, overwriting a potentially existing role for any given --- jid. --- @param jidnick_role A table associating a jid/nick with a table consisting --- of two subkeys: `role` and `reason`. The jids within --- must not be malformed. -function room_mt:set_roles(actor, jidnick_role, callback) - local allowed, err_type, err_condition = self:can_set_roles(actor, jidnick_role); +function room_mt:set_role(actor, occupant_jid, role, callback, reason) + if role == "none" then role = nil; end + if role and role ~= "moderator" and role ~= "participant" and role ~= "visitor" then return nil, "modify", "not-acceptable"; end + local allowed, err_type, err_condition = self:can_set_role(actor, occupant_jid, role); if not allowed then return allowed, err_type, err_condition; end - - local modified_nicks = {}; - local nb_modified_nicks = 0; - for jidnick, value in pairs(jidnick_role) do - local occupant_jid = jidnick; - local role = value["role"]; - local reason = value["reason"]; - - local occupant = self._occupants[occupant_jid]; - local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user"}) - :tag("item", {affiliation=occupant.affiliation or "none", nick=select(3, jid_split(occupant_jid)), role=role or "none"}) - :tag("reason"):text(reason or ""):up() - :up(); - local presence_type = nil; - if not role or role == "none" then -- kick - presence_type = "unavailable"; - self._occupants[occupant_jid] = nil; - for jid in pairs(occupant.sessions) do -- remove for all sessions of the nick - self._jid_nick[jid] = nil; - end - x:tag("status", {code = "307"}):up(); - else - occupant.role = role; + local occupant = self._occupants[occupant_jid]; + local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user"}) + :tag("item", {affiliation=occupant.affiliation or "none", nick=select(3, jid_split(occupant_jid)), role=role or "none"}) + :tag("reason"):text(reason or ""):up() + :up(); + local presence_type = nil; + if not role then -- kick + presence_type = "unavailable"; + self._occupants[occupant_jid] = nil; + for jid in pairs(occupant.sessions) do -- remove for all sessions of the nick + self._jid_nick[jid] = nil; end - local self_x = st.clone(x); - self_x:tag("status", {code = "110"}):up(); - local bp; - for jid,pres in pairs(occupant.sessions) do -- send to all sessions of the nick - local p = st.clone(pres); - p.attr.from = occupant_jid; - p.attr.type = presence_type; - p.attr.to = jid; - self:_route_stanza(p); - if occupant.jid == jid then - bp = st.clone(p); - bp:add_child(x); - modified_nicks[occupant_jid] = bp; - nb_modified_nicks = nb_modified_nicks + 1; - end - p:add_child(self_x); - self:route_stanza(p); + x:tag("status", {code = "307"}):up(); + else + occupant.role = role; + end + local self_x = st.clone(x); + self_x:tag("status", {code = "110"}):up(); + local bp; + for jid,pres in pairs(occupant.sessions) do -- send to all sessions of the nick + local p = st.clone(pres); + p.attr.from = occupant_jid; + p.attr.type = presence_type; + p.attr.to = jid; + if occupant.jid == jid then + bp = st.clone(p); + bp:add_child(x); end + p:add_child(self_x); + self:_route_stanza(p); end - - if nb_modified_nicks > 0 then - if callback then callback(); end - end - for nick,p in pairs(modified_nicks) do - self:broadcast_except_nick(p, nick); + if callback then callback(); end + if bp then + self:broadcast_except_nick(bp, occupant_jid); end return true; end @@ -1311,9 +1159,9 @@ for _, item in pairs(muc_child.tags) do if item.name == "item" then if from_occupant == to_occupant then - item.attr.jid = jid_bare(stanza.attr.to); + item.attr.jid = stanza.attr.to; else - item.attr.jid = jid_bare(from_occupant.jid); + item.attr.jid = from_occupant.jid; end end end