plugins/mod_muc.lua

changeset 809
09d6b5fadc84
parent 782
6f9b2a9d6d45
child 810
863046d84b56
equal deleted inserted replaced
808:28d6515f5b7b 809:09d6b5fadc84
39 rooms_info:set(room, datamanager.store(room, muc_domain, 'rooms') or nil); 39 rooms_info:set(room, datamanager.store(room, muc_domain, 'rooms') or nil);
40 end 40 end
41 41
42 local component; 42 local component;
43 43
44 function filter_xmlns_from_array(array, filters)
45 local count = 0;
46 for i=#array,1,-1 do
47 local attr = array[i].attr;
48 if filters[attr and attr.xmlns] then
49 t_remove(array, i);
50 count = count + 1;
51 end
52 end
53 return count;
54 end
55 function filter_xmlns_from_stanza(stanza, filters)
56 if filters then
57 if filter_xmlns_from_array(stanza.tags, filters) ~= 0 then
58 return stanza, filter_xmlns_from_array(stanza, filters);
59 end
60 end
61 return stanza, 0;
62 end
63 local presence_filters = {["http://jabber.org/protocol/muc"]=true;["http://jabber.org/protocol/muc#user"]=true};
64 function get_filtered_presence(stanza)
65 return filter_xmlns_from_stanza(st.deserialize(st.preserialize(stanza)), presence_filters);
66 end
44 function getUsingPath(stanza, path, getText) 67 function getUsingPath(stanza, path, getText)
45 local tag = stanza; 68 local tag = stanza;
46 for _, name in ipairs(path) do 69 for _, name in ipairs(path) do
47 if type(tag) ~= 'table' then return; end 70 if type(tag) ~= 'table' then return; end
48 tag = tag:child_with_name(name); 71 tag = tag:child_with_name(name);
91 function set_subject(current_nick, room, subject) 114 function set_subject(current_nick, room, subject)
92 -- TODO check nick's authority 115 -- TODO check nick's authority
93 if subject == "" then subject = nil; end 116 if subject == "" then subject = nil; end
94 rooms_info:set(room, 'subject', subject); 117 rooms_info:set(room, 'subject', subject);
95 save_room(); 118 save_room();
96 broadcast_message(current_nick, room, subject or "", nil); 119 local msg = st.message({type='groupchat', from=from})
120 :tag('subject'):text(subject):up();
121 broadcast_message_stanza(room, msg, false);
122 --broadcast_message(current_nick, room, subject or "", nil);
97 return true; 123 return true;
98 end 124 end
99 125
100 function broadcast_presence(type, from, room, code, newnick) 126 function broadcast_presence(type, from, room, code, newnick)
101 local data = rooms:get(room, from); 127 local data = rooms:get(room, from);
142 t_insert(history, st.preserialize(stanza)); 168 t_insert(history, st.preserialize(stanza));
143 while #history > history_length do t_remove(history, 1) end 169 while #history > history_length do t_remove(history, 1) end
144 end 170 end
145 end 171 end
146 end 172 end
173 function broadcast_message_stanza(room, stanza, historic)
174 local r = rooms:get(room);
175 if r then
176 for occupant, o_data in pairs(r) do
177 for jid in pairs(o_data.sessions) do
178 stanza.attr.to = jid;
179 core_route_stanza(component, stanza);
180 end
181 end
182 if historic then -- add to history
183 local history = rooms_info:get(room, 'history');
184 if not history then history = {}; rooms_info:set(room, 'history', history); end
185 -- stanza = st.deserialize(st.preserialize(stanza));
186 stanza:tag("delay", {xmlns = "urn:xmpp:delay", from = muc_domain, stamp = datetime.datetime()}):up(); -- XEP-0203
187 stanza:tag("x", {xmlns = "jabber:x:delay", from = muc_domain, stamp = datetime.legacy()}):up(); -- XEP-0091 (deprecated)
188 t_insert(history, st.preserialize(stanza));
189 while #history > history_length do t_remove(history, 1) end
190 end
191 end
192 end
193 function broadcast_presence_stanza(room, stanza, code, nick)
194 stanza = get_filtered_presence(stanza);
195 local data = rooms:get(room, stanza.attr.from);
196 stanza:tag("x", {xmlns='http://jabber.org/protocol/muc#user'})
197 :tag("item", {affiliation=data.affiliation, role=data.role, nick=nick}):up();
198 if code then
199 stanza:tag("status", {code=code}):up();
200 end
201 local me;
202 local r = rooms:get(room);
203 if r then
204 for occupant, o_data in pairs(r) do
205 if occupant ~= stanza.attr.from then
206 for jid in pairs(o_data.sessions) do
207 stanza.attr.to = jid;
208 core_route_stanza(component, stanza);
209 end
210 else
211 me = o_data;
212 end
213 end
214 end
215 if me then
216 stanza:tag("status", {code='110'});
217 for jid in pairs(me.sessions) do
218 stanza.attr.to = jid;
219 core_route_stanza(component, stanza);
220 end
221 end
222 end
147 223
148 function handle_to_occupant(origin, stanza) -- PM, vCards, etc 224 function handle_to_occupant(origin, stanza) -- PM, vCards, etc
149 local from, to = stanza.attr.from, stanza.attr.to; 225 local from, to = stanza.attr.from, stanza.attr.to;
150 local room = jid_bare(to); 226 local room = jid_bare(to);
151 local current_nick = jid_nick:get(from, room); 227 local current_nick = jid_nick:get(from, room);
152 local type = stanza.attr.type; 228 local type = stanza.attr.type;
153 if stanza.name == "presence" then 229 if stanza.name == "presence" then
230 local pr = get_filtered_presence(stanza);
231 pr.attr.from = to;
154 if type == "error" then -- error, kick em out! 232 if type == "error" then -- error, kick em out!
155 local data = rooms:get(room, to); 233 if current_nick then
156 data.role = 'none';
157 broadcast_presence('unavailable', to, room); -- TODO also add <status>This participant is kicked from the room because he sent an error presence: badformed error stanza</status>
158 rooms:remove(room, to);
159 jid_nick:remove(from, room);
160 elseif type == "unavailable" then -- unavailable
161 if current_nick == to then
162 local data = rooms:get(room, to); 234 local data = rooms:get(room, to);
163 data.role = 'none'; 235 data.role = 'none';
164 broadcast_presence('unavailable', to, room); 236 local pr = st.presence({type='unavailable', from=current_nick}):tag('status'):text('This participant is kicked from the room because he sent an error presence'):up()
237 :tag("x", {xmlns='http://jabber.org/protocol/muc#user'})
238 :tag("item", {affiliation=data.affiliation, role=data.role}):up();
239 broadcast_presence_stanza(room, pr);
240 --broadcast_presence('unavailable', to, room); -- TODO also add <status>This participant is kicked from the room because he sent an error presence: badformed error stanza</status>
165 rooms:remove(room, to); 241 rooms:remove(room, to);
166 jid_nick:remove(from, room); 242 jid_nick:remove(from, room);
167 end -- TODO else do nothing? 243 end
244 elseif type == "unavailable" then -- unavailable
245 if current_nick then
246 local data = rooms:get(room, to);
247 data.role = 'none';
248 broadcast_presence_stanza(room, pr);
249 --broadcast_presence('unavailable', to, room);
250 rooms:remove(room, to);
251 jid_nick:remove(from, room);
252 end
168 elseif not type then -- available 253 elseif not type then -- available
169 if current_nick then 254 if current_nick then
170 if current_nick == to then -- simple presence 255 if current_nick == to then -- simple presence
171 -- TODO broadcast 256 broadcast_presence_stanza(room, pr);
257 -- FIXME check if something was filtered. if it was, then user may be rejoining
172 else -- change nick 258 else -- change nick
173 if rooms:get(room, to) then 259 if rooms:get(room, to) then
174 origin.send(st.error_reply(stanza, "cancel", "conflict")); 260 origin.send(st.error_reply(stanza, "cancel", "conflict"));
175 else 261 else
176 local data = rooms:get(room, current_nick); 262 local data = rooms:get(room, current_nick);
177 local to_nick = select(3, jid_split(to)); 263 local to_nick = select(3, jid_split(to));
178 if to_nick then 264 if to_nick then
179 broadcast_presence('unavailable', current_nick, room, '303', to_nick); 265 local p = st.presence({type='unavailable', from=current_nick});
266 --[[:tag('x', {xmlns='http://jabber.org/protocol/muc#user'})
267 :tag('item', {affiliation=data.affiliation, role=data.role, nick=to_nick}):up()
268 :tag('status', {code='303'});]]
269 broadcast_presence_stanza(room, p, '303', to_nick);
270 --broadcast_presence('unavailable', current_nick, room, '303', to_nick);
180 rooms:remove(room, current_nick); 271 rooms:remove(room, current_nick);
181 rooms:set(room, to, data); 272 rooms:set(room, to, data);
182 jid_nick:set(from, room, to); 273 jid_nick:set(from, room, to);
183 broadcast_presence(nil, to, room, nil); 274 broadcast_presence_stanza(room, pr);
275 --broadcast_presence(nil, to, room, nil);
184 else 276 else
185 --TODO: malformed-jid 277 --TODO malformed-jid
186 end 278 end
187 end 279 end
188 end 280 end
189 else -- enter room 281 else -- enter room
282 local new_nick = to;
190 if rooms:get(room, to) then 283 if rooms:get(room, to) then
284 new_nick = nil;
285 end
286 if not new_nick then
191 origin.send(st.error_reply(stanza, "cancel", "conflict")); 287 origin.send(st.error_reply(stanza, "cancel", "conflict"));
192 else 288 else
193 local data; 289 local data;
194 if not rooms:get(room) and not rooms_info:get(room) then -- new room 290 if not rooms:get(room) and not rooms_info:get(room) then -- new room
195 data = {affiliation='owner', role='moderator', jid=from}; 291 data = {affiliation='owner', role='moderator', jid=from, sessions={[from]=get_filtered_presence(stanza)}};
196 end 292 end
197 if not data then -- new occupant 293 if not data then -- new occupant
198 data = {affiliation='none', role='participant', jid=from}; 294 data = {affiliation='none', role='participant', jid=from, sessions={[from]=get_filtered_presence(stanza)}};
199 end 295 end
200 rooms:set(room, to, data); 296 rooms:set(room, to, data);
201 jid_nick:set(from, room, to); 297 jid_nick:set(from, room, to);
202 local r = rooms:get(room); 298 local r = rooms:get(room);
203 if r then 299 if r then
204 for occupant, o_data in pairs(r) do 300 for occupant, o_data in pairs(r) do
205 if occupant ~= from then 301 if occupant ~= from then
206 local pres = st.presence({to=from, from=occupant}) 302 local pres = get_filtered_presence(o_data.sessions[o_data.jid]);
303 pres.attr.to, pres.attr.from = from, occupant;
304 pres
305 --local pres = st.presence({to=from, from=occupant})
207 :tag("x", {xmlns='http://jabber.org/protocol/muc#user'}) 306 :tag("x", {xmlns='http://jabber.org/protocol/muc#user'})
208 :tag("item", {affiliation=o_data.affiliation, role=o_data.role}):up(); 307 :tag("item", {affiliation=o_data.affiliation, role=o_data.role}):up();
209 core_route_stanza(component, pres); 308 core_route_stanza(component, pres);
210 end 309 end
211 end 310 end
212 end 311 end
213 broadcast_presence(nil, to, room); 312 broadcast_presence_stanza(room, pr);
313 --broadcast_presence(nil, to, room);
214 local history = rooms_info:get(room, 'history'); -- send discussion history 314 local history = rooms_info:get(room, 'history'); -- send discussion history
215 if history then 315 if history then
216 for _, msg in ipairs(history) do 316 for _, msg in ipairs(history) do
217 msg = st.deserialize(msg); 317 msg = st.deserialize(msg);
218 msg.attr.to=from; 318 msg.attr.to=from;
251 local room = jid_bare(to); 351 local room = jid_bare(to);
252 local current_nick = jid_nick:get(from, room); 352 local current_nick = jid_nick:get(from, room);
253 if not current_nick then -- not in room 353 if not current_nick then -- not in room
254 origin.send(st.error_reply(stanza, "cancel", "not-acceptable")); 354 origin.send(st.error_reply(stanza, "cancel", "not-acceptable"));
255 else 355 else
356 local from = stanza.attr.from;
357 stanza.attr.from = current_nick;
256 local subject = getText(stanza, {"subject"}); 358 local subject = getText(stanza, {"subject"});
257 if subject then 359 if subject then
258 set_subject(current_nick, room, subject); 360 set_subject(current_nick, room, subject); -- TODO use broadcast_message_stanza
259 else 361 else
260 broadcast_message(current_nick, room, nil, getText(stanza, {"body"})); 362 --broadcast_message(current_nick, room, nil, getText(stanza, {"body"}));
261 -- TODO add to discussion history 363 broadcast_message_stanza(room, stanza, true);
262 end 364 end
365 end
366 elseif stanza.name == "presence" then -- hack - some buggy clients send presence updates to the room rather than their nick
367 local to = stanza.attr.to;
368 local current_nick = jid_nick:get(stanza.attr.from, to);
369 if current_nick then
370 stanza.attr.to = current_nick;
371 handle_to_occupant(origin, stanza);
372 stanza.attr.to = to;
373 else
374 origin.send(st.error_reply(stanza, "cancel", "service-unavailable"));
263 end 375 end
264 else 376 else
265 if type == "error" or type == "result" then return; end 377 if type == "error" or type == "result" then return; end
266 origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); 378 origin.send(st.error_reply(stanza, "cancel", "service-unavailable"));
267 end 379 end
284 end 396 end
285 end 397 end
286 398
287 register_component(muc_domain, function(origin, stanza) 399 register_component(muc_domain, function(origin, stanza)
288 local to_node, to_host, to_resource = jid_split(stanza.attr.to); 400 local to_node, to_host, to_resource = jid_split(stanza.attr.to);
289 if stanza.name == "presence" and stanza.attr.type ~= nil and stanza.attr.type ~= "unavailable" then 401 if to_resource and not to_node then
290 if type == "error" or type == "result" then return; end
291 origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); -- FIXME what's appropriate?
292 elseif to_resource and not to_node then
293 if type == "error" or type == "result" then return; end 402 if type == "error" or type == "result" then return; end
294 origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); -- host/resource 403 origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); -- host/resource
295 elseif to_resource then 404 elseif to_resource then
296 handle_to_occupant(origin, stanza); 405 handle_to_occupant(origin, stanza);
297 elseif to_node then 406 elseif to_node then

mercurial