Comparison

plugins/muc/muc.lib.lua @ 6215:1dd09dc04945

plugins/muc: Move history to an external module This resulted in the split up of the main muc-occupant-joined event handler into 3 seperate ones, handling occupant list, history and subject
author daurnimator <quae@daurnimator.com>
date Thu, 03 Apr 2014 15:14:52 -0400
parent 6214:9813c74ce006
child 6216:50b8b7caf200
comparison
equal deleted inserted replaced
6214:9813c74ce006 6215:1dd09dc04945
9 9
10 local select = select; 10 local select = select;
11 local pairs, ipairs = pairs, ipairs; 11 local pairs, ipairs = pairs, ipairs;
12 local next = next; 12 local next = next;
13 local setmetatable = setmetatable; 13 local setmetatable = setmetatable;
14 local t_insert, t_remove = table.insert, table.remove;
15
16 local gettime = os.time;
17 local datetime = require "util.datetime";
18 14
19 local dataform = require "util.dataforms"; 15 local dataform = require "util.dataforms";
20
21 local jid_split = require "util.jid".split; 16 local jid_split = require "util.jid".split;
22 local jid_bare = require "util.jid".bare; 17 local jid_bare = require "util.jid".bare;
23 local jid_prep = require "util.jid".prep; 18 local jid_prep = require "util.jid".prep;
24 local st = require "util.stanza"; 19 local st = require "util.stanza";
25 local log = require "util.logger".init("mod_muc"); 20 local log = require "util.logger".init("mod_muc");
26 local base64 = require "util.encodings".base64; 21 local base64 = require "util.encodings".base64;
27 local md5 = require "util.hashes".md5; 22 local md5 = require "util.hashes".md5;
28 23
29 local occupant_lib = module:require "muc/occupant" 24 local occupant_lib = module:require "muc/occupant"
30 25
31 local default_history_length, max_history_length = 20, math.huge;
32 26
33 local is_kickable_error do 27 local is_kickable_error do
34 local kickable_error_conditions = { 28 local kickable_error_conditions = {
35 ["gone"] = true; 29 ["gone"] = true;
36 ["internal-server-error"] = true; 30 ["internal-server-error"] = true;
183 end 177 end
184 end 178 end
185 return x 179 return x
186 end 180 end
187 181
188 function room_mt:broadcast_message(stanza, historic) 182 function room_mt:broadcast_message(stanza)
189 module:fire_event("muc-broadcast-message", {room = self, stanza = stanza, historic = historic}); 183 module:fire_event("muc-broadcast-message", {room = self, stanza = stanza});
190 self:broadcast(stanza); 184 self:broadcast(stanza);
191 end 185 end
192
193 -- add to history
194 module:hook("muc-broadcast-message", function(event)
195 if event.historic then
196 local room = event.room
197 local history = room._data['history'];
198 if not history then history = {}; room._data['history'] = history; end
199 local stanza = st.clone(event.stanza);
200 stanza.attr.to = "";
201 local ts = gettime();
202 local stamp = datetime.datetime(ts);
203 stanza:tag("delay", {xmlns = "urn:xmpp:delay", from = module.host, stamp = stamp}):up(); -- XEP-0203
204 stanza:tag("x", {xmlns = "jabber:x:delay", from = module.host, stamp = datetime.legacy()}):up(); -- XEP-0091 (deprecated)
205 local entry = { stanza = stanza, timestamp = ts };
206 t_insert(history, entry);
207 while #history > room:get_historylength() do t_remove(history, 1) end
208 end
209 end);
210 186
211 -- Broadcast a stanza to all occupants in the room. 187 -- Broadcast a stanza to all occupants in the room.
212 -- optionally checks conditional called with (nick, occupant) 188 -- optionally checks conditional called with (nick, occupant)
213 function room_mt:broadcast(stanza, cond_func) 189 function room_mt:broadcast(stanza, cond_func)
214 for nick, occupant in self:each_occupant() do 190 for nick, occupant in self:each_occupant() do
310 self:route_stanza(pres); 286 self:route_stanza(pres);
311 end 287 end
312 end 288 end
313 end 289 end
314 290
315 local function parse_history(stanza)
316 local x_tag = stanza:get_child("x", "http://jabber.org/protocol/muc");
317 local history_tag = x_tag and x_tag:get_child("history", "http://jabber.org/protocol/muc");
318 if not history_tag then
319 return nil, 20, nil
320 end
321
322 local maxchars = tonumber(history_tag.attr.maxchars);
323
324 local maxstanzas = tonumber(history_tag.attr.maxstanzas);
325
326 -- messages received since the UTC datetime specified
327 local since = history_tag.attr.since;
328 if since then
329 since = datetime.parse(since);
330 end
331
332 -- messages received in the last "X" seconds.
333 local seconds = tonumber(history_tag.attr.seconds);
334 if seconds then
335 seconds = gettime() - seconds
336 if since then
337 since = math.max(since, seconds);
338 else
339 since = seconds;
340 end
341 end
342
343 return maxchars, maxstanzas, since
344 end
345
346 module:hook("muc-get-history", function(event)
347 local room = event.room
348 local history = room._data['history']; -- send discussion history
349 if not history then return nil end
350 local history_len = #history
351
352 local to = event.to
353 local maxchars = event.maxchars
354 local maxstanzas = event.maxstanzas or history_len
355 local since = event.since
356 local n = 0;
357 local charcount = 0;
358 for i=history_len,1,-1 do
359 local entry = history[i];
360 if maxchars then
361 if not entry.chars then
362 entry.stanza.attr.to = "";
363 entry.chars = #tostring(entry.stanza);
364 end
365 charcount = charcount + entry.chars + #to;
366 if charcount > maxchars then break; end
367 end
368 if since and since > entry.timestamp then break; end
369 if n + 1 > maxstanzas then break; end
370 n = n + 1;
371 end
372
373 local i = history_len-n+1
374 function event:next_stanza()
375 if i > history_len then return nil end
376 local entry = history[i]
377 local msg = entry.stanza
378 msg.attr.to = to;
379 i = i + 1
380 return msg
381 end
382 return true;
383 end);
384
385 function room_mt:send_history(stanza)
386 local maxchars, maxstanzas, since = parse_history(stanza)
387 local event = {
388 room = self;
389 to = stanza.attr.from; -- `to` is required to calculate the character count for `maxchars`
390 maxchars = maxchars, maxstanzas = maxstanzas, since = since;
391 next_stanza = function() end; -- events should define this iterator
392 }
393 module:fire_event("muc-get-history", event)
394 for msg in event.next_stanza , event do
395 self:route_stanza(msg);
396 end
397 end
398
399 function room_mt:get_disco_info(stanza) 291 function room_mt:get_disco_info(stanza)
400 local reply = st.reply(stanza):query("http://jabber.org/protocol/disco#info"); 292 local reply = st.reply(stanza):query("http://jabber.org/protocol/disco#info");
401 local form = dataform.new { 293 local form = dataform.new {
402 { name = "FORM_TYPE", type = "hidden", value = "http://jabber.org/protocol/muc#roominfo" }; 294 { name = "FORM_TYPE", type = "hidden", value = "http://jabber.org/protocol/muc#roominfo" };
403 }; 295 };
449 if subject == "" then subject = nil; end 341 if subject == "" then subject = nil; end
450 self._data['subject'] = subject; 342 self._data['subject'] = subject;
451 self._data['subject_from'] = current_nick; 343 self._data['subject_from'] = current_nick;
452 if self.save then self:save(); end 344 if self.save then self:save(); end
453 local msg = create_subject_message(current_nick, subject); 345 local msg = create_subject_message(current_nick, subject);
454 self:broadcast_message(msg, false); 346 self:broadcast_message(msg);
455 return true; 347 return true;
456 end 348 end
457 349
458 function room_mt:handle_kickable(origin, stanza) 350 function room_mt:handle_kickable(origin, stanza)
459 local real_jid = stanza.attr.from; 351 local real_jid = stanza.attr.from;
527 end 419 end
528 end 420 end
529 function room_mt:get_changesubject() 421 function room_mt:get_changesubject()
530 return self._data.changesubject; 422 return self._data.changesubject;
531 end 423 end
532 function room_mt:get_historylength()
533 return self._data.history_length or default_history_length;
534 end
535 function room_mt:set_historylength(length)
536 length = math.min(tonumber(length) or default_history_length, max_history_length or math.huge);
537 if length == default_history_length then
538 length = nil;
539 end
540 self._data.history_length = length;
541 end
542 424
543 -- Give the room creator owner affiliation 425 -- Give the room creator owner affiliation
544 module:hook("muc-room-pre-create", function(event) 426 module:hook("muc-room-pre-create", function(event)
545 event.room:set_affiliation(true, jid_bare(event.stanza.attr.from), "owner"); 427 event.room:set_affiliation(true, jid_bare(event.stanza.attr.from), "owner");
546 end, -1); 428 end, -1);
567 event.origin.send(reply:tag("x", {xmlns = "http://jabber.org/protocol/muc"})); 449 event.origin.send(reply:tag("x", {xmlns = "http://jabber.org/protocol/muc"}));
568 return true; 450 return true;
569 end 451 end
570 end, -10); 452 end, -10);
571 453
454 -- Send occupant list to newly joined user
572 module:hook("muc-occupant-joined", function(event) 455 module:hook("muc-occupant-joined", function(event)
573 local room, stanza = event.room, event.stanza; 456 local real_jid = event.stanza.attr.from;
574 local real_jid = stanza.attr.from; 457 event.room:send_occupant_list(real_jid, function(nick, occupant)
575 room:send_occupant_list(real_jid, function(nick, occupant)
576 -- Don't include self 458 -- Don't include self
577 return occupant:get_presence(real_jid) == nil; 459 return occupant:get_presence(real_jid) == nil;
578 end); 460 end);
579 room:send_history(stanza); 461 end, 80);
580 room:send_subject(real_jid); 462
581 end, -1); 463 -- Send subject to joining user
464 module:hook("muc-occupant-joined", function(event)
465 event.room:send_subject(event.stanza.attr.from);
466 end, 20);
582 467
583 function room_mt:handle_presence_to_occupant(origin, stanza) 468 function room_mt:handle_presence_to_occupant(origin, stanza)
584 local type = stanza.attr.type; 469 local type = stanza.attr.type;
585 if type == "error" then -- error, kick em out! 470 if type == "error" then -- error, kick em out!
586 return self:handle_kickable(origin, stanza) 471 return self:handle_kickable(origin, stanza)
869 type = 'boolean', 754 type = 'boolean',
870 label = 'Make Room Members-Only?', 755 label = 'Make Room Members-Only?',
871 value = event.room:get_members_only() 756 value = event.room:get_members_only()
872 }); 757 });
873 end); 758 end);
874 module:hook("muc-config-form", function(event)
875 table.insert(event.form, {
876 name = 'muc#roomconfig_historylength',
877 type = 'text-single',
878 label = 'Maximum Number of History Messages Returned by Room',
879 value = tostring(event.room:get_historylength())
880 });
881 end);
882 759
883 function room_mt:process_form(origin, stanza) 760 function room_mt:process_form(origin, stanza)
884 local form = stanza.tags[1]:get_child("x", "jabber:x:data"); 761 local form = stanza.tags[1]:get_child("x", "jabber:x:data");
885 if form.attr.type == "cancel" then 762 if form.attr.type == "cancel" then
886 origin.send(st.reply(stanza)); 763 origin.send(st.reply(stanza));
911 :tag('x', {xmlns='http://jabber.org/protocol/muc#user'}) 788 :tag('x', {xmlns='http://jabber.org/protocol/muc#user'})
912 for code in pairs(event.status_codes) do 789 for code in pairs(event.status_codes) do
913 msg:tag("status", {code = code;}):up(); 790 msg:tag("status", {code = code;}):up();
914 end 791 end
915 msg:up(); 792 msg:up();
916 self:broadcast_message(msg, false) 793 self:broadcast_message(msg);
917 end 794 end
918 else 795 else
919 origin.send(st.error_reply(stanza, "cancel", "bad-request", "Not a submitted form")); 796 origin.send(st.error_reply(stanza, "cancel", "bad-request", "Not a submitted form"));
920 end 797 end
921 return true; 798 return true;
932 module:hook("muc-config-submitted", function(event) 809 module:hook("muc-config-submitted", function(event)
933 event.update_option("public", "muc#roomconfig_publicroom"); 810 event.update_option("public", "muc#roomconfig_publicroom");
934 end); 811 end);
935 module:hook("muc-config-submitted", function(event) 812 module:hook("muc-config-submitted", function(event)
936 event.update_option("changesubject", "muc#roomconfig_changesubject"); 813 event.update_option("changesubject", "muc#roomconfig_changesubject");
937 end);
938 module:hook("muc-config-submitted", function(event)
939 event.update_option("historylength", "muc#roomconfig_historylength");
940 end); 814 end);
941 815
942 -- Removes everyone from the room 816 -- Removes everyone from the room
943 function room_mt:clear(x) 817 function room_mt:clear(x)
944 x = x or st.stanza("x", {xmlns='http://jabber.org/protocol/muc#user'}); 818 x = x or st.stanza("x", {xmlns='http://jabber.org/protocol/muc#user'});
1097 else 971 else
1098 stanza.attr.from = from; 972 stanza.attr.from = from;
1099 origin.send(st.error_reply(stanza, "auth", "forbidden")); 973 origin.send(st.error_reply(stanza, "auth", "forbidden"));
1100 end 974 end
1101 else 975 else
1102 self:broadcast_message(stanza, self:get_historylength() > 0 and stanza:get_child("body")); 976 self:broadcast_message(stanza);
1103 end 977 end
1104 stanza.attr.from = from; 978 stanza.attr.from = from;
1105 return true; 979 return true;
1106 end 980 end
1107 end 981 end
1410 1284
1411 local whois = module:require "muc/whois"; 1285 local whois = module:require "muc/whois";
1412 room_mt.get_whois = whois.get; 1286 room_mt.get_whois = whois.get;
1413 room_mt.set_whois = whois.set; 1287 room_mt.set_whois = whois.set;
1414 1288
1289 local history = module:require "muc/history";
1290 room_mt.send_history = history.send;
1291 room_mt.get_historylength = history.get_length;
1292 room_mt.set_historylength = history.set_length;
1293
1415 local _M = {}; -- module "muc" 1294 local _M = {}; -- module "muc"
1295
1296 _M.set_max_history_length = history.set_max_length;
1416 1297
1417 function _M.new_room(jid, config) 1298 function _M.new_room(jid, config)
1418 return setmetatable({ 1299 return setmetatable({
1419 jid = jid; 1300 jid = jid;
1420 _jid_nick = {}; 1301 _jid_nick = {};
1421 _occupants = {}; 1302 _occupants = {};
1422 _data = { 1303 _data = {
1423 history_length = math.min((config and config.history_length)
1424 or default_history_length, max_history_length);
1425 }; 1304 };
1426 _affiliations = {}; 1305 _affiliations = {};
1427 }, room_mt); 1306 }, room_mt);
1428 end 1307 end
1429 1308
1430 function _M.set_max_history_length(_max_history_length)
1431 max_history_length = _max_history_length or math.huge;
1432 end
1433
1434 _M.room_mt = room_mt; 1309 _M.room_mt = room_mt;
1435 1310
1436 return _M; 1311 return _M;