plugins/muc/muc.lib.lua
changeset 8845 463505cc75d5
parent 8842 dcd53a565c01
child 8846 041ddc670934
child 8890 c47f220580fd
equal deleted inserted replaced
8844:bc8558bbc797 8845:463505cc75d5
   169 
   169 
   170 		local maxchars = history_tag and tonumber(history_tag.attr.maxchars);
   170 		local maxchars = history_tag and tonumber(history_tag.attr.maxchars);
   171 		if maxchars then maxchars = math.floor(maxchars); end
   171 		if maxchars then maxchars = math.floor(maxchars); end
   172 
   172 
   173 		local maxstanzas = math.floor(history_tag and tonumber(history_tag.attr.maxstanzas) or #history);
   173 		local maxstanzas = math.floor(history_tag and tonumber(history_tag.attr.maxstanzas) or #history);
   174 		if not history_tag then maxstanzas = self._data.default_history_messages; end
   174 		if not history_tag then maxstanzas = 20; end
   175 
   175 
   176 		local seconds = history_tag and tonumber(history_tag.attr.seconds);
   176 		local seconds = history_tag and tonumber(history_tag.attr.seconds);
   177 		if seconds then seconds = datetime.datetime(os.time() - math.floor(seconds)); end
   177 		if seconds then seconds = datetime.datetime(os.time() - math.floor(seconds)); end
   178 
   178 
   179 		local since = history_tag and history_tag.attr.since;
   179 		local since = history_tag and history_tag.attr.since;
   356 	if length == default_history_length then
   356 	if length == default_history_length then
   357 		length = nil;
   357 		length = nil;
   358 	end
   358 	end
   359 	self._data.history_length = length;
   359 	self._data.history_length = length;
   360 end
   360 end
   361 
       
   362 -- Fix for clients who don't support XEP-0045 correctly
       
   363 -- Default number of history messages the room returns
       
   364 function room_mt:get_defaulthistorymessages()
       
   365         return self._data.default_history_messages or default_history_length;
       
   366 end
       
   367 function room_mt:set_defaulthistorymessages(number)
       
   368         number = math.min(tonumber(number) or default_history_length, self._data.history_length or default_history_length);
       
   369         if number == default_history_length then
       
   370                 number = nil;
       
   371         end
       
   372         self._data.default_history_messages = number;
       
   373 end
       
   374 
       
   375 
   361 
   376 
   362 
   377 local valid_whois = { moderators = true, anyone = true };
   363 local valid_whois = { moderators = true, anyone = true };
   378 
   364 
   379 function room_mt:set_whois(whois)
   365 function room_mt:set_whois(whois)
   710 		{
   696 		{
   711 			name = 'muc#roomconfig_historylength',
   697 			name = 'muc#roomconfig_historylength',
   712 			type = 'text-single',
   698 			type = 'text-single',
   713 			label = 'Maximum Number of History Messages Returned by Room',
   699 			label = 'Maximum Number of History Messages Returned by Room',
   714 			value = tostring(self:get_historylength())
   700 			value = tostring(self:get_historylength())
   715 		},
       
   716 		{
       
   717 			name = 'muc#roomconfig_defaulthistorymessages',
       
   718 			type = 'text-single',
       
   719 			label = 'Default Number of History Messages Returned by Room',
       
   720 			value = tostring(self:get_defaulthistorymessages())
       
   721 		}
   701 		}
   722 	});
   702 	});
   723 	return module:fire_event("muc-config-form", { room = self, actor = actor, form = form }) or form;
   703 	return module:fire_event("muc-config-form", { room = self, actor = actor, form = form }) or form;
   724 end
   704 end
   725 
   705 
   764 	handle_option("moderated", "muc#roomconfig_moderatedroom");
   744 	handle_option("moderated", "muc#roomconfig_moderatedroom");
   765 	handle_option("members_only", "muc#roomconfig_membersonly");
   745 	handle_option("members_only", "muc#roomconfig_membersonly");
   766 	handle_option("public", "muc#roomconfig_publicroom");
   746 	handle_option("public", "muc#roomconfig_publicroom");
   767 	handle_option("changesubject", "muc#roomconfig_changesubject");
   747 	handle_option("changesubject", "muc#roomconfig_changesubject");
   768 	handle_option("historylength", "muc#roomconfig_historylength");
   748 	handle_option("historylength", "muc#roomconfig_historylength");
   769 	handle_option("defaulthistorymessages", "muc#roomconfig_defaulthistorymessages");
       
   770 	handle_option("whois", "muc#roomconfig_whois", valid_whois);
   749 	handle_option("whois", "muc#roomconfig_whois", valid_whois);
   771 	handle_option("password", "muc#roomconfig_roomsecret");
   750 	handle_option("password", "muc#roomconfig_roomsecret");
   772 
   751 
   773 	if self.save then self:save(true); end
   752 	if self.save then self:save(true); end
   774 	if self.locked then
   753 	if self.locked then
   821 		elseif xmlns == "http://jabber.org/protocol/muc#admin" then
   800 		elseif xmlns == "http://jabber.org/protocol/muc#admin" then
   822 			local actor = stanza.attr.from;
   801 			local actor = stanza.attr.from;
   823 			local affiliation = self:get_affiliation(actor);
   802 			local affiliation = self:get_affiliation(actor);
   824 			local current_nick = self._jid_nick[actor];
   803 			local current_nick = self._jid_nick[actor];
   825 			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);
   826 			if type == "set" then
   805 			local item = stanza.tags[1].tags[1];
   827 				local at_least_one_item_provided = false;
   806 			if item and item.name == "item" then
   828 				local callback = function() origin.send(st.reply(stanza)); end
   807 				if type == "set" then
   829 
   808 					local callback = function() origin.send(st.reply(stanza)); end
   830 				-- Gather all changes to affiliations and roles
       
   831 				local jid_affiliation = {};
       
   832 				local jidnick_role = {};
       
   833 				for item in stanza.tags[1]:childtags("item") do
       
   834 					at_least_one_item_provided = true;
       
   835 
       
   836 					if item.attr.jid then -- Validate provided JID
   809 					if item.attr.jid then -- Validate provided JID
   837 						item.attr.jid = jid_prep(item.attr.jid);
   810 						item.attr.jid = jid_prep(item.attr.jid);
   838 						if not item.attr.jid then
   811 						if not item.attr.jid then
   839 							origin.send(st.error_reply(stanza, "modify", "jid-malformed"));
   812 							origin.send(st.error_reply(stanza, "modify", "jid-malformed"));
   840 							return;
   813 							return;
   845 						if occupant then item.attr.jid = occupant.jid; end
   818 						if occupant then item.attr.jid = occupant.jid; end
   846 					elseif not item.attr.nick and item.attr.jid then
   819 					elseif not item.attr.nick and item.attr.jid then
   847 						local nick = self._jid_nick[item.attr.jid];
   820 						local nick = self._jid_nick[item.attr.jid];
   848 						if nick then item.attr.nick = select(3, jid_split(nick)); end
   821 						if nick then item.attr.nick = select(3, jid_split(nick)); end
   849 					end
   822 					end
   850 
       
   851 					local reason = item.tags[1] and item.tags[1].name == "reason" and #item.tags[1] == 1 and item.tags[1][1];
   823 					local reason = item.tags[1] and item.tags[1].name == "reason" and #item.tags[1] == 1 and item.tags[1][1];
   852 					if item.attr.affiliation and item.attr.jid and not item.attr.role then
   824 					if item.attr.affiliation and item.attr.jid and not item.attr.role then
   853 						jid_affiliation[item.attr.jid] = { ["affiliation"] = item.attr.affiliation, ["reason"] = reason };
   825 						local success, errtype, err = self:set_affiliation(actor, item.attr.jid, item.attr.affiliation, callback, reason);
       
   826 						if not success then origin.send(st.error_reply(stanza, errtype, err)); end
   854 					elseif item.attr.role and item.attr.nick and not item.attr.affiliation then
   827 					elseif item.attr.role and item.attr.nick and not item.attr.affiliation then
   855 						jidnick_role[self.jid.."/"..item.attr.nick] = { ["role"] = item.attr.role, ["reason"] = reason };
   828 						local success, errtype, err = self:set_role(actor, self.jid.."/"..item.attr.nick, item.attr.role, callback, reason);
       
   829 						if not success then origin.send(st.error_reply(stanza, errtype, err)); end
   856 					else
   830 					else
   857 						origin.send(st.error_reply(stanza, "cancel", "bad-request"));
   831 						origin.send(st.error_reply(stanza, "cancel", "bad-request"));
   858 						return;
       
   859 					end
   832 					end
   860 				end
   833 				elseif type == "get" then
   861 
       
   862 				if not at_least_one_item_provided then
       
   863 					origin.send(st.error_reply(stanza, "cancel", "bad-request"));
       
   864 					return;
       
   865 				else
       
   866 					local can_set_affiliations, errtype_aff, err_aff = self:can_set_affiliations(actor, jid_affiliation)
       
   867 					local can_set_roles, errtype_role, err_role = self:can_set_roles(actor, jidnick_role)
       
   868 
       
   869 					if can_set_affiliations and can_set_roles then
       
   870 						local nb_affiliation_changes = 0;
       
   871 						for _ in pairs(jid_affiliation) do nb_affiliation_changes = nb_affiliation_changes + 1; end
       
   872 						local nb_role_changes = 0;
       
   873 						for _ in pairs(jidnick_role) do nb_role_changes = nb_role_changes + 1; end
       
   874 
       
   875 						if nb_affiliation_changes > 0 and nb_role_changes > 0 then
       
   876 							origin.send(st.error_reply(stanza, "cancel", "bad-request"));
       
   877 						end
       
   878 						if nb_affiliation_changes > 0 then
       
   879 							self:set_affiliations(actor, jid_affiliation, callback);
       
   880 						end
       
   881 						if nb_role_changes > 0 then
       
   882 							self:set_roles(actor, jidnick_role, callback);
       
   883 						end
       
   884 					else
       
   885 						if not can_set_affiliations then
       
   886 							origin.send(st.error_reply(stanza, errtype_aff, err_aff));
       
   887 						elseif not can_set_roles then
       
   888 							origin.send(st.error_reply(stanza, errtype_role, err_role));
       
   889 						else
       
   890 							origin.send(st.error_reply(stanza, "cancel", "bad-request"));
       
   891 						end
       
   892 
       
   893 						return;
       
   894 					end
       
   895 				end
       
   896 			elseif type == "get" then
       
   897 				local item = stanza.tags[1].tags[1];
       
   898 				if item and item.name == "item" then
       
   899 					local _aff = item.attr.affiliation;
   834 					local _aff = item.attr.affiliation;
   900 					local _rol = item.attr.role;
   835 					local _rol = item.attr.role;
   901 					if _aff and not _rol then
   836 					if _aff and not _rol then
   902 						if affiliation == "owner" or (affiliation == "admin" and _aff ~= "owner" and _aff ~= "admin")
   837 						if affiliation == "owner" or (affiliation == "admin" and _aff ~= "owner" and _aff ~= "admin")
   903 						or (affiliation and affiliation ~= "outcast" and self:get_members_only() and self:get_whois() == "anyone") then
   838 						or (affiliation and affiliation ~= "outcast" and self:get_members_only() and self:get_whois() == "anyone") then
   931 							origin.send(st.error_reply(stanza, "auth", "forbidden"));
   866 							origin.send(st.error_reply(stanza, "auth", "forbidden"));
   932 						end
   867 						end
   933 					else
   868 					else
   934 						origin.send(st.error_reply(stanza, "cancel", "bad-request"));
   869 						origin.send(st.error_reply(stanza, "cancel", "bad-request"));
   935 					end
   870 					end
   936 				else
   871 				end
   937 					origin.send(st.error_reply(stanza, "cancel", "bad-request"));
   872 			elseif type == "set" or type == "get" then
   938 				end
   873 				origin.send(st.error_reply(stanza, "cancel", "bad-request"));
   939 			end
   874 			end
   940 		elseif xmlns == "http://jabber.org/protocol/muc#owner" and (type == "get" or type == "set") and stanza.tags[1].name == "query" then
   875 		elseif xmlns == "http://jabber.org/protocol/muc#owner" and (type == "get" or type == "set") and stanza.tags[1].name == "query" then
   941 			if self:get_affiliation(stanza.attr.from) ~= "owner" then
   876 			if self:get_affiliation(stanza.attr.from) ~= "owner" then
   942 				origin.send(st.error_reply(stanza, "auth", "forbidden", "Only owners can configure rooms"));
   877 				origin.send(st.error_reply(stanza, "auth", "forbidden", "Only owners can configure rooms"));
   943 			elseif stanza.attr.type == "get" then
   878 			elseif stanza.attr.type == "get" then
  1061 	local bare = node and node.."@"..host or host;
   996 	local bare = node and node.."@"..host or host;
  1062 	local result = self._affiliations[bare]; -- Affiliations are granted, revoked, and maintained based on the user's bare JID.
   997 	local result = self._affiliations[bare]; -- Affiliations are granted, revoked, and maintained based on the user's bare JID.
  1063 	if not result and self._affiliations[host] == "outcast" then result = "outcast"; end -- host banned
   998 	if not result and self._affiliations[host] == "outcast" then result = "outcast"; end -- host banned
  1064 	return result;
   999 	return result;
  1065 end
  1000 end
  1066 --- Checks whether the given affiliation changes in jid_affiliation can be applied by actor.
  1001 function room_mt:set_affiliation(actor, jid, affiliation, callback, reason)
  1067 -- Note: Empty tables can always be applied and won't have any effect.
  1002 	jid = jid_bare(jid);
  1068 function room_mt:can_set_affiliations(actor, jid_affiliation)
  1003 	if affiliation == "none" then affiliation = nil; end
  1069 	local actor_affiliation;
  1004 	if affiliation and affiliation ~= "outcast" and affiliation ~= "owner" and affiliation ~= "admin" and affiliation ~= "member" then
       
  1005 		return nil, "modify", "not-acceptable";
       
  1006 	end
  1070 	if actor ~= true then
  1007 	if actor ~= true then
  1071 		actor_affiliation = self:get_affiliation(actor);
  1008 		local actor_affiliation = self:get_affiliation(actor);
  1072 	end
       
  1073 
       
  1074 	-- First let's see if there are any problems with the affiliations given
       
  1075 	-- in jid_affiliation
       
  1076 	for jid, value in pairs(jid_affiliation) do
       
  1077 		local affiliation = value["affiliation"];
       
  1078 
       
  1079 		if jid ~= jid_bare(jid) then
       
  1080 			return false, "modify", "not-acceptable";
       
  1081 		end
       
  1082 		jid = jid_bare(jid);
       
  1083 		if affiliation == "none" then affiliation = nil; end
       
  1084 		if affiliation and affiliation ~= "outcast" and affiliation ~= "owner" and affiliation ~= "admin" and affiliation ~= "member" then
       
  1085 			return false, "modify", "not-acceptable";
       
  1086 		end
       
  1087 
       
  1088 		local target_affiliation = self:get_affiliation(jid);
  1009 		local target_affiliation = self:get_affiliation(jid);
  1089 		if target_affiliation == affiliation then
  1010 		if target_affiliation == affiliation then -- no change, shortcut
  1090 			-- no change, no error checking necessary
  1011 			if callback then callback(); end
       
  1012 			return true;
       
  1013 		end
       
  1014 		if actor_affiliation ~= "owner" then
       
  1015 			if affiliation == "owner" or affiliation == "admin" or actor_affiliation ~= "admin" or target_affiliation == "owner" or target_affiliation == "admin" then
       
  1016 				return nil, "cancel", "not-allowed";
       
  1017 			end
       
  1018 		elseif target_affiliation == "owner" and jid_bare(actor) == jid then -- self change
       
  1019 			local is_last = true;
       
  1020 			for j, aff in pairs(self._affiliations) do if j ~= jid and aff == "owner" then is_last = false; break; end end
       
  1021 			if is_last then
       
  1022 				return nil, "cancel", "conflict";
       
  1023 			end
       
  1024 		end
       
  1025 	end
       
  1026 	self._affiliations[jid] = affiliation;
       
  1027 	local role = self:get_default_role(affiliation);
       
  1028 	local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user"})
       
  1029 			:tag("item", {affiliation=affiliation or "none", role=role or "none"})
       
  1030 				:tag("reason"):text(reason or ""):up()
       
  1031 			:up();
       
  1032 	local presence_type = nil;
       
  1033 	if not role then -- getting kicked
       
  1034 		presence_type = "unavailable";
       
  1035 		if affiliation == "outcast" then
       
  1036 			x:tag("status", {code="301"}):up(); -- banned
  1091 		else
  1037 		else
  1092 			if actor ~= true and actor_affiliation ~= "owner" then
  1038 			x:tag("status", {code="321"}):up(); -- affiliation change
  1093 				if affiliation == "owner" or affiliation == "admin" or actor_affiliation ~= "admin" or target_affiliation == "owner" or target_affiliation == "admin" then
  1039 		end
  1094 					return false, "cancel", "not-allowed";
       
  1095 				end
       
  1096 			elseif target_affiliation == "owner" and jid_bare(actor) == jid then -- self change
       
  1097 				local is_last = true;
       
  1098 				for j, aff in pairs(self._affiliations) do if j ~= jid and aff == "owner" then is_last = false; break; end end
       
  1099 				if is_last then
       
  1100 					return false, "cancel", "conflict";
       
  1101 				end
       
  1102 			end
       
  1103 		end
       
  1104 	end
       
  1105 
       
  1106 	return true;
       
  1107 end
       
  1108 --- Updates the room affiliations by applying the ones given here.
       
  1109 -- Takes the affiliations given in jid_affiliation and applies them to
       
  1110 -- the room, overwriting a potentially existing affiliation for any given
       
  1111 -- jid.
       
  1112 -- @param jid_affiliation A table associating a jid with a table consisting
       
  1113 --                        of two subkeys: `affilation` and `reason`. The jids
       
  1114 --                        within must not be malformed.
       
  1115 function room_mt:set_affiliations(actor, jid_affiliation, callback)
       
  1116 	local can_set, err_type, err_condition = self:can_set_affiliations(actor, jid_affiliation)
       
  1117 
       
  1118 	if not can_set then
       
  1119 		return false, err_type, err_condition;
       
  1120 	end
  1040 	end
  1121 	-- Your own presence should have status 110
  1041 	-- Your own presence should have status 110
       
  1042 	local self_x = st.clone(x);
       
  1043 	self_x:tag("status", {code="110"});
  1122 	local modified_nicks = {};
  1044 	local modified_nicks = {};
  1123 	local nb_modified_nicks = 0;
  1045 	for nick, occupant in pairs(self._occupants) do
  1124 	-- Now we can be sure that jid_affiliation causes no problems
  1046 		if jid_bare(occupant.jid) == jid then
  1125 	-- We can actually set them
  1047 			if not role then -- getting kicked
  1126 	for jid, value in pairs(jid_affiliation) do
  1048 				self._occupants[nick] = nil;
  1127 		local affiliation = value["affiliation"];
       
  1128 		local reason = value["reason"];
       
  1129 
       
  1130 		self._affiliations[jid] = affiliation;
       
  1131 		local role = self:get_default_role(affiliation);
       
  1132 		local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user"})
       
  1133 				:tag("item", {affiliation=affiliation or "none", role=role or "none"})
       
  1134 					:tag("reason"):text(reason or ""):up()
       
  1135 				:up();
       
  1136 		local self_x = st.clone(x);
       
  1137 		self_x:tag("status", {code="110"}):up();
       
  1138 		local presence_type = nil;
       
  1139 		if not role then -- getting kicked
       
  1140 			presence_type = "unavailable";
       
  1141 			if affiliation == "outcast" then
       
  1142 				-- banned
       
  1143 				x:tag("status", {code="301"}):up();
       
  1144 				self_x:tag("status", {code="301"}):up();
       
  1145 			else
  1049 			else
  1146 				-- affiliation change
  1050 				occupant.affiliation, occupant.role = affiliation, role;
  1147 				x:tag("status", {code="321"}):up();
  1051 			end
  1148 				self_x:tag("status", {code="321"}):up();
  1052 			for jid,pres in pairs(occupant.sessions) do -- remove for all sessions of the nick
  1149 			end
  1053 				if not role then self._jid_nick[jid] = nil; end
  1150 		end
  1054 				local p = st.clone(pres);
  1151 		for nick, occupant in pairs(self._occupants) do
  1055 				p.attr.from = nick;
  1152 			if jid_bare(occupant.jid) == jid then
  1056 				p.attr.type = presence_type;
  1153 				if not role then -- getting kicked
  1057 				p.attr.to = jid;
  1154 					self._occupants[nick] = nil;
  1058 				if occupant.jid == jid then
  1155 				else
       
  1156 					occupant.affiliation, occupant.role = affiliation, role;
       
  1157 				end
       
  1158 				for jid,pres in pairs(occupant.sessions) do -- remove for all sessions of the nick
       
  1159 					if not role then self._jid_nick[jid] = nil; end
       
  1160 					local p = st.clone(pres);
       
  1161 					p.attr.from = nick;
       
  1162 					p.attr.type = presence_type;
       
  1163 					p.attr.to = jid;
       
  1164 					self:_route_stanza(p);
       
  1165 					if occupant.jid == jid then
       
  1166 					-- Broadcast this presence to everyone else later, with the public <x> variant
  1059 					-- Broadcast this presence to everyone else later, with the public <x> variant
  1167 						local bp = st.clone(p);
  1060 					local bp = st.clone(p);
  1168 						bp:add_child(x);
  1061 					bp:add_child(x);
  1169 						modified_nicks[nick] = bp;
  1062 					modified_nicks[nick] = bp;
  1170 						nb_modified_nicks = nb_modified_nicks + 1;
  1063 				end
  1171 					end
  1064 				p:add_child(self_x);
  1172 					p:add_child(self_x);
  1065 				self:_route_stanza(p);
  1173 					self:_route_stanza(p);
  1066 			end
  1174 				end
  1067 		end
  1175 			end
  1068 	end
  1176 		end
  1069 	if self.save then self:save(); end
  1177 	end
  1070 	if callback then callback(); end
  1178 
       
  1179 	if nb_modified_nicks > 0 then
       
  1180 		if self.save then self:save(); end
       
  1181 		if callback then callback(); end
       
  1182 	end
       
  1183 	for nick,p in pairs(modified_nicks) do
  1071 	for nick,p in pairs(modified_nicks) do
  1184 		p.attr.from = nick;
  1072 		p.attr.from = nick;
  1185 		self:broadcast_except_nick(p, nick);
  1073 		self:broadcast_except_nick(p, nick);
  1186 	end
  1074 	end
  1187 	return true;
  1075 	return true;
  1188 end
       
  1189 function room_mt:set_affiliation(actor, jid, affiliation, callback, reason)
       
  1190 	return self.set_affiliations(actor, { [jid] = { ["affiliation"] = affiliation, ["reason"] = reason } }, callback)
       
  1191 end
  1076 end
  1192 
  1077 
  1193 function room_mt:get_role(nick)
  1078 function room_mt:get_role(nick)
  1194 	local session = self._occupants[nick];
  1079 	local session = self._occupants[nick];
  1195 	return session and session.role or nil;
  1080 	return session and session.role or nil;
  1210 			end
  1095 			end
  1211 		end
  1096 		end
  1212 	end
  1097 	end
  1213 	return nil, "cancel", "not-allowed";
  1098 	return nil, "cancel", "not-allowed";
  1214 end
  1099 end
  1215 
  1100 function room_mt:set_role(actor, occupant_jid, role, callback, reason)
  1216 --- Checks whether the given role changes in jidnick_role can be applied by actor.
  1101 	if role == "none" then role = nil; end
  1217 -- Note: Empty tables can always be applied and won't have any effect.
  1102 	if role and role ~= "moderator" and role ~= "participant" and role ~= "visitor" then return nil, "modify", "not-acceptable"; end
  1218 function room_mt:can_set_roles(actor, jidnick_role)
  1103 	local allowed, err_type, err_condition = self:can_set_role(actor, occupant_jid, role);
  1219 	for jidnick, role_info in pairs(jidnick_role) do
       
  1220 		local role = role_info["role"];
       
  1221 		if role == "none" then role = nil; end
       
  1222 		if role and role ~= "moderator" and role ~= "participant" and role ~= "visitor" then return false, "modify", "not-acceptable"; end
       
  1223 		local can_set, err_type, err_condition = self:can_set_role(actor, jidnick, role)
       
  1224 		if not can_set then
       
  1225 			return false, err_type, err_condition;
       
  1226 		end
       
  1227 	end
       
  1228 
       
  1229 	return true;
       
  1230 end
       
  1231 
       
  1232 --- Updates the room roles by applying the ones given here.
       
  1233 -- Takes the roles given in jidnick_role and applies them to
       
  1234 -- the room, overwriting a potentially existing role for any given
       
  1235 -- jid.
       
  1236 -- @param jidnick_role A table associating a jid/nick with a table consisting
       
  1237 --                     of two subkeys: `role` and `reason`. The jids within
       
  1238 --                     must not be malformed.
       
  1239 function room_mt:set_roles(actor, jidnick_role, callback)
       
  1240 	local allowed, err_type, err_condition = self:can_set_roles(actor, jidnick_role);
       
  1241 	if not allowed then return allowed, err_type, err_condition; end
  1104 	if not allowed then return allowed, err_type, err_condition; end
  1242 
  1105 	local occupant = self._occupants[occupant_jid];
  1243 	local modified_nicks = {};
  1106 	local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user"})
  1244 	local nb_modified_nicks = 0;
  1107 			:tag("item", {affiliation=occupant.affiliation or "none", nick=select(3, jid_split(occupant_jid)), role=role or "none"})
  1245 	for jidnick, value in pairs(jidnick_role) do
  1108 				:tag("reason"):text(reason or ""):up()
  1246 		local occupant_jid = jidnick;
  1109 			:up();
  1247 		local role = value["role"];
  1110 	local presence_type = nil;
  1248 		local reason = value["reason"];
  1111 	if not role then -- kick
  1249 
  1112 		presence_type = "unavailable";
  1250 		local occupant = self._occupants[occupant_jid];
  1113 		self._occupants[occupant_jid] = nil;
  1251 		local x = st.stanza("x", {xmlns = "http://jabber.org/protocol/muc#user"})
  1114 		for jid in pairs(occupant.sessions) do -- remove for all sessions of the nick
  1252 				:tag("item", {affiliation=occupant.affiliation or "none", nick=select(3, jid_split(occupant_jid)), role=role or "none"})
  1115 			self._jid_nick[jid] = nil;
  1253 					:tag("reason"):text(reason or ""):up()
  1116 		end
  1254 				:up();
  1117 		x:tag("status", {code = "307"}):up();
  1255 		local presence_type = nil;
  1118 	else
  1256 		if not role or role == "none" then -- kick
  1119 		occupant.role = role;
  1257 			presence_type = "unavailable";
  1120 	end
  1258 			self._occupants[occupant_jid] = nil;
  1121 	local self_x = st.clone(x);
  1259 			for jid in pairs(occupant.sessions) do -- remove for all sessions of the nick
  1122 	self_x:tag("status", {code = "110"}):up();
  1260 				self._jid_nick[jid] = nil;
  1123 	local bp;
  1261 			end
  1124 	for jid,pres in pairs(occupant.sessions) do -- send to all sessions of the nick
  1262 			x:tag("status", {code = "307"}):up();
  1125 		local p = st.clone(pres);
  1263 		else
  1126 		p.attr.from = occupant_jid;
  1264 			occupant.role = role;
  1127 		p.attr.type = presence_type;
  1265 		end
  1128 		p.attr.to = jid;
  1266 		local self_x = st.clone(x);
  1129 		if occupant.jid == jid then
  1267 		self_x:tag("status", {code = "110"}):up();
  1130 			bp = st.clone(p);
  1268 		local bp;
  1131 			bp:add_child(x);
  1269 		for jid,pres in pairs(occupant.sessions) do -- send to all sessions of the nick
  1132 		end
  1270 			local p = st.clone(pres);
  1133 		p:add_child(self_x);
  1271 			p.attr.from = occupant_jid;
  1134 		self:_route_stanza(p);
  1272 			p.attr.type = presence_type;
  1135 	end
  1273 			p.attr.to = jid;
  1136 	if callback then callback(); end
  1274 			self:_route_stanza(p);
  1137 	if bp then
  1275 			if occupant.jid == jid then
  1138 		self:broadcast_except_nick(bp, occupant_jid);
  1276 				bp = st.clone(p);
       
  1277 				bp:add_child(x);
       
  1278 				modified_nicks[occupant_jid] = bp;
       
  1279 				nb_modified_nicks = nb_modified_nicks + 1;
       
  1280 			end
       
  1281 			p:add_child(self_x);
       
  1282 			self:route_stanza(p);
       
  1283 		end
       
  1284 	end
       
  1285 
       
  1286 	if nb_modified_nicks > 0 then
       
  1287 		if callback then callback(); end
       
  1288 	end
       
  1289 	for nick,p in pairs(modified_nicks) do
       
  1290 		self:broadcast_except_nick(p, nick);
       
  1291 	end
  1139 	end
  1292 	return true;
  1140 	return true;
  1293 end
  1141 end
  1294 
  1142 
  1295 function room_mt:_route_stanza(stanza)
  1143 function room_mt:_route_stanza(stanza)
  1309 	end
  1157 	end
  1310 	if muc_child then
  1158 	if muc_child then
  1311 		for _, item in pairs(muc_child.tags) do
  1159 		for _, item in pairs(muc_child.tags) do
  1312 			if item.name == "item" then
  1160 			if item.name == "item" then
  1313 				if from_occupant == to_occupant then
  1161 				if from_occupant == to_occupant then
  1314 					item.attr.jid = jid_bare(stanza.attr.to);
  1162 					item.attr.jid = stanza.attr.to;
  1315 				else
  1163 				else
  1316 					item.attr.jid = jid_bare(from_occupant.jid);
  1164 					item.attr.jid = from_occupant.jid;
  1317 				end
  1165 				end
  1318 			end
  1166 			end
  1319 		end
  1167 		end
  1320 	end
  1168 	end
  1321 	self:route_stanza(stanza);
  1169 	self:route_stanza(stanza);