Software /
code /
prosody
Comparison
plugins/muc/muc.lib.lua @ 8792:c2b99fa134b3
MUC: Import revised, more comprehensive patch for 8da11142fabf (#345)
author | Matthew Wild <mwild1@gmail.com> |
---|---|
date | Wed, 16 May 2018 12:14:21 +0100 |
parent | 8791:8da11142fabf |
child | 8793:05a3275b6873 |
comparison
equal
deleted
inserted
replaced
8791:8da11142fabf | 8792:c2b99fa134b3 |
---|---|
802 local affiliation = self:get_affiliation(actor); | 802 local affiliation = self:get_affiliation(actor); |
803 local current_nick = self._jid_nick[actor]; | 803 local current_nick = self._jid_nick[actor]; |
804 local role = current_nick and self._occupants[current_nick].role or self:get_default_role(affiliation); | 804 local role = current_nick and self._occupants[current_nick].role or self:get_default_role(affiliation); |
805 if type == "set" then | 805 if type == "set" then |
806 local at_least_one_item_provided = false; | 806 local at_least_one_item_provided = false; |
807 | 807 local callback = function() origin.send(st.reply(stanza)); end |
808 | |
809 -- Gather all changes to affiliations and roles | |
810 local jid_affiliation = {}; | |
811 local jidnick_role = {}; | |
808 for item in stanza.tags[1]:childtags("item") do | 812 for item in stanza.tags[1]:childtags("item") do |
809 at_least_one_item_provided = true; | 813 at_least_one_item_provided = true; |
810 | 814 |
811 local callback = function() origin.send(st.reply(stanza)); end | |
812 if item.attr.jid then -- Validate provided JID | 815 if item.attr.jid then -- Validate provided JID |
813 item.attr.jid = jid_prep(item.attr.jid); | 816 item.attr.jid = jid_prep(item.attr.jid); |
814 if not item.attr.jid then | 817 if not item.attr.jid then |
815 origin.send(st.error_reply(stanza, "modify", "jid-malformed")); | 818 origin.send(st.error_reply(stanza, "modify", "jid-malformed")); |
819 return; | |
816 end | 820 end |
817 end | 821 end |
818 if not item.attr.jid and item.attr.nick then -- COMPAT Workaround for Miranda sending 'nick' instead of 'jid' when changing affiliation | 822 if not item.attr.jid and item.attr.nick then -- COMPAT Workaround for Miranda sending 'nick' instead of 'jid' when changing affiliation |
819 local occupant = self._occupants[self.jid.."/"..item.attr.nick]; | 823 local occupant = self._occupants[self.jid.."/"..item.attr.nick]; |
820 if occupant then item.attr.jid = occupant.jid; end | 824 if occupant then item.attr.jid = occupant.jid; end |
821 elseif not item.attr.nick and item.attr.jid then | 825 elseif not item.attr.nick and item.attr.jid then |
822 local nick = self._jid_nick[item.attr.jid]; | 826 local nick = self._jid_nick[item.attr.jid]; |
823 if nick then item.attr.nick = select(3, jid_split(nick)); end | 827 if nick then item.attr.nick = select(3, jid_split(nick)); end |
824 end | 828 end |
829 | |
825 local reason = item.tags[1] and item.tags[1].name == "reason" and #item.tags[1] == 1 and item.tags[1][1]; | 830 local reason = item.tags[1] and item.tags[1].name == "reason" and #item.tags[1] == 1 and item.tags[1][1]; |
826 if item.attr.affiliation and item.attr.jid and not item.attr.role then | 831 if item.attr.affiliation and item.attr.jid and not item.attr.role then |
827 local success, errtype, err = self:set_affiliation(actor, item.attr.jid, item.attr.affiliation, callback, reason); | 832 jid_affiliation[item.attr.jid] = { ["affiliation"] = item.attr.affiliation, ["reason"] = reason }; |
828 if not success then origin.send(st.error_reply(stanza, errtype, err)); end | |
829 elseif item.attr.role and item.attr.nick and not item.attr.affiliation then | 833 elseif item.attr.role and item.attr.nick and not item.attr.affiliation then |
830 local success, errtype, err = self:set_role(actor, self.jid.."/"..item.attr.nick, item.attr.role, callback, reason); | 834 jidnick_role[item.attr.jid.."/"..item.attr.nick] = { ["role"] = item.attr.role, ["reason"] = reason }; |
831 if not success then origin.send(st.error_reply(stanza, errtype, err)); end | |
832 else | 835 else |
833 origin.send(st.error_reply(stanza, "cancel", "bad-request")); | 836 origin.send(st.error_reply(stanza, "cancel", "bad-request")); |
834 return; | 837 return; |
835 end | 838 end |
836 end | 839 end |
837 | 840 |
838 if not at_least_one_item_provided then | 841 if not at_least_one_item_provided then |
839 origin.send(st.error_reply(stanza, "cancel", "bad-request")); | 842 origin.send(st.error_reply(stanza, "cancel", "bad-request")); |
840 return; | 843 return; |
844 else | |
845 local can_set_affiliations, errtype_aff, err_aff = self:can_set_affiliations(actor, jid_affiliation) | |
846 local can_set_roles, errtype_role, err_role = self:can_set_roles(actor, jidnick_role) | |
847 | |
848 if can_set_affiliations and can_set_roles then | |
849 local nb_affiliation_changes = 0; | |
850 for _ in pairs(jid_affiliation) do nb_affiliation_changes = nb_affiliation_changes + 1; end | |
851 local nb_role_changes = 0; | |
852 for _ in pairs(jidnick_role) do nb_role_changes = nb_role_changes + 1; end | |
853 | |
854 if nb_affiliation_changes > 0 and nb_role_changes > 0 then | |
855 origin.send(st.error_reply(stanza, "cancel", "bad-request")); | |
856 end | |
857 if nb_affiliation_changes > 0 then | |
858 self:set_affiliations(actor, jid_affiliation, callback); | |
859 end | |
860 if nb_role_changes > 0 then | |
861 self:set_roles(actor, jidnick_role, callback); | |
862 end | |
863 else | |
864 if not can_set_affiliations then | |
865 origin.send(st.error_reply(stanza, errtype_aff, err_aff)); | |
866 elseif not can_set_roles then | |
867 origin.send(st.error_reply(stanza, errtype_role, err_role)); | |
868 else | |
869 origin.send(st.error_reply(stanza, "cancel", "bad-request")); | |
870 end | |
871 | |
872 return; | |
873 end | |
841 end | 874 end |
842 elseif type == "get" then | 875 elseif type == "get" then |
843 local item = stanza.tags[1].tags[1]; | 876 local item = stanza.tags[1].tags[1]; |
844 if item and item.name == "item" then | 877 if item and item.name == "item" then |
845 local _aff = item.attr.affiliation; | 878 local _aff = item.attr.affiliation; |
1007 local bare = node and node.."@"..host or host; | 1040 local bare = node and node.."@"..host or host; |
1008 local result = self._affiliations[bare]; -- Affiliations are granted, revoked, and maintained based on the user's bare JID. | 1041 local result = self._affiliations[bare]; -- Affiliations are granted, revoked, and maintained based on the user's bare JID. |
1009 if not result and self._affiliations[host] == "outcast" then result = "outcast"; end -- host banned | 1042 if not result and self._affiliations[host] == "outcast" then result = "outcast"; end -- host banned |
1010 return result; | 1043 return result; |
1011 end | 1044 end |
1012 function room_mt:set_affiliation(actor, jid, affiliation, callback, reason) | 1045 --- Checks whether the given affiliation changes in jid_affiliation can be applied by actor. |
1013 jid = jid_bare(jid); | 1046 -- Note: Empty tables can always be applied and won't have any effect. |
1014 if affiliation == "none" then affiliation = nil; end | 1047 function room_mt:can_set_affiliations(actor, jid_affiliation) |
1015 if affiliation and affiliation ~= "outcast" and affiliation ~= "owner" and affiliation ~= "admin" and affiliation ~= "member" then | 1048 local actor_affiliation; |
1016 return nil, "modify", "not-acceptable"; | |
1017 end | |
1018 if actor ~= true then | 1049 if actor ~= true then |
1019 local actor_affiliation = self:get_affiliation(actor); | 1050 actor_affiliation = self:get_affiliation(actor); |
1051 end | |
1052 | |
1053 -- First let's see if there are any problems with the affiliations given | |
1054 -- in jid_affiliation | |
1055 for jid, value in pairs(jid_affiliation) do | |
1056 local affiliation = value["affiliation"]; | |
1057 | |
1058 jid = jid_bare(jid); | |
1059 if affiliation == "none" then affiliation = nil; end | |
1060 if affiliation and affiliation ~= "outcast" and affiliation ~= "owner" and affiliation ~= "admin" and affiliation ~= "member" then | |
1061 return false, "modify", "not-acceptable"; | |
1062 end | |
1063 | |
1020 local target_affiliation = self:get_affiliation(jid); | 1064 local target_affiliation = self:get_affiliation(jid); |
1021 if target_affiliation == affiliation then -- no change, shortcut | 1065 if target_affiliation == affiliation then |
1022 if callback then callback(); end | 1066 -- no change, no error checking necessary |
1023 return true; | |
1024 end | |
1025 if actor_affiliation ~= "owner" then | |
1026 if affiliation == "owner" or affiliation == "admin" or actor_affiliation ~= "admin" or target_affiliation == "owner" or target_affiliation == "admin" then | |
1027 return nil, "cancel", "not-allowed"; | |
1028 end | |
1029 elseif target_affiliation == "owner" and jid_bare(actor) == jid then -- self change | |
1030 local is_last = true; | |
1031 for j, aff in pairs(self._affiliations) do if j ~= jid and aff == "owner" then is_last = false; break; end end | |
1032 if is_last then | |
1033 return nil, "cancel", "conflict"; | |
1034 end | |
1035 end | |
1036 end | |
1037 self._affiliations[jid] = affiliation; | |
1038 local role = self:get_default_role(affiliation); | |
1039 local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user"}) | |
1040 :tag("item", {affiliation=affiliation or "none", role=role or "none"}) | |
1041 :tag("reason"):text(reason or ""):up() | |
1042 :up(); | |
1043 local presence_type = nil; | |
1044 if not role then -- getting kicked | |
1045 presence_type = "unavailable"; | |
1046 if affiliation == "outcast" then | |
1047 x:tag("status", {code="301"}):up(); -- banned | |
1048 else | 1067 else |
1049 x:tag("status", {code="321"}):up(); -- affiliation change | 1068 if actor ~= true and actor_affiliation ~= "owner" then |
1050 end | 1069 if affiliation == "owner" or affiliation == "admin" or actor_affiliation ~= "admin" or target_affiliation == "owner" or target_affiliation == "admin" then |
1070 return false, "cancel", "not-allowed"; | |
1071 end | |
1072 elseif target_affiliation == "owner" and jid_bare(actor) == jid then -- self change | |
1073 local is_last = true; | |
1074 for j, aff in pairs(self._affiliations) do if j ~= jid and aff == "owner" then is_last = false; break; end end | |
1075 if is_last then | |
1076 return false, "cancel", "conflict"; | |
1077 end | |
1078 end | |
1079 end | |
1080 end | |
1081 | |
1082 return true; | |
1083 end | |
1084 --- Updates the room affiliations by applying the ones given here. | |
1085 -- Takes the affiliations given in jid_affiliation and applies them to | |
1086 -- the room, overwriting a potentially existing affiliation for any given | |
1087 -- jid. | |
1088 -- @param jid_affiliation A table associating a jid with a table consisting | |
1089 -- of two subkeys: `affilation` and `reason`. The jids | |
1090 -- within must not be malformed. | |
1091 function room_mt:set_affiliations(actor, jid_affiliation, callback) | |
1092 local can_set, err_type, err_condition = self:can_set_affiliations(actor, jid_affiliation) | |
1093 | |
1094 if not can_set then | |
1095 return false, err_type, err_condition; | |
1051 end | 1096 end |
1052 -- Your own presence should have status 110 | 1097 -- Your own presence should have status 110 |
1053 local self_x = st.clone(x); | |
1054 self_x:tag("status", {code="110"}); | |
1055 local modified_nicks = {}; | 1098 local modified_nicks = {}; |
1056 for nick, occupant in pairs(self._occupants) do | 1099 local nb_modified_nicks = 0; |
1057 if jid_bare(occupant.jid) == jid then | 1100 -- Now we can be sure that jid_affiliation causes no problems |
1058 if not role then -- getting kicked | 1101 -- We can actually set them |
1059 self._occupants[nick] = nil; | 1102 for jid, value in pairs(jid_affiliation) do |
1103 local affiliation = value["affiliation"]; | |
1104 local reason = value["reason"]; | |
1105 | |
1106 self._affiliations[jid] = affiliation; | |
1107 local role = self:get_default_role(affiliation); | |
1108 local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user"}) | |
1109 :tag("item", {affiliation=affiliation or "none", role=role or "none"}) | |
1110 :tag("reason"):text(reason or ""):up() | |
1111 :up(); | |
1112 local self_x = st.clone(x); | |
1113 self_x:tag("status", {code="110"}); | |
1114 local presence_type = nil; | |
1115 if not role then -- getting kicked | |
1116 presence_type = "unavailable"; | |
1117 if affiliation == "outcast" then | |
1118 x:tag("status", {code="301"}):up(); -- banned | |
1060 else | 1119 else |
1061 occupant.affiliation, occupant.role = affiliation, role; | 1120 x:tag("status", {code="321"}):up(); -- affiliation change |
1062 end | 1121 end |
1063 for jid,pres in pairs(occupant.sessions) do -- remove for all sessions of the nick | 1122 end |
1064 if not role then self._jid_nick[jid] = nil; end | 1123 for nick, occupant in pairs(self._occupants) do |
1065 local p = st.clone(pres); | 1124 if jid_bare(occupant.jid) == jid then |
1066 p.attr.from = nick; | 1125 if not role then -- getting kicked |
1067 p.attr.type = presence_type; | 1126 self._occupants[nick] = nil; |
1068 p.attr.to = jid; | 1127 else |
1069 if occupant.jid == jid then | 1128 occupant.affiliation, occupant.role = affiliation, role; |
1129 end | |
1130 for jid,pres in pairs(occupant.sessions) do -- remove for all sessions of the nick | |
1131 if not role then self._jid_nick[jid] = nil; end | |
1132 local p = st.clone(pres); | |
1133 p.attr.from = nick; | |
1134 p.attr.type = presence_type; | |
1135 p.attr.to = jid; | |
1136 self:_route_stanza(p); | |
1137 if occupant.jid == jid then | |
1070 -- Broadcast this presence to everyone else later, with the public <x> variant | 1138 -- Broadcast this presence to everyone else later, with the public <x> variant |
1071 local bp = st.clone(p); | 1139 local bp = st.clone(p); |
1072 bp:add_child(x); | 1140 bp:add_child(x); |
1073 modified_nicks[nick] = bp; | 1141 modified_nicks[nick] = bp; |
1074 end | 1142 nb_modified_nicks = nb_modified_nicks + 1; |
1075 p:add_child(self_x); | 1143 end |
1076 self:_route_stanza(p); | 1144 p:add_child(self_x); |
1077 end | 1145 self:_route_stanza(p); |
1078 end | 1146 end |
1079 end | 1147 end |
1080 if self.save then self:save(); end | 1148 end |
1081 if callback then callback(); end | 1149 end |
1150 | |
1151 if nb_modified_nicks > 0 then | |
1152 if self.save then self:save(); end | |
1153 if callback then callback(); end | |
1154 end | |
1082 for nick,p in pairs(modified_nicks) do | 1155 for nick,p in pairs(modified_nicks) do |
1083 p.attr.from = nick; | 1156 p.attr.from = nick; |
1084 self:broadcast_except_nick(p, nick); | 1157 self:broadcast_except_nick(p, nick); |
1085 end | 1158 end |
1086 return true; | 1159 return true; |
1160 end | |
1161 function room_mt:set_affiliation(actor, jid, affiliation, callback, reason) | |
1162 return self.set_affiliations(actor, { [jid] = { ["affiliation"] = affiliation, ["reason"] = reason } }, callback) | |
1087 end | 1163 end |
1088 | 1164 |
1089 function room_mt:get_role(nick) | 1165 function room_mt:get_role(nick) |
1090 local session = self._occupants[nick]; | 1166 local session = self._occupants[nick]; |
1091 return session and session.role or nil; | 1167 return session and session.role or nil; |
1106 end | 1182 end |
1107 end | 1183 end |
1108 end | 1184 end |
1109 return nil, "cancel", "not-allowed"; | 1185 return nil, "cancel", "not-allowed"; |
1110 end | 1186 end |
1111 function room_mt:set_role(actor, occupant_jid, role, callback, reason) | 1187 |
1112 if role == "none" then role = nil; end | 1188 --- Checks whether the given role changes in jidnick_role can be applied by actor. |
1113 if role and role ~= "moderator" and role ~= "participant" and role ~= "visitor" then return nil, "modify", "not-acceptable"; end | 1189 -- Note: Empty tables can always be applied and won't have any effect. |
1114 local allowed, err_type, err_condition = self:can_set_role(actor, occupant_jid, role); | 1190 function room_mt:can_set_roles(actor, jidnick_role) |
1191 for jidnick, role in pairs(jidnick_role) do | |
1192 if role == "none" then role = nil; end | |
1193 if role and role ~= "moderator" and role ~= "participant" and role ~= "visitor" then return false, "modify", "not-acceptable"; end | |
1194 local can_set, err_type, err_condition = self:can_set_role(actor, jidnick, role) | |
1195 if not can_set then | |
1196 return false, err_type, err_condition; | |
1197 end | |
1198 end | |
1199 | |
1200 return true; | |
1201 end | |
1202 | |
1203 --- Updates the room roles by applying the ones given here. | |
1204 -- Takes the roles given in jidnick_role and applies them to | |
1205 -- the room, overwriting a potentially existing role for any given | |
1206 -- jid. | |
1207 -- @param jidnick_role A table associating a jid/nick with a table consisting | |
1208 -- of two subkeys: `role` and `reason`. The jids within | |
1209 -- must not be malformed. | |
1210 function room_mt:set_roles(actor, jidnick_role, callback) | |
1211 local allowed, err_type, err_condition = self:can_set_roles(actor, jidnick_role); | |
1115 if not allowed then return allowed, err_type, err_condition; end | 1212 if not allowed then return allowed, err_type, err_condition; end |
1116 local occupant = self._occupants[occupant_jid]; | 1213 |
1117 local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user"}) | 1214 local modified_nicks = {}; |
1118 :tag("item", {affiliation=occupant.affiliation or "none", nick=select(3, jid_split(occupant_jid)), role=role or "none"}) | 1215 local nb_modified_nicks = 0; |
1119 :tag("reason"):text(reason or ""):up() | 1216 for jidnick, value in pairs(jidnick_role) do |
1120 :up(); | 1217 local occupant_jid = jidnick; |
1121 local presence_type = nil; | 1218 local role = value["role"]; |
1122 if not role then -- kick | 1219 local reason = value["reason"]; |
1123 presence_type = "unavailable"; | 1220 |
1124 self._occupants[occupant_jid] = nil; | 1221 local occupant = self._occupants[occupant_jid]; |
1125 for jid in pairs(occupant.sessions) do -- remove for all sessions of the nick | 1222 local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user"}) |
1126 self._jid_nick[jid] = nil; | 1223 :tag("item", {affiliation=occupant.affiliation or "none", nick=select(3, jid_split(occupant_jid)), role=role or "none"}) |
1127 end | 1224 :tag("reason"):text(reason or ""):up() |
1128 x:tag("status", {code = "307"}):up(); | 1225 :up(); |
1129 else | 1226 local presence_type = nil; |
1130 occupant.role = role; | 1227 if not role then -- kick |
1131 end | 1228 presence_type = "unavailable"; |
1132 local self_x = st.clone(x); | 1229 self._occupants[occupant_jid] = nil; |
1133 self_x:tag("status", {code = "110"}):up(); | 1230 for jid in pairs(occupant.sessions) do -- remove for all sessions of the nick |
1134 local bp; | 1231 self._jid_nick[jid] = nil; |
1135 for jid,pres in pairs(occupant.sessions) do -- send to all sessions of the nick | 1232 end |
1136 local p = st.clone(pres); | 1233 x:tag("status", {code = "307"}):up(); |
1137 p.attr.from = occupant_jid; | 1234 else |
1138 p.attr.type = presence_type; | 1235 occupant.role = role; |
1139 p.attr.to = jid; | 1236 end |
1140 if occupant.jid == jid then | 1237 local self_x = st.clone(x); |
1141 bp = st.clone(p); | 1238 self_x:tag("status", {code = "110"}):up(); |
1142 bp:add_child(x); | 1239 local bp; |
1143 end | 1240 for jid,pres in pairs(occupant.sessions) do -- send to all sessions of the nick |
1144 p:add_child(self_x); | 1241 local p = st.clone(pres); |
1145 self:_route_stanza(p); | 1242 p.attr.from = occupant_jid; |
1146 end | 1243 p.attr.type = presence_type; |
1147 if callback then callback(); end | 1244 p.attr.to = jid; |
1148 if bp then | 1245 self:_route_stanza(p); |
1149 self:broadcast_except_nick(bp, occupant_jid); | 1246 if occupant.jid == jid then |
1247 bp = st.clone(p); | |
1248 bp:add_child(x); | |
1249 modified_nicks[occupant_jid] = p; | |
1250 nb_modified_nicks = nb_modified_nicks + 1; | |
1251 end | |
1252 p:add_child(self_x); | |
1253 self:route_stanza(p); | |
1254 end | |
1255 end | |
1256 | |
1257 if nb_modified_nicks > 0 then | |
1258 if callback then callback(); end | |
1259 end | |
1260 for nick,p in pairs(modified_nicks) do | |
1261 self:broadcast_except_nick(p, nick); | |
1150 end | 1262 end |
1151 return true; | 1263 return true; |
1152 end | 1264 end |
1153 | 1265 |
1154 function room_mt:_route_stanza(stanza) | 1266 function room_mt:_route_stanza(stanza) |