comparison mod_muc_log_http/muc_log_http/mod_muc_log_http.lua @ 105:cef943f0a551

mod_muc_log_http: themified ...
author Thilo Cestonaro <thilo@cestona.ro>
date Tue, 08 Dec 2009 21:12:40 +0100
parents mod_muc_log_http/mod_muc_log_http.lua@dc0fe152cadc
children 0e4e000e3de9
comparison
equal deleted inserted replaced
104:dc0fe152cadc 105:cef943f0a551
1 -- Copyright (C) 2009 Thilo Cestonaro
2 --
3 -- This project is MIT/X11 licensed. Please see the
4 -- COPYING file in the source package for more information.
5 --
6
7 local prosody = prosody;
8 local tabSort = table.sort;
9 local tonumber = _G.tonumber;
10 local tostring = _G.tostring;
11 local strformat = string.format;
12 local splitJid = require "util.jid".split;
13 local config_get = require "core.configmanager".get;
14 local httpserver = require "net.httpserver";
15 local datamanager = require "util.datamanager";
16 local data_load, data_getpath = datamanager.load, datamanager.getpath;
17 local datastore = "muc_log";
18 local urlBase = "muc_log";
19 local muc_hosts = {};
20 local config = nil;
21 local tostring = _G.tostring;
22 local tonumber = _G.tonumber;
23 local os_date, os_time = os.date, os.time;
24 local str_format = string.format;
25 local io_open = io.open;
26 local themesParent = (CFG_PLUGINDIR or "./plugins/") .. "muc_log_http/themes";
27
28 local lom = require "lxp.lom";
29
30 --[[ LuaFileSystem
31 * URL: http://www.keplerproject.org/luafilesystem/index.html
32 * Install: luarocks install luafilesystem
33 * ]]
34 local lfs = require "lfs";
35
36
37 --[[
38 * Default templates for the html output.
39 ]]--
40 local html = {};
41 local theme = "default";
42
43 local function checkDatastorePathExists(node, host, today, create)
44 create = create or false;
45 local path = data_getpath(node, host, datastore, "dat", true);
46 path = path:gsub("/[^/]*$", "");
47
48 -- check existance
49 local attributes, err = lfs.attributes(path);
50 if attributes == nil or attributes.mode ~= "directory" then
51 module:log("warn", "muc_log folder isn't a folder: %s", path);
52 return false;
53 end
54
55 attributes, err = lfs.attributes(path .. "/" .. today);
56 if attributes == nil then
57 if create then
58 return lfs.mkdir(path .. "/" .. today);
59 else
60 return false;
61 end
62 elseif attributes.mode == "directory" then
63 return true;
64 end
65 return false;
66 end
67
68 function createDoc(body)
69 if body then
70 return html.doc:gsub("###BODY_STUFF###", body);
71 end
72 end
73
74 local function htmlEscape(t)
75 t = t:gsub("<", "&lt;");
76 t = t:gsub(">", "&gt;");
77 t = t:gsub("(http://[%a%d@%.:/&%?=%-_#]+)", [[<a href="%1">%1</a>]]);
78 t = t:gsub("\n", "<br />");
79 return t;
80 end
81
82 function splitUrl(url)
83 local tmp = url:sub(string.len("/muc_log/") + 1);
84 local day = nil;
85 local room = nil;
86 local component = nil;
87 local at = nil;
88 local slash = nil;
89 local slash2 = nil;
90
91 slash = tmp:find("/");
92 if slash then
93 component = tmp:sub(1, slash - 1);
94 if tmp:len() > slash then
95 room = tmp:sub(slash + 1);
96 slash = room:find("/");
97 if slash then
98 tmp = room;
99 room = tmp:sub(1, slash - 1);
100 if tmp:len() > slash then
101 day = tmp:sub(slash + 1);
102 slash = day:find("/");
103 if slash then
104 day = day:sub(1, slash - 1);
105 end
106 end
107 end
108 end
109 end
110
111 return room, component, day;
112 end
113
114 local function generateComponentListSiteContent()
115 local components = "";
116 for component,muc_host in pairs(muc_hosts or {}) do
117 components = components .. html.components.bit:gsub("###COMPONENT###", component);
118 end
119 if components ~= "" then
120 return html.components.body:gsub("###COMPONENTS_STUFF###", components);
121 end
122 end
123
124 local function generateRoomListSiteContent(component)
125 local rooms = "";
126 if prosody.hosts[component] and prosody.hosts[component].muc ~= nil then
127 for jid, room in pairs(prosody.hosts[component].muc.rooms) do
128 local node = splitJid(jid);
129 if not room._data.hidden and node then
130 rooms = rooms .. html.rooms.bit:gsub("###ROOM###", node):gsub("###COMPONENT###", component);
131 end
132 end
133 if rooms ~= "" then
134 return html.rooms.body:gsub("###ROOMS_STUFF###", rooms):gsub("###COMPONENT###", component);
135 end
136 end
137 end
138
139 -- Calendar stuff
140 local function getDaysForMonth(month, year)
141 local daysCount = 30;
142 local leapyear = false;
143
144 if year%4 == 0 and year%100 == 0 then
145 if year%400 == 0 then
146 leapyear = true;
147 else
148 leapyear = false; -- turn of the century but not a leapyear
149 end
150 elseif year%4 == 0 then
151 leapyear = true;
152 end
153
154 if month == 2 and leapyear then
155 daysCount = 29;
156 elseif month == 2 and not leapyear then
157 daysCount = 28;
158 elseif month < 8 and month%2 == 1 or
159 month >= 8 and month%2 == 0
160 then
161 daysCount = 31;
162 end
163 return daysCount;
164 end
165
166 local function createMonth(month, year, dayCallback)
167 local htmlStr = html.month.header;
168 local days = getDaysForMonth(month, year);
169 local time = os_time{year=year, month=month, day=1};
170 local dow = tostring(os_date("%a", time))
171 local title = tostring(os_date("%B", time));
172 local weekDays = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
173 local weekDay = 0;
174 local weeks = 1;
175 local logAvailableForMinimumOneDay = false;
176
177 local weekDaysHtml = "";
178 for _, tmp in ipairs(weekDays) do
179 weekDaysHtml = weekDaysHtml .. html.month.weekDay:gsub("###DAY###", tmp) .. "\n";
180 end
181
182 htmlStr = htmlStr:gsub("###TITLE###", title):gsub("###WEEKDAYS###", weekDaysHtml);
183
184 for i = 1, 31 do
185 weekDay = weekDay + 1;
186 if weekDay == 1 then htmlStr = htmlStr .. "<tr>\n"; end
187 if i == 1 then
188 for _, tmp in ipairs(weekDays) do
189 if dow ~= tmp then
190 htmlStr = htmlStr .. html.month.emptyDay .. "\n";
191 weekDay = weekDay + 1;
192 else
193 break;
194 end
195 end
196 end
197 if i < days + 1 then
198 local tmp = tostring("<font color='#DDDDDD'>"..tostring(i).."</font>");
199 if dayCallback ~= nil and dayCallback.callback ~= nil then
200 tmp = dayCallback.callback(dayCallback.path, i, month, year);
201 end
202 if tmp == nil then
203 tmp = tostring("<font color='#DDDDDD'>"..tostring(i).."</font>");
204 else
205 logAvailableForMinimumOneDay = true;
206 end
207 htmlStr = htmlStr .. html.month.day:gsub("###DAY###", tmp) .. "\n";
208 end
209
210 if i >= days then
211 break;
212 end
213
214 if weekDay == 7 then
215 weekDay = 0;
216 weeks = weeks + 1;
217 htmlStr = htmlStr .. "</tr>\n";
218 end
219 end
220
221 if weekDay + 1 < 8 or weeks < 6 then
222 weekDay = weekDay + 1;
223 if weekDay > 7 then
224 weekDay = 1;
225 end
226 if weekDay == 1 then
227 weeks = weeks + 1;
228 end
229 for y = weeks, 6 do
230 if weekDay == 1 then
231 htmlStr = htmlStr .. "<tr>\n";
232 end
233 for i = weekDay, 7 do
234 htmlStr = htmlStr .. html.month.emptyDay .. "\n";
235 end
236 weekDay = 1
237 htmlStr = htmlStr .. "</tr>\n";
238 end
239 end
240 htmlStr = htmlStr .. html.month.footer;
241 if logAvailableForMinimumOneDay then
242 return htmlStr;
243 end
244 end
245
246 local function createYear(year, dayCallback)
247 local year = year;
248 local tmp;
249 if tonumber(year) <= 99 then
250 year = year + 2000;
251 end
252 local htmlStr = "<div name='yearDiv' style='padding: 40px; text-align: center;'>" .. html.year.title:gsub("###YEAR###", tostring(year));
253 for i=1, 12 do
254 tmp = createMonth(i, year, dayCallback);
255 if tmp then
256 htmlStr = htmlStr .. "<div style='float: left; padding: 5px;'>\n" .. tmp .. "</div>\n";
257 end
258 end
259 return htmlStr .. "</div><br style='clear:both;'/> \n";
260 end
261
262 local function perDayCallback(path, day, month, year)
263 local year = year;
264 if year > 2000 then
265 year = year - 2000;
266 end
267 local bareDay = str_format("%.02d%.02d%.02d", year, month, day);
268 local attributes, err = lfs.attributes(path.."/"..bareDay)
269 if attributes ~= nil and attributes.mode == "directory" then
270 local s = html.days.bit;
271 s = s:gsub("###BARE_DAY###", bareDay);
272 s = s:gsub("###DAY###", day);
273 return s;
274 end
275 return;
276 end
277
278 local function generateDayListSiteContentByRoom(bareRoomJid)
279 local days = "";
280 local arrDays = {};
281 local tmp;
282 local node, host, resource = splitJid(bareRoomJid);
283 local path = data_getpath(node, host, datastore);
284 local room = nil;
285 local attributes = nil;
286
287 path = path:gsub("/[^/]*$", "");
288 attributes = lfs.attributes(path);
289 if muc_hosts ~= nil and muc_hosts[host] and prosody.hosts[host] ~= nil and prosody.hosts[host].muc ~= nil and prosody.hosts[host].muc.rooms[bareRoomJid] ~= nil then
290 room = prosody.hosts[host].muc.rooms[bareRoomJid];
291 if room._data.hidden then
292 room = nil
293 end
294 end
295 if attributes ~= nil and room ~= nil then
296 local alreadyDoneYears = {};
297 for file in lfs.dir(path) do
298 local year, month, day = file:match("^(%d%d)(%d%d)(%d%d)");
299 if year ~= nil and alreadyDoneYears[year] == nil then
300 days = days .. createYear(year, {callback=perDayCallback, path=path});
301 alreadyDoneYears[year] = true;
302 end
303 end
304 end
305
306 if days ~= "" then
307 tmp = html.days.body:gsub("###DAYS_STUFF###", days);
308 return tmp:gsub("###JID###", bareRoomJid);
309 end
310 end
311
312 local function parseIqStanza(stanza, timeStuff, nick)
313 local text = nil;
314 local victim = nil;
315 if(stanza.attr.type == "set") then
316 for _,tag in ipairs(stanza) do
317 if tag.tag == "query" then
318 for _,item in ipairs(tag) do
319 if item.tag == "item" and item.attr.nick ~= nil and tostring(item.attr.role) == 'none' then
320 victim = item.attr.nick;
321 for _,reason in ipairs(item) do
322 if reason.tag == "reason" then
323 text = reason[1];
324 break;
325 end
326 end
327 break;
328 end
329 end
330 break;
331 end
332 end
333 if victim ~= nil then
334 if text ~= nil then
335 text = html.day.reason:gsub("###REASON###", htmlEscape(text));
336 else
337 text = "";
338 end
339 return html.day.kick:gsub("###TIME_STUFF###", timeStuff):gsub("###VICTIM###", victim):gsub("###REASON_STUFF###", text);
340 end
341 end
342 return;
343 end
344
345 local function parsePresenceStanza(stanza, timeStuff, nick)
346 local ret = "";
347 local showJoin = "block"
348
349 if config and not config.showJoin then
350 showJoin = "none";
351 end
352
353 if stanza.attr.type == nil then
354 local showStatus = "block"
355 if config and not config.showStatus then
356 showStatus = "none";
357 end
358 local show, status = nil, "";
359 local alreadyJoined = false;
360 for _, tag in ipairs(stanza) do
361 if tag.tag == "alreadyJoined" then
362 alreadyJoined = true;
363 elseif tag.tag == "show" then
364 show = tag[1];
365 elseif tag.tag == "status" then
366 status = tag[1];
367 end
368 end
369 if alreadyJoined == true then
370 if show == nil then
371 show = "online";
372 end
373 ret = html.day.presence.statusChange:gsub("###TIME_STUFF###", timeStuff);
374 if status ~= "" then
375 status = html.day.presence.statusText:gsub("###STATUS###", htmlEscape(status));
376 end
377 ret = ret:gsub("###SHOW###", show):gsub("###NICK###", nick):gsub("###SHOWHIDE###", showStatus):gsub("###STATUS_STUFF###", status);
378 else
379 ret = html.day.presence.join:gsub("###TIME_STUFF###", timeStuff):gsub("###SHOWHIDE###", showJoin):gsub("###NICK###", nick);
380 end
381 elseif stanza.attr.type ~= nil and stanza.attr.type == "unavailable" then
382
383 ret = html.day.presence.leave:gsub("###TIME_STUFF###", timeStuff):gsub("###SHOWHIDE###", showJoin):gsub("###NICK###", nick);
384 end
385 return ret;
386 end
387
388 local function parseMessageStanza(stanza, timeStuff, nick)
389 local body, title, ret = nil, nil, "";
390
391 for _,tag in ipairs(stanza) do
392 if tag.tag == "body" then
393 body = tag[1];
394 if nick ~= nil then
395 break;
396 end
397 elseif tag.tag == "nick" and nick == nil then
398 nick = htmlEscape(tag[1]);
399 if body ~= nil or title ~= nil then
400 break;
401 end
402 elseif tag.tag == "subject" then
403 title = tag[1];
404 if nick ~= nil then
405 break;
406 end
407 end
408 end
409 if nick ~= nil and body ~= nil then
410 body = htmlEscape(body);
411 local me = body:find("^/me");
412 local template = "";
413 if not me then
414 template = html.day.message;
415 else
416 template = html.day.messageMe;
417 body = body:gsub("^/me ", "");
418 end
419 ret = template:gsub("###TIME_STUFF###", timeStuff):gsub("###NICK###", nick):gsub("###MSG###", body);
420 elseif nick ~= nil and title ~= nil then
421 title = htmlEscape(title);
422 ret = html.day.titleChange:gsub("###TIME_STUFF###", timeStuff):gsub("###NICK###", nick):gsub("###TITLE###", title);
423 end
424 return ret;
425 end
426
427 local function incrementDay(bare_day)
428 local year, month, day = bare_day:match("^(%d%d)(%d%d)(%d%d)");
429 local leapyear = false;
430 module:log("debug", tostring(day).."/"..tostring(month).."/"..tostring(year))
431
432 day = tonumber(day);
433 month = tonumber(month);
434 year = tonumber(year);
435
436 if year%4 == 0 and year%100 == 0 then
437 if year%400 == 0 then
438 leapyear = true;
439 else
440 leapyear = false; -- turn of the century but not a leapyear
441 end
442 elseif year%4 == 0 then
443 leapyear = true;
444 end
445
446 if (month == 2 and leapyear and day + 1 > 29) or
447 (month == 2 and not leapyear and day + 1 > 28) or
448 (month < 8 and month%2 == 1 and day + 1 > 31) or
449 (month < 8 and month%2 == 0 and day + 1 > 30) or
450 (month >= 8 and month%2 == 0 and day + 1 > 31) or
451 (month >= 8 and month%2 == 1 and day + 1 > 30)
452 then
453 if month + 1 > 12 then
454 year = year + 1;
455 else
456 month = month + 1;
457 end
458 else
459 day = day + 1;
460 end
461 return strformat("%.02d%.02d%.02d", year, month, day);
462 end
463
464 local function findNextDay(bareRoomJid, bare_day)
465 local node, host, resource = splitJid(bareRoomJid);
466 local day = incrementDay(bare_day);
467 local max_trys = 7;
468
469 module:log("debug", day);
470 while(not checkDatastorePathExists(node, host, day, false)) do
471 max_trys = max_trys - 1;
472 if max_trys == 0 then
473 break;
474 end
475 day = incrementDay(day);
476 end
477 if max_trys == 0 then
478 return nil;
479 else
480 return day;
481 end
482 end
483
484 local function decrementDay(bare_day)
485 local year, month, day = bare_day:match("^(%d%d)(%d%d)(%d%d)");
486 module:log("debug", tostring(day).."/"..tostring(month).."/"..tostring(year))
487 day = tonumber(day);
488 month = tonumber(month);
489 year = tonumber(year);
490
491 if day - 1 == 0 then
492 if month - 1 == 0 then
493 year = year - 1;
494 else
495 month = month - 1;
496 end
497 else
498 day = day - 1;
499 end
500 return strformat("%.02d%.02d%.02d", year, month, day);
501 end
502
503 local function findPreviousDay(bareRoomJid, bare_day)
504 local node, host, resource = splitJid(bareRoomJid);
505 local day = decrementDay(bare_day);
506 local max_trys = 7;
507 module:log("debug", day);
508 while(not checkDatastorePathExists(node, host, day, false)) do
509 max_trys = max_trys - 1;
510 if max_trys == 0 then
511 break;
512 end
513 day = decrementDay(day);
514 end
515 if max_trys == 0 then
516 return nil;
517 else
518 return day;
519 end
520 end
521
522 local function parseDay(bareRoomJid, roomSubject, bare_day)
523 local ret = "";
524 local year;
525 local month;
526 local day;
527 local tmp;
528 local node, host, resource = splitJid(bareRoomJid);
529 local year, month, day = bare_day:match("^(%d%d)(%d%d)(%d%d)");
530 local previousDay = findPreviousDay(bareRoomJid, bare_day);
531 local nextDay = findNextDay(bareRoomJid, bare_day);
532
533 if bare_day ~= nil then
534 local data = data_load(node, host, datastore .. "/" .. bare_day);
535 if data ~= nil then
536 for i=1, #data, 1 do
537 local stanza = lom.parse(data[i]);
538 if stanza ~= nil and stanza.attr ~= nil and stanza.attr.time ~= nil then
539 local timeStuff = html.day.time:gsub("###TIME###", stanza.attr.time);
540 if stanza[1] ~= nil then
541 local nick;
542 local tmp;
543
544 -- grep nick from "from" resource
545 if stanza[1].attr.from ~= nil then -- presence and messages
546 nick = htmlEscape(stanza[1].attr.from:match("/(.+)$"));
547 elseif stanza[1].attr.to ~= nil then -- iq
548 nick = htmlEscape(stanza[1].attr.to:match("/(.+)$"));
549 end
550
551 if stanza[1].tag == "presence" and nick ~= nil then
552 tmp = parsePresenceStanza(stanza[1], timeStuff, nick);
553 elseif stanza[1].tag == "message" then
554 tmp = parseMessageStanza(stanza[1], timeStuff, nick);
555 elseif stanza[1].tag == "iq" then
556 tmp = parseIqStanza(stanza[1], timeStuff, nick);
557 else
558 module:log("info", "unknown stanza subtag in log found. room: %s; day: %s", bareRoomJid, year .. "/" .. month .. "/" .. day);
559 end
560 if tmp ~= nil then
561 ret = ret .. tmp
562 tmp = nil;
563 end
564 end
565 end
566 end
567 end
568 if ret ~= "" then
569 if nextDay then
570 nextDay = html.day.dayLink:gsub("###DAY###", nextDay):gsub("###TEXT###", "next day &gt;&gt;")
571 end
572 if previousDay then
573 previousDay = html.day.dayLink:gsub("###DAY###", previousDay):gsub("###TEXT###", "&lt;&lt; previous day");
574 end
575 tmp = html.day.body:gsub("###DAY_STUFF###", ret):gsub("###JID###", bareRoomJid);
576 tmp = tmp:gsub("###YEAR###", year):gsub("###MONTH###", month):gsub("###DAY###", day);
577 tmp = tmp:gsub("###TITLE_STUFF###", html.day.title:gsub("###TITLE###", roomSubject));
578 tmp = tmp:gsub("###STATUS_CHECKED###", config.showStatus and "checked='checked'" or "");
579 tmp = tmp:gsub("###JOIN_CHECKED###", config.showJoin and "checked='checked'" or "");
580 tmp = tmp:gsub("###NEXT_LINK###", nextDay or "");
581 tmp = tmp:gsub("###PREVIOUS_LINK###", previousDay or "");
582
583 return tmp;
584 end
585 end
586 end
587
588 function handle_request(method, body, request)
589 local node, host, day = splitUrl(request.url.path);
590
591 if muc_hosts ~= nil then
592 if node ~= nil and host ~= nil then
593 local bare = node .. "@" .. host;
594 if prosody.hosts[host] ~= nil and prosody.hosts[host].muc ~= nil then
595 if prosody.hosts[host].muc.rooms[bare] ~= nil then
596 local room = prosody.hosts[host].muc.rooms[bare];
597 if day == nil then
598 return createDoc(generateDayListSiteContentByRoom(bare));
599 else
600 local subject = ""
601 if room._data ~= nil and room._data.subject ~= nil then
602 subject = room._data.subject;
603 end
604 return createDoc(parseDay(bare, subject, day));
605 end
606 else
607 return createDoc(generateRoomListSiteContent(host));
608 end
609 else
610 return createDoc(generateComponentListSiteContent());
611 end
612 elseif host ~= nil then
613 return createDoc(generateRoomListSiteContent(host));
614 else
615 return createDoc(generateComponentListSiteContent());
616 end
617 end
618 return;
619 end
620
621 -- Compatibility: Lua-5.1
622 function split(str, pat)
623 local t = {} -- NOTE: use {n = 0} in Lua-5.0
624 local fpat = "(.-)" .. pat
625 local last_end = 1
626 local s, e, cap = str:find(fpat, 1)
627 while s do
628 if s ~= 1 or cap ~= "" then
629 table.insert(t,cap)
630 end
631 last_end = e+1
632 s, e, cap = str:find(fpat, last_end)
633 end
634 if last_end <= #str then
635 cap = str:sub(last_end)
636 table.insert(t, cap)
637 end
638 return t
639 end
640
641 local function assign(arr, content)
642 local tmp = html;
643 local idx = nil;
644 for _,i in ipairs(arr) do
645 if idx ~= nil then
646 if tmp[idx] == nil then
647 tmp[idx] = {};
648 end
649 tmp = tmp[idx];
650 end
651 idx = i;
652 end
653 tmp[idx] = content;
654 end
655
656 local function readFile(filepath)
657 local f = assert(io_open(filepath, "r"));
658 local t = f:read("*all");
659 f:close()
660 return t;
661 end
662
663 local function loadTheme(path)
664 local iter = lfs.dir(path);
665 for file in iter do
666 if file ~= "." and file ~= ".." then
667 module:log("debug", "opening theme file: " .. file);
668 local tmp = split(file:gsub("\.html$", ""), "_");
669 local content = readFile(path .. "/" .. file);
670 assign(tmp, content);
671 end
672 end
673 return true;
674 end
675
676 function module.load()
677 config = config_get("*", "core", "muc_log_http") or {};
678 if config.showStatus == nil then
679 config.showStatus = true;
680 end
681 if config.showJoin == nil then
682 config.showJoin = true;
683 end
684
685 theme = config.theme or "default";
686 local themePath = themesParent .. "/" .. tostring(theme);
687 local attributes, err = lfs.attributes(themePath);
688 if attributes == nil or attributes.mode ~= "directory" then
689 module:log("error", "Theme folder of theme \"".. tostring(theme) .. "\" isn't existing. expected Path: " .. themePath);
690 return false;
691 end
692
693 -- module:log("debug", (require "util.serialization").serialize(html));
694 if(not loadTheme(themePath)) then
695 module:log("error", "Theme \"".. tostring(theme) .. "\" is missing something.");
696 return false;
697 end
698 -- module:log("debug", (require "util.serialization").serialize(html));
699
700 httpserver.new_from_config({ config.http_port or true }, handle_request, { base = urlBase, ssl = false, port = 5290 });
701
702 for jid, host in pairs(prosody.hosts) do
703 if host.muc then
704 local enabledModules = config_get(jid, "core", "modules_enabled");
705 if enabledModules then
706 for _,mod in ipairs(enabledModules) do
707 if(mod == "muc_log") then
708 module:log("debug", "component: %s", tostring(jid));
709 muc_hosts[jid] = true;
710 break;
711 end
712 end
713 end
714 end
715 end
716 module:log("debug", "loaded mod_muc_log_http");
717 end
718
719 function module.unload()
720 muc_hosts = nil;
721 module:log("debug", "unloaded mod_muc_log_http");
722 end
723
724 module:add_event_hook("component-activated", function(component, config)
725 if config.core and config.core.modules_enabled then
726 for _,mod in ipairs(config.core.modules_enabled) do
727 if(mod == "muc_log") then
728 module:log("debug", "component: %s", tostring(component));
729 muc_hosts[component] = true;
730 break;
731 end
732 end
733 end
734 end);