comparison mod_muc_log_http/muc_log_http/mod_muc_log_http.lua @ 1032:b69e5d63a4fe

mod_muc_log, mod_muc_log_http: backport changes from Metronome.
author Marco Cirillo <maranda@lightwitch.org>
date Sat, 01 Jun 2013 23:37:39 +0200
parents 043189eb41bc
children c6d4778bc0e8
comparison
equal deleted inserted replaced
1031:6b34cc81e15d 1032:b69e5d63a4fe
1 module:depends("http"); 1 module:depends("http");
2 2
3 local prosody = prosody; 3 local prosody = prosody;
4 local hosts = prosody.hosts; 4 local hosts = prosody.hosts;
5 local my_host = module:get_host(); 5 local my_host = module:get_host();
6 local tabSort = table.sort;
7 local strchar = string.char; 6 local strchar = string.char;
8 local strformat = string.format; 7 local strformat = string.format;
9 local splitJid = require "util.jid".split; 8 local split_jid = require "util.jid".split;
10 local config_get = require "core.configmanager".get; 9 local config_get = require "core.configmanager".get;
11 local urldecode = require "net.http".urldecode; 10 local urldecode = require "net.http".urldecode;
12 local http_event = require "net.http.server".fire_event; 11 local http_event = require "net.http.server".fire_server_event;
13 local data_load, data_getpath = datamanager.load, datamanager.getpath; 12 local data_load, data_getpath = datamanager.load, datamanager.getpath;
14 local datastore = "muc_log"; 13 local datastore = "muc_log";
15 local urlBase = "muc_log"; 14 local url_base = "muc_log";
16 local config = nil; 15 local config = nil;
17 local tostring, tonumber = tostring, tonumber; 16 local table, tostring, tonumber = table, tostring, tonumber;
18 local os_date, os_time = os.date, os.time; 17 local os_date, os_time = os.date, os.time;
19 local str_format = string.format; 18 local str_format = string.format;
20 local io_open = io.open; 19 local io_open = io.open;
21 local themesParent = (module.path and module.path:gsub("[/\\][^/\\]*$", "") or (prosody.paths.plugins or "./plugins") .. "/muc_log_http") .. "/themes"; 20 local themes_parent = (module.path and module.path:gsub("[/\\][^/\\]*$", "") or (prosody.paths.plugins or "./plugins") .. "/muc_log_http") .. "/themes";
22 21
23 local lom = require "lxp.lom"; 22 local lom = require "lxp.lom";
24 local lfs = require "lfs"; 23 local lfs = require "lfs";
25 local html = {}; 24 local html = {};
26 local theme; 25 local theme;
32 if lfs.attributes(data_getpath(node, host, datastore .. "/" .. today), "mode") then return true; else return false; end 31 if lfs.attributes(data_getpath(node, host, datastore .. "/" .. today), "mode") then return true; else return false; end
33 end 32 end
34 33
35 -- Module Definitions 34 -- Module Definitions
36 35
37 local function htmlEscape(t) 36 local function html_escape(t)
38 if t then 37 if t then
39 t = t:gsub("<", "&lt;"); 38 t = t:gsub("<", "&lt;");
40 t = t:gsub(">", "&gt;"); 39 t = t:gsub(">", "&gt;");
41 t = t:gsub("(http://[%a%d@%.:/&%?=%-_#%%~]+)", function(h) 40 t = t:gsub("(http://[%a%d@%.:/&%?=%-_#%%~]+)", function(h)
42 h = urlunescape(h) 41 h = urlunescape(h)
48 t = ""; 47 t = "";
49 end 48 end
50 return t; 49 return t;
51 end 50 end
52 51
53 function createDoc(body, title) 52 function create_doc(body, title)
54 if not body then return "" end 53 if not body then return "" end
55 body = body:gsub("%%", "%%%%"); 54 body = body:gsub("%%", "%%%%");
56 return html.doc:gsub("###BODY_STUFF###", body) 55 return html.doc:gsub("###BODY_STUFF###", body)
57 :gsub("<title>muc_log</title>", "<title>"..(title and htmlEscape(title) or "Chatroom logs").."</title>"); 56 :gsub("<title>muc_log</title>", "<title>"..(title and html_escape(title) or "Chatroom logs").."</title>");
58 end 57 end
59 58
60 function urlunescape (escapedUrl) 59 function urlunescape (url)
61 escapedUrl = escapedUrl:gsub("+", " ") 60 url = url:gsub("+", " ")
62 escapedUrl = escapedUrl:gsub("%%(%x%x)", function(h) return strchar(tonumber(h,16)) end) 61 url = url:gsub("%%(%x%x)", function(h) return strchar(tonumber(h,16)) end)
63 escapedUrl = escapedUrl:gsub("\r\n", "\n") 62 url = url:gsub("\r\n", "\n")
64 return escapedUrl 63 return url
65 end 64 end
66 65
67 local function generateRoomListSiteContent(component) 66 local function generate_room_list(component)
68 local rooms = ""; 67 local rooms = "";
69 local component_host = hosts[component]; 68 local component_host = hosts[component];
70 if component_host and component_host.muc ~= nil then 69 if component_host and component_host.muc ~= nil then
71 for jid, room in pairs(component_host.muc.rooms) do 70 for jid, room in pairs(component_host.muc.rooms) do
72 local node = splitJid(jid); 71 local node = split_jid(jid);
73 if not room._data.hidden and node then 72 if not room._data.hidden and room._data.logging and node then
74 rooms = rooms .. html.rooms.bit:gsub("###ROOM###", node):gsub("###COMPONENT###", component); 73 rooms = rooms .. html.rooms.bit:gsub("###ROOM###", node):gsub("###COMPONENT###", component);
75 end 74 end
76 end 75 end
77 return html.rooms.body:gsub("###ROOMS_STUFF###", rooms):gsub("###COMPONENT###", component), "Chatroom logs for "..component; 76 return html.rooms.body:gsub("###ROOMS_STUFF###", rooms):gsub("###COMPONENT###", component), "Chatroom logs for "..component;
78 end 77 end
87 return 31; 86 return 31;
88 end 87 end
89 return 30; 88 return 30;
90 end 89 end
91 90
92 local function createMonth(month, year, dayCallback) 91 local function create_month(month, year, callback)
93 local htmlStr = html.month.header; 92 local html_str = html.month.header;
94 local days = get_days_for_month(month, year); 93 local days = get_days_for_month(month, year);
95 local time = os_time{year=year, month=month, day=1}; 94 local time = os_time{year=year, month=month, day=1};
96 local dow = tostring(os_date("%a", time)) 95 local dow = tostring(os_date("%a", time))
97 local title = tostring(os_date("%B", time)); 96 local title = tostring(os_date("%B", time));
98 local weekDays = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; 97 local week_days = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
99 local weekDay = 0; 98 local week_day = 0;
100 local weeks = 1; 99 local weeks = 1;
101 local logAvailableForMinimumOneDay = false; 100 local _available_for_one_day = false;
102 101
103 local weekDaysHtml = ""; 102 local week_days_html = "";
104 for _, tmp in ipairs(weekDays) do 103 for _, tmp in ipairs(week_days) do
105 weekDaysHtml = weekDaysHtml .. html.month.weekDay:gsub("###DAY###", tmp) .. "\n"; 104 week_days_html = week_days_html .. html.month.weekDay:gsub("###DAY###", tmp) .. "\n";
106 end 105 end
107 106
108 htmlStr = htmlStr:gsub("###TITLE###", title):gsub("###WEEKDAYS###", weekDaysHtml); 107 html_str = html_str:gsub("###TITLE###", title):gsub("###WEEKDAYS###", week_days_html);
109 108
110 for i = 1, 31 do 109 for i = 1, 31 do
111 weekDay = weekDay + 1; 110 week_day = week_day + 1;
112 if weekDay == 1 then htmlStr = htmlStr .. "<tr>\n"; end 111 if week_day == 1 then html_str = html_str .. "<tr>\n"; end
113 if i == 1 then 112 if i == 1 then
114 for _, tmp in ipairs(weekDays) do 113 for _, tmp in ipairs(week_days) do
115 if dow ~= tmp then 114 if dow ~= tmp then
116 htmlStr = htmlStr .. html.month.emptyDay .. "\n"; 115 html_str = html_str .. html.month.emptyDay .. "\n";
117 weekDay = weekDay + 1; 116 week_day = week_day + 1;
118 else 117 else
119 break; 118 break;
120 end 119 end
121 end 120 end
122 end 121 end
123 if i < days + 1 then 122 if i < days + 1 then
124 local tmp = tostring(i); 123 local tmp = tostring(i);
125 if dayCallback ~= nil and dayCallback.callback ~= nil then 124 if callback and callback.callback then
126 tmp = dayCallback.callback(dayCallback.path, i, month, year, dayCallback.room, dayCallback.webPath); 125 tmp = callback.callback(callback.path, i, month, year, callback.room, callback.webpath);
127 end 126 end
128 if tmp == nil then 127 if tmp == nil then
129 tmp = tostring(i); 128 tmp = tostring(i);
130 else 129 else
131 logAvailableForMinimumOneDay = true; 130 _available_for_one_day = true;
132 end 131 end
133 htmlStr = htmlStr .. html.month.day:gsub("###DAY###", tmp) .. "\n"; 132 html_str = html_str .. html.month.day:gsub("###DAY###", tmp) .. "\n";
134 end 133 end
135 134
136 if i >= days then 135 if i >= days then
137 break; 136 break;
138 end 137 end
139 138
140 if weekDay == 7 then 139 if week_day == 7 then
141 weekDay = 0; 140 week_day = 0;
142 weeks = weeks + 1; 141 weeks = weeks + 1;
143 htmlStr = htmlStr .. "</tr>\n"; 142 html_str = html_str .. "</tr>\n";
144 end 143 end
145 end 144 end
146 145
147 if weekDay + 1 < 8 or weeks < 6 then 146 if week_day + 1 < 8 or weeks < 6 then
148 weekDay = weekDay + 1; 147 week_day = week_day + 1;
149 if weekDay > 7 then 148 if week_day > 7 then
150 weekDay = 1; 149 week_day = 1;
151 end 150 end
152 if weekDay == 1 then 151 if week_day == 1 then
153 weeks = weeks + 1; 152 weeks = weeks + 1;
154 end 153 end
155 for y = weeks, 6 do 154 for y = weeks, 6 do
156 if weekDay == 1 then 155 if week_day == 1 then
157 htmlStr = htmlStr .. "<tr>\n"; 156 html_str = html_str .. "<tr>\n";
158 end 157 end
159 for i = weekDay, 7 do 158 for i = week_day, 7 do
160 htmlStr = htmlStr .. html.month.emptyDay .. "\n"; 159 html_str = html_str .. html.month.emptyDay .. "\n";
161 end 160 end
162 weekDay = 1 161 week_day = 1
163 htmlStr = htmlStr .. "</tr>\n"; 162 html_str = html_str .. "</tr>\n";
164 end 163 end
165 end 164 end
166 htmlStr = htmlStr .. html.month.footer; 165 html_str = html_str .. html.month.footer;
167 if logAvailableForMinimumOneDay then 166 if _available_for_one_day then
168 return htmlStr; 167 return html_str;
169 end 168 end
170 end 169 end
171 170
172 local function createYear(year, dayCallback) 171 local function create_year(year, callback)
173 local year = year; 172 local year = year;
174 local tmp; 173 local tmp;
175 if tonumber(year) <= 99 then 174 if tonumber(year) <= 99 then
176 year = year + 2000; 175 year = year + 2000;
177 end 176 end
178 local htmlStr = ""; 177 local html_str = "";
179 for i=1, 12 do 178 for i=1, 12 do
180 tmp = createMonth(i, year, dayCallback); 179 tmp = create_month(i, year, callback);
181 if tmp then 180 if tmp then
182 htmlStr = htmlStr .. "<div style='float: left; padding: 5px;'>\n" .. tmp .. "</div>\n"; 181 html_str = html_str .. "<div style='float: left; padding: 5px;'>\n" .. tmp .. "</div>\n";
183 end 182 end
184 end 183 end
185 if htmlStr ~= "" then 184 if html_str ~= "" then
186 return "<div name='yearDiv' style='padding: 40px; text-align: center;'>" .. html.year.title:gsub("###YEAR###", tostring(year)) .. htmlStr .. "</div><br style='clear:both;'/> \n"; 185 return "<div name='yearDiv' style='padding: 40px; text-align: center;'>" .. html.year.title:gsub("###YEAR###", tostring(year)) .. html_str .. "</div><br style='clear:both;'/> \n";
187 end 186 end
188 return ""; 187 return "";
189 end 188 end
190 189
191 local function perDayCallback(path, day, month, year, room, webPath) 190 local function day_callback(path, day, month, year, room, webpath)
192 local webPath = webPath or "" 191 local webpath = webpath or ""
193 local year = year; 192 local year = year;
194 if year > 2000 then 193 if year > 2000 then
195 year = year - 2000; 194 year = year - 2000;
196 end 195 end
197 local bareDay = str_format("20%.02d-%.02d-%.02d", year, month, day); 196 local bare_day = str_format("20%.02d-%.02d-%.02d", year, month, day);
198 room = p_encode(room); 197 room = p_encode(room);
199 local attributes, err = lfs.attributes(path.."/"..str_format("%.02d%.02d%.02d", year, month, day).."/"..room..".dat"); 198 local attributes, err = lfs.attributes(path.."/"..str_format("%.02d%.02d%.02d", year, month, day).."/"..room..".dat");
200 if attributes ~= nil and attributes.mode == "file" then 199 if attributes ~= nil and attributes.mode == "file" then
201 local s = html.days.bit; 200 local s = html.days.bit;
202 s = s:gsub("###BARE_DAY###", webPath .. bareDay); 201 s = s:gsub("###BARE_DAY###", webpath .. bare_day);
203 s = s:gsub("###DAY###", day); 202 s = s:gsub("###DAY###", day);
204 return s; 203 return s;
205 end 204 end
206 return; 205 return;
207 end 206 end
208 207
209 local function generateDayListSiteContentByRoom(bareRoomJid) 208 local function generate_day_room_content(bare_room_jid)
210 local days = ""; 209 local days = "";
211 local arrDays = {}; 210 local days_array = {};
212 local tmp; 211 local tmp;
213 local node, host = splitJid(bareRoomJid); 212 local node, host = split_jid(bare_room_jid);
214 local path = data_getpath(node, host, datastore); 213 local path = data_getpath(node, host, datastore);
215 local room = nil; 214 local room = nil;
216 local nextRoom = ""; 215 local next_room = "";
217 local previousRoom = ""; 216 local previous_room = "";
218 local rooms = ""; 217 local rooms = "";
219 local attributes = nil; 218 local attributes = nil;
220 local since = ""; 219 local since = "";
221 local to = ""; 220 local to = "";
222 local topic = ""; 221 local topic = "";
223 local component = hosts[host]; 222 local component = hosts[host];
224 223
225 if not(component and component.muc and component.muc.rooms[bareRoomJid]) then 224 if not(component and component.muc and component.muc.rooms[bare_room_jid]) then
226 return; 225 return;
227 end 226 end
228 227
229 path = path:gsub("/[^/]*$", ""); 228 path = path:gsub("/[^/]*$", "");
230 attributes = lfs.attributes(path); 229 attributes = lfs.attributes(path);
231 do 230 do
232 local found = 0; 231 local found = 0;
233 for jid, room in pairs(component.muc.rooms) do 232 for jid, room in pairs(component.muc.rooms) do
234 local node = splitJid(jid) 233 local node = split_jid(jid)
235 if not room._data.hidden and node then 234 if not room._data.hidden and room._data.logging and node then
236 if found == 0 then 235 if found == 0 then
237 previousRoom = node 236 previous_room = node
238 elseif found == 1 then 237 elseif found == 1 then
239 nextRoom = node 238 next_room = node
240 found = -1 239 found = -1
241 end 240 end
242 if jid == bareRoomJid then 241 if jid == bare_room_jid then
243 found = 1 242 found = 1
244 end 243 end
245 244
246 rooms = rooms .. html.days.rooms.bit:gsub("###ROOM###", node); 245 rooms = rooms .. html.days.rooms.bit:gsub("###ROOM###", node);
247 end 246 end
248 end 247 end
249 248
250 room = component.muc.rooms[bareRoomJid]; 249 room = component.muc.rooms[bare_room_jid];
251 if room._data.hidden then 250 if room._data.hidden or not room._data.logging then
252 room = nil; 251 room = nil;
253 end 252 end
254 end 253 end
255 if attributes ~= nil and room ~= nil then 254 if attributes and room then
256 local alreadyDoneYears = {}; 255 local already_done_years = {};
257 topic = room._data.subject or "(no subject)" 256 topic = room._data.subject or "(no subject)"
258 if topic:len() > 135 then 257 if topic:len() > 135 then
259 topic = topic:sub(1, topic:find(" ", 120)) .. " ..." 258 topic = topic:sub(1, topic:find(" ", 120)) .. " ..."
260 end 259 end
261 local folders = {}; 260 local folders = {};
264 for _, folder in ipairs(folders) do 263 for _, folder in ipairs(folders) do
265 local year, month, day = folder:match("^(%d%d)(%d%d)(%d%d)"); 264 local year, month, day = folder:match("^(%d%d)(%d%d)(%d%d)");
266 if year then 265 if year then
267 to = tostring(os_date("%B %Y", os_time({ day=tonumber(day), month=tonumber(month), year=2000+tonumber(year) }))); 266 to = tostring(os_date("%B %Y", os_time({ day=tonumber(day), month=tonumber(month), year=2000+tonumber(year) })));
268 if since == "" then since = to; end 267 if since == "" then since = to; end
269 if not alreadyDoneYears[year] then 268 if not already_done_years[year] then
270 module:log("debug", "creating overview for: %s", to); 269 module:log("debug", "creating overview for: %s", to);
271 days = createYear(year, {callback=perDayCallback, path=path, room=node}) .. days; 270 days = create_year(year, {callback=day_callback, path=path, room=node}) .. days;
272 alreadyDoneYears[year] = true; 271 already_done_years[year] = true;
273 end 272 end
274 end 273 end
275 end 274 end
276 end 275 end
277 276
278 tmp = html.days.body:gsub("###DAYS_STUFF###", days); 277 tmp = html.days.body:gsub("###DAYS_STUFF###", days);
279 tmp = tmp:gsub("###PREVIOUS_ROOM###", previousRoom == "" and node or previousRoom); 278 tmp = tmp:gsub("###PREVIOUS_ROOM###", previous_room == "" and node or previous_room);
280 tmp = tmp:gsub("###NEXT_ROOM###", nextRoom == "" and node or nextRoom); 279 tmp = tmp:gsub("###NEXT_ROOM###", next_room == "" and node or next_room);
281 tmp = tmp:gsub("###ROOMS###", rooms); 280 tmp = tmp:gsub("###ROOMS###", rooms);
282 tmp = tmp:gsub("###ROOMTOPIC###", topic); 281 tmp = tmp:gsub("###ROOMTOPIC###", topic);
283 tmp = tmp:gsub("###SINCE###", since); 282 tmp = tmp:gsub("###SINCE###", since);
284 tmp = tmp:gsub("###TO###", to); 283 tmp = tmp:gsub("###TO###", to);
285 return tmp:gsub("###JID###", bareRoomJid), "Chatroom logs for "..bareRoomJid; 284 return tmp:gsub("###JID###", bare_room_jid), "Chatroom logs for "..bare_room_jid;
286 end 285 end
287 286
288 local function parseIqStanza(stanza, timeStuff, nick) 287 local function parse_iq(stanza, time, nick)
289 local text = nil; 288 local text = nil;
290 local victim = nil; 289 local victim = nil;
291 if(stanza.attr.type == "set") then 290 if(stanza.attr.type == "set") then
292 for _,tag in ipairs(stanza) do 291 for _,tag in ipairs(stanza) do
293 if tag.tag == "query" then 292 if tag.tag == "query" then
304 end 303 end
305 end 304 end
306 break; 305 break;
307 end 306 end
308 end 307 end
309 if victim ~= nil then 308 if victim then
310 if text ~= nil then 309 if text then
311 text = html.day.reason:gsub("###REASON###", htmlEscape(text)); 310 text = html.day.reason:gsub("###REASON###", html_escape(text));
312 else 311 else
313 text = ""; 312 text = "";
314 end 313 end
315 return html.day.kick:gsub("###TIME_STUFF###", timeStuff):gsub("###VICTIM###", victim):gsub("###REASON_STUFF###", text); 314 return html.day.kick:gsub("###TIME_STUFF###", time):gsub("###VICTIM###", victim):gsub("###REASON_STUFF###", text);
316 end 315 end
317 end 316 end
318 return; 317 return;
319 end 318 end
320 319
321 local function parsePresenceStanza(stanza, timeStuff, nick) 320 local function parse_presence(stanza, time, nick)
322 local ret = ""; 321 local ret = "";
323 local showJoin = "block" 322 local show_join = "block"
324 323
325 if config and not config.showJoin then 324 if config and not config.show_join then
326 showJoin = "none"; 325 show_join = "none";
327 end 326 end
328 327
329 if stanza.attr.type == nil then 328 if stanza.attr.type == nil then
330 local showStatus = "block" 329 local show_status = "block"
331 if config and not config.showStatus then 330 if config and not config.show_status then
332 showStatus = "none"; 331 show_status = "none";
333 end 332 end
334 local show, status = nil, ""; 333 local show, status = nil, "";
335 local alreadyJoined = false; 334 local already_joined = false;
336 for _, tag in ipairs(stanza) do 335 for _, tag in ipairs(stanza) do
337 if tag.tag == "alreadyJoined" then 336 if tag.tag == "alreadyJoined" then
338 alreadyJoined = true; 337 already_joined = true;
339 elseif tag.tag == "show" then 338 elseif tag.tag == "show" then
340 show = tag[1]; 339 show = tag[1];
341 elseif tag.tag == "status" and tag[1] ~= nil then 340 elseif tag.tag == "status" and tag[1] ~= nil then
342 status = tag[1]; 341 status = tag[1];
343 end 342 end
344 end 343 end
345 if alreadyJoined == true then 344 if already_joined == true then
346 if show == nil then 345 if show == nil then
347 show = "online"; 346 show = "online";
348 end 347 end
349 ret = html.day.presence.statusChange:gsub("###TIME_STUFF###", timeStuff); 348 ret = html.day.presence.statusChange:gsub("###TIME_STUFF###", time);
350 if status ~= "" then 349 if status ~= "" then
351 status = html.day.presence.statusText:gsub("###STATUS###", htmlEscape(status)); 350 status = html.day.presence.statusText:gsub("###STATUS###", html_escape(status));
352 end 351 end
353 ret = ret:gsub("###SHOW###", show):gsub("###NICK###", nick):gsub("###SHOWHIDE###", showStatus):gsub("###STATUS_STUFF###", status); 352 ret = ret:gsub("###SHOW###", show):gsub("###NICK###", nick):gsub("###SHOWHIDE###", show_status):gsub("###STATUS_STUFF###", status);
354 else 353 else
355 ret = html.day.presence.join:gsub("###TIME_STUFF###", timeStuff):gsub("###SHOWHIDE###", showJoin):gsub("###NICK###", nick); 354 ret = html.day.presence.join:gsub("###TIME_STUFF###", time):gsub("###SHOWHIDE###", show_join):gsub("###NICK###", nick);
356 end 355 end
357 elseif stanza.attr.type ~= nil and stanza.attr.type == "unavailable" then 356 elseif stanza.attr.type == "unavailable" then
358 357
359 ret = html.day.presence.leave:gsub("###TIME_STUFF###", timeStuff):gsub("###SHOWHIDE###", showJoin):gsub("###NICK###", nick); 358 ret = html.day.presence.leave:gsub("###TIME_STUFF###", time):gsub("###SHOWHIDE###", show_join):gsub("###NICK###", nick);
360 end 359 end
361 return ret; 360 return ret;
362 end 361 end
363 362
364 local function parseMessageStanza(stanza, timeStuff, nick) 363 local function parse_message(stanza, time, nick)
365 local body, title, ret = nil, nil, ""; 364 local body, title, ret = nil, nil, "";
366 365
367 for _,tag in ipairs(stanza) do 366 for _,tag in ipairs(stanza) do
368 if tag.tag == "body" then 367 if tag.tag == "body" then
369 body = tag[1]; 368 body = tag[1];
370 if nick ~= nil then 369 if nick then
371 break; 370 break;
372 end 371 end
373 elseif tag.tag == "nick" and nick == nil then 372 elseif tag.tag == "nick" and nick == nil then
374 nick = htmlEscape(tag[1]); 373 nick = html_escape(tag[1]);
375 if body ~= nil or title ~= nil then 374 if body or title then
376 break; 375 break;
377 end 376 end
378 elseif tag.tag == "subject" then 377 elseif tag.tag == "subject" then
379 title = tag[1]; 378 title = tag[1];
380 if nick ~= nil then 379 if nick then
381 break; 380 break;
382 end 381 end
383 end 382 end
384 end 383 end
385 if nick ~= nil and body ~= nil then 384 if nick and body then
386 body = htmlEscape(body); 385 body = html_escape(body);
387 local me = body:find("^/me"); 386 local me = body:find("^/me");
388 local template = ""; 387 local template = "";
389 if not me then 388 if not me then
390 template = html.day.message; 389 template = html.day.message;
391 else 390 else
392 template = html.day.messageMe; 391 template = html.day.messageMe;
393 body = body:gsub("^/me ", ""); 392 body = body:gsub("^/me ", "");
394 end 393 end
395 ret = template:gsub("###TIME_STUFF###", timeStuff):gsub("###NICK###", nick):gsub("###MSG###", body); 394 ret = template:gsub("###TIME_STUFF###", time):gsub("###NICK###", nick):gsub("###MSG###", body);
396 elseif nick ~= nil and title ~= nil then 395 elseif nick and title then
397 title = htmlEscape(title); 396 title = html_escape(title);
398 ret = html.day.titleChange:gsub("###TIME_STUFF###", timeStuff):gsub("###NICK###", nick):gsub("###TITLE###", title); 397 ret = html.day.titleChange:gsub("###TIME_STUFF###", time):gsub("###NICK###", nick):gsub("###TITLE###", title);
399 end 398 end
400 return ret; 399 return ret;
401 end 400 end
402 401
403 local function incrementDay(bare_day) 402 local function increment_day(bare_day)
404 local year, month, day = bare_day:match("^20(%d%d)-(%d%d)-(%d%d)$"); 403 local year, month, day = bare_day:match("^20(%d%d)-(%d%d)-(%d%d)$");
405 local leapyear = false; 404 local leapyear = false;
406 module:log("debug", tostring(day).."/"..tostring(month).."/"..tostring(year)) 405 module:log("debug", tostring(day).."/"..tostring(month).."/"..tostring(year))
407 406
408 day = tonumber(day); 407 day = tonumber(day);
438 day = day + 1; 437 day = day + 1;
439 end 438 end
440 return strformat("20%.02d-%.02d-%.02d", year, month, day); 439 return strformat("20%.02d-%.02d-%.02d", year, month, day);
441 end 440 end
442 441
443 local function findNextDay(bareRoomJid, bare_day) 442 local function find_next_day(bare_room_jid, bare_day)
444 local node, host = splitJid(bareRoomJid); 443 local node, host = split_jid(bare_room_jid);
445 local day = incrementDay(bare_day); 444 local day = increment_day(bare_day);
446 local max_trys = 7; 445 local max_trys = 7;
447 446
448 module:log("debug", day); 447 module:log("debug", day);
449 while(not store_exists(node, host, day)) do 448 while(not store_exists(node, host, day)) do
450 max_trys = max_trys - 1; 449 max_trys = max_trys - 1;
451 if max_trys == 0 then 450 if max_trys == 0 then
452 break; 451 break;
453 end 452 end
454 day = incrementDay(day); 453 day = increment_day(day);
455 end 454 end
456 if max_trys == 0 then 455 if max_trys == 0 then
457 return nil; 456 return nil;
458 else 457 else
459 return day; 458 return day;
460 end 459 end
461 end 460 end
462 461
463 local function decrementDay(bare_day) 462 local function decrement_day(bare_day)
464 local year, month, day = bare_day:match("^20(%d%d)-(%d%d)-(%d%d)$"); 463 local year, month, day = bare_day:match("^20(%d%d)-(%d%d)-(%d%d)$");
465 local leapyear = false; 464 local leapyear = false;
466 module:log("debug", tostring(day).."/"..tostring(month).."/"..tostring(year)) 465 module:log("debug", tostring(day).."/"..tostring(month).."/"..tostring(year))
467 466
468 day = tonumber(day); 467 day = tonumber(day);
496 day = day - 1; 495 day = day - 1;
497 end 496 end
498 return strformat("20%.02d-%.02d-%.02d", year, month, day); 497 return strformat("20%.02d-%.02d-%.02d", year, month, day);
499 end 498 end
500 499
501 local function findPreviousDay(bareRoomJid, bare_day) 500 local function find_previous_day(bare_room_jid, bare_day)
502 local node, host = splitJid(bareRoomJid); 501 local node, host = split_jid(bare_room_jid);
503 local day = decrementDay(bare_day); 502 local day = decrement_day(bare_day);
504 local max_trys = 7; 503 local max_trys = 7;
505 module:log("debug", day); 504 module:log("debug", day);
506 while(not store_exists(node, host, day)) do 505 while(not store_exists(node, host, day)) do
507 max_trys = max_trys - 1; 506 max_trys = max_trys - 1;
508 if max_trys == 0 then 507 if max_trys == 0 then
509 break; 508 break;
510 end 509 end
511 day = decrementDay(day); 510 day = decrement_day(day);
512 end 511 end
513 if max_trys == 0 then 512 if max_trys == 0 then
514 return nil; 513 return nil;
515 else 514 else
516 return day; 515 return day;
517 end 516 end
518 end 517 end
519 518
520 local function parseDay(bareRoomJid, roomSubject, bare_day) 519 local function parse_day(bare_room_jid, room_subject, bare_day)
521 local ret = ""; 520 local ret = "";
522 local year; 521 local year;
523 local month; 522 local month;
524 local day; 523 local day;
525 local tmp; 524 local tmp;
526 local node, host = splitJid(bareRoomJid); 525 local node, host = split_jid(bare_room_jid);
527 local year, month, day = bare_day:match("^20(%d%d)-(%d%d)-(%d%d)$"); 526 local year, month, day = bare_day:match("^20(%d%d)-(%d%d)-(%d%d)$");
528 local previousDay = findPreviousDay(bareRoomJid, bare_day); 527 local previous_day = find_previous_day(bare_room_jid, bare_day);
529 local nextDay = findNextDay(bareRoomJid, bare_day); 528 local next_day = find_next_day(bare_room_jid, bare_day);
530 local temptime = {day=0, month=0, year=0}; 529 local temptime = {day=0, month=0, year=0};
531 local path = data_getpath(node, host, datastore); 530 local path = data_getpath(node, host, datastore);
532 path = path:gsub("/[^/]*$", ""); 531 path = path:gsub("/[^/]*$", "");
533 local calendar = "" 532 local calendar = ""
534 533
537 end 536 end
538 537
539 temptime.day = tonumber(day) 538 temptime.day = tonumber(day)
540 temptime.month = tonumber(month) 539 temptime.month = tonumber(month)
541 temptime.year = tonumber(year) 540 temptime.year = tonumber(year)
542 calendar = createMonth(temptime.month, temptime.year, {callback=perDayCallback, path=path, room=node, webPath="../"}) or "" 541 calendar = create_month(temptime.month, temptime.year, {callback=day_callback, path=path, room=node, webpath="../"}) or ""
543 542
544 if bare_day then 543 if bare_day then
545 local data = data_load(node, host, datastore .. "/" .. bare_day:match("^20(.*)"):gsub("-", "")); 544 local data = data_load(node, host, datastore .. "/" .. bare_day:match("^20(.*)"):gsub("-", ""));
546 if data then 545 if data then
547 for i=1, #data, 1 do 546 for i=1, #data, 1 do
552 local nick; 551 local nick;
553 local tmp; 552 local tmp;
554 553
555 -- grep nick from "from" resource 554 -- grep nick from "from" resource
556 if stanza[1].attr.from then -- presence and messages 555 if stanza[1].attr.from then -- presence and messages
557 nick = htmlEscape(stanza[1].attr.from:match("/(.+)$")); 556 nick = html_escape(stanza[1].attr.from:match("/(.+)$"));
558 elseif stanza[1].attr.to then -- iq 557 elseif stanza[1].attr.to then -- iq
559 nick = htmlEscape(stanza[1].attr.to:match("/(.+)$")); 558 nick = html_escape(stanza[1].attr.to:match("/(.+)$"));
560 end 559 end
561 560
562 if stanza[1].tag == "presence" and nick then 561 if stanza[1].tag == "presence" and nick then
563 tmp = parsePresenceStanza(stanza[1], timeStuff, nick); 562 tmp = parse_presence(stanza[1], timeStuff, nick);
564 elseif stanza[1].tag == "message" then 563 elseif stanza[1].tag == "message" then
565 tmp = parseMessageStanza(stanza[1], timeStuff, nick); 564 tmp = parse_message(stanza[1], timeStuff, nick);
566 elseif stanza[1].tag == "iq" then 565 elseif stanza[1].tag == "iq" then
567 tmp = parseIqStanza(stanza[1], timeStuff, nick); 566 tmp = parse_iq(stanza[1], timeStuff, nick);
568 else 567 else
569 module:log("info", "unknown stanza subtag in log found. room: %s; day: %s", bareRoomJid, year .. "/" .. month .. "/" .. day); 568 module:log("info", "unknown stanza subtag in log found. room: %s; day: %s", bare_room_jid, year .. "/" .. month .. "/" .. day);
570 end 569 end
571 if tmp then 570 if tmp then
572 ret = ret .. tmp 571 ret = ret .. tmp
573 tmp = nil; 572 tmp = nil;
574 end 573 end
575 end 574 end
576 end 575 end
577 end 576 end
578 end 577 end
579 if ret ~= "" then 578 if ret ~= "" then
580 if nextDay then 579 if next_day then
581 nextDay = html.day.dayLink:gsub("###DAY###", nextDay):gsub("###TEXT###", "&gt;") 580 next_day = html.day.dayLink:gsub("###DAY###", next_day):gsub("###TEXT###", "&gt;")
582 end 581 end
583 if previousDay then 582 if previous_day then
584 previousDay = html.day.dayLink:gsub("###DAY###", previousDay):gsub("###TEXT###", "&lt;"); 583 previous_day = html.day.dayLink:gsub("###DAY###", previous_day):gsub("###TEXT###", "&lt;");
585 end 584 end
586 ret = ret:gsub("%%", "%%%%"); 585 ret = ret:gsub("%%", "%%%%");
587 tmp = html.day.body:gsub("###DAY_STUFF###", ret):gsub("###JID###", bareRoomJid); 586 if config.show_presences then
587 tmp = html.day.body:gsub("###DAY_STUFF###", ret):gsub("###JID###", bare_room_jid);
588 else
589 tmp = html.day.bodynp:gsub("###DAY_STUFF###", ret):gsub("###JID###", bare_room_jid);
590 end
588 tmp = tmp:gsub("###CALENDAR###", calendar); 591 tmp = tmp:gsub("###CALENDAR###", calendar);
589 tmp = tmp:gsub("###DATE###", tostring(os_date("%A, %B %d, %Y", os_time(temptime)))); 592 tmp = tmp:gsub("###DATE###", tostring(os_date("%A, %B %d, %Y", os_time(temptime))));
590 tmp = tmp:gsub("###TITLE_STUFF###", html.day.title:gsub("###TITLE###", roomSubject)); 593 tmp = tmp:gsub("###TITLE_STUFF###", html.day.title:gsub("###TITLE###", room_subject));
591 tmp = tmp:gsub("###STATUS_CHECKED###", config.showStatus and "checked='checked'" or ""); 594 tmp = tmp:gsub("###STATUS_CHECKED###", config.show_status and "checked='checked'" or "");
592 tmp = tmp:gsub("###JOIN_CHECKED###", config.showJoin and "checked='checked'" or ""); 595 tmp = tmp:gsub("###JOIN_CHECKED###", config.show_join and "checked='checked'" or "");
593 tmp = tmp:gsub("###NEXT_LINK###", nextDay or ""); 596 tmp = tmp:gsub("###NEXT_LINK###", next_day or "");
594 tmp = tmp:gsub("###PREVIOUS_LINK###", previousDay or ""); 597 tmp = tmp:gsub("###PREVIOUS_LINK###", previous_day or "");
595 598
596 return tmp, "Chatroom logs for "..bareRoomJid.." ("..tostring(os_date("%A, %B %d, %Y", os_time(temptime)))..")"; 599 return tmp, "Chatroom logs for "..bare_room_jid.." ("..tostring(os_date("%A, %B %d, %Y", os_time(temptime)))..")";
597 end 600 end
598 end 601 end
599 end 602 end
600 603
601 local function handle_error(code, err) return http_event("http-error", { code = code, message = err }); end 604 local function handle_error(code, err) return http_event("http-error", { code = code, message = err }); end
602 function handle_request(event) 605 function handle_request(event)
603 local response = event.response; 606 local response = event.response;
604 local request = event.request; 607 local request = event.request;
605 local room; 608 local room;
606 609
607 local node, day, more = request.url.path:match("^/"..urlBase.."/+([^/]*)/*([^/]*)/*(.*)$"); 610 local node, day, more = request.url.path:match("^/"..url_base.."/+([^/]*)/*([^/]*)/*(.*)$");
608 if more ~= "" then 611 if more ~= "" then
609 response.status_code = 404; 612 response.status_code = 404;
610 return response:send(handle_error(response.status_code, "Unknown URL.")); 613 return response:send(handle_error(response.status_code, "Unknown URL."));
611 end 614 end
612 if node == "" then node = nil; end 615 if node == "" then node = nil; end
623 if node then room = hosts[my_host].modules.muc.rooms[node.."@"..my_host]; end 626 if node then room = hosts[my_host].modules.muc.rooms[node.."@"..my_host]; end
624 if node and not room then 627 if node and not room then
625 response.status_code = 404; 628 response.status_code = 404;
626 return response:send(handle_error(response.status_code, "Room doesn't exist.")); 629 return response:send(handle_error(response.status_code, "Room doesn't exist."));
627 end 630 end
628 if room and room._data.hidden then 631 if room and (room._data.hidden or not room._data.logging) then
629 response.status_code = 404; 632 response.status_code = 404;
630 return response:send(handle_error(response.status_code, "There're no logs for this room.")); 633 return response:send(handle_error(response.status_code, "There're no logs for this room."));
631 end 634 end
632 635
633 636
634 if not node then -- room list for component 637 if not node then -- room list for component
635 return response:send(createDoc(generateRoomListSiteContent(my_host))); 638 return response:send(create_doc(generate_room_list(my_host)));
636 elseif not day then -- room's listing 639 elseif not day then -- room's listing
637 return response:send(createDoc(generateDayListSiteContentByRoom(node.."@"..my_host))); 640 return response:send(create_doc(generate_day_room_content(node.."@"..my_host)));
638 else 641 else
639 if not day:match("^20(%d%d)-(%d%d)-(%d%d)$") then 642 if not day:match("^20(%d%d)-(%d%d)-(%d%d)$") then
640 local y,m,d = day:match("^(%d%d)(%d%d)(%d%d)$"); 643 local y,m,d = day:match("^(%d%d)(%d%d)(%d%d)$");
641 if not y then 644 if not y then
642 response.status_code = 404; 645 response.status_code = 404;
643 return response:send(handle_error(response.status_code, "No entries for that year.")); 646 return response:send(handle_error(response.status_code, "No entries for that year."));
644 end 647 end
645 response.status_code = 301; 648 response.status_code = 301;
646 response.headers = { ["Location"] = request.url.path:match("^/"..urlBase.."/+[^/]*").."/20"..y.."-"..m.."-"..d.."/" }; 649 response.headers = { ["Location"] = request.url.path:match("^/"..url_base.."/+[^/]*").."/20"..y.."-"..m.."-"..d.."/" };
647 return response:send(); 650 return response:send();
648 end 651 end
649 652
650 local body = createDoc(parseDay(node.."@"..my_host, room._data.subject or "", day)); 653 local body = create_doc(parse_day(node.."@"..my_host, room._data.subject or "", day));
651 if body == "" then 654 if body == "" then
652 response.status_code = 404; 655 response.status_code = 404;
653 return response:send(handle_error(response.status_code, "Day entry doesn't exist.")); 656 return response:send(handle_error(response.status_code, "Day entry doesn't exist."));
654 end 657 end
655 return response:send(body); 658 return response:send(body);
656 end 659 end
657 end 660 end
658 661
659 local function readFile(filepath) 662 local function read_file(filepath)
660 local f,err = io_open(filepath, "r"); 663 local f,err = io_open(filepath, "r");
661 if not f then return f,err; end 664 if not f then return f,err; end
662 local t = f:read("*all"); 665 local t = f:read("*all");
663 f:close() 666 f:close()
664 return t; 667 return t;
665 end 668 end
666 669
667 local function loadTheme(path) 670 local function load_theme(path)
668 for file in lfs.dir(path) do 671 for file in lfs.dir(path) do
669 if file:match("%.html$") then 672 if file:match("%.html$") then
670 module:log("debug", "opening theme file: " .. file); 673 module:log("debug", "opening theme file: " .. file);
671 local content,err = readFile(path .. "/" .. file); 674 local content,err = read_file(path .. "/" .. file);
672 if not content then return content,err; end 675 if not content then return content,err; end
673 676
674 -- html.a.b.c = content of a_b_c.html 677 -- html.a.b.c = content of a_b_c.html
675 local tmp = html; 678 local tmp = html;
676 for idx in file:gmatch("([^_]*)_") do 679 for idx in file:gmatch("([^_]*)_") do
683 return true; 686 return true;
684 end 687 end
685 688
686 function module.load() 689 function module.load()
687 config = module:get_option("muc_log_http_config", {}); 690 config = module:get_option("muc_log_http_config", {});
688 if config.showStatus == nil then config.showStatus = true; end 691 if module:get_option_boolean("muc_log_presences", false) then config.show_presences = true end
689 if config.showJoin == nil then config.showJoin = true; end 692 if config.show_status == nil then config.show_status = true; end
690 if config.urlBase and type(config.urlBase) == "string" then urlBase = config.urlBase; end 693 if config.show_join == nil then config.show_join = true; end
694 if config.url_base and type(config.url_base) == "string" then url_base = config.url_base; end
691 695
692 theme = config.theme or "prosody"; 696 theme = config.theme or "prosody";
693 local themePath = themesParent .. "/" .. tostring(theme); 697 local theme_path = themes_parent .. "/" .. tostring(theme);
694 local attributes, err = lfs.attributes(themePath); 698 local attributes, err = lfs.attributes(theme_path);
695 if attributes == nil or attributes.mode ~= "directory" then 699 if attributes == nil or attributes.mode ~= "directory" then
696 module:log("error", "Theme folder of theme \"".. tostring(theme) .. "\" isn't existing. expected Path: " .. themePath); 700 module:log("error", "Theme folder of theme \"".. tostring(theme) .. "\" isn't existing. expected Path: " .. theme_path);
697 return false; 701 return false;
698 end 702 end
699 703
700 local themeLoaded,err = loadTheme(themePath); 704 local themeLoaded,err = load_theme(theme_path);
701 if not themeLoaded then 705 if not themeLoaded then
702 module:log("error", "Theme \"%s\" is missing something: %s", tostring(theme), err); 706 module:log("error", "Theme \"%s\" is missing something: %s", tostring(theme), err);
703 return false; 707 return false;
704 end 708 end
705 709
706 module:provides("http", { 710 module:provides("http", {
707 default_path = urlBase, 711 default_path = url_base,
708 route = { 712 route = {
709 ["GET /*"] = handle_request; 713 ["GET /*"] = handle_request;
710 } 714 }
711 }); 715 });
712 end 716 end