# HG changeset patch # User Thilo Cestonaro # Date 1255898376 -7200 # Node ID a96d3f37d8452877002b9768998c7ad55e0bc5ec # Parent 59f49039052837b67518a0d983b1d39d5ba882ea mod_muclogging: with http_server part for viewing diff -r 59f490390528 -r a96d3f37d845 mod_muclogging/mod_muclogging.lua --- a/mod_muclogging/mod_muclogging.lua Sat Oct 17 01:37:25 2009 +0200 +++ b/mod_muclogging/mod_muclogging.lua Sun Oct 18 22:39:36 2009 +0200 @@ -7,9 +7,45 @@ local splitJid = require "util.jid".split; local bareJid = require "util.jid".bare; local config_get = require "core.configmanager".get; +local httpserver = require "net.httpserver"; +local dump = require "util.logger".dump; +local config = {}; + +--[[ LuaFileSystem +* URL: http://www.keplerproject.org/luafilesystem/index.html +* Install: luarocks install luafilesystem +* ]] +local lfs = require "lfs"; + +local lom = require "lxp.lom"; + +function validateLogFolder() + module:log("debug", "validateLogFolder; Folder: %s", tostring(config.folder)); + if config.folder == nil then + module:log("warn", "muclogging folder isn't configured. configure it please!"); + return false; + end + + -- check existance + local attributes = lfs.attributes(config.folder); + if attributes == nil then + module:log("warn", "muclogging folder doesn't exist. create it please!"); + return false; + elseif attributes.mode ~= "directory" then + module:log("warn", "muclogging folder isn't a folder, it's a %s. change this please!", attributes.mode); + return false; + end --TODO: check for write rights! + + module:log("debug", "Folder is validated and correct.") + return true; +end function logIfNeeded(e) local stanza, origin = e.stanza, e.origin; + if validateLogFolder() == false then + return; + end + if (stanza.name == "presence") or (stanza.name == "message" and tostring(stanza.attr.type) == "groupchat") then @@ -18,11 +54,11 @@ local bare = node .. "@" .. host; if prosody.hosts[host] ~= nil and prosody.hosts[host].muc ~= nil and prosody.hosts[host].muc.rooms[bare] ~= nil then local room = prosody.hosts[host].muc.rooms[bare] - local logFolder = config_get(host, "core", "logFolder"); - if logFolder ~= nil then + local logging = config_get(host, "core", "logging"); + if logging == true then local today = os.date("%y%m%d"); local now = os.date("%X") - local fn = logFolder .. "/" .. today .. "_" .. bare .. ".log"; + local fn = config.folder .. "/" .. today .. "_" .. bare .. ".log"; local mucFrom = nil; if stanza.name == "presence" and stanza.attr.type == nil then @@ -54,7 +90,227 @@ return; end +function createDoc(body) + return [[ + + muclogging + + + + ]] .. tostring(body) .. [[ + + ]]; +end + +function splitQuery(query) + local ret = {}; + if query == nil then return ret; end + local last = 1; + local idx = query:find("&", last); + while idx ~= nil do + ret[#ret + 1] = query:sub(last, idx - 1); + last = idx + 1; + idx = query:find("&", last); + end + ret[#ret + 1] = query:sub(last); + return ret; +end + +function grepRoomJid(url) + local tmp = url:sub(string.len("/muclogging/") + 1); + local node = nil; + local host = nil; + local at = nil; + local slash = nil; + + at = tmp:find("@"); + slash = tmp:find("/"); + if slash ~= nil then + slash = slash - 1; + end + + if at ~= nil then + node = tmp:sub(1, at - 1); + host = tmp:sub(at + 1, slash); + end + return node, host; +end + +local function generateRoomListSiteContent() + local ret = "

Rooms hosted on this server:


"; + for host, config in pairs(prosody.hosts) do + if prosody.hosts[host].muc ~= nil then + local logging = config_get(host, "core", "logging"); + if logging then + for jid, room in pairs(prosody.hosts[host].muc.rooms) do + ret = ret .. "" .. jid .."
\n"; + end + else + module:log("debug", "logging not enabled for muc component: %s", tostring(host)); + end + end + end + return ret .. "


"; +end + +local function generateDayListSiteContentByRoom(bareRoomJid) + local ret = ""; + + for file in lfs.dir(config.folder) do + local year, month, day = file:match("^(%d%d)(%d%d)(%d%d)_" .. bareRoomJid .. ".log"); + module:log("debug", "year: %s, month: %s, day: %s", year, month, day); + if year ~= nil and month ~= nil and day ~= nil and + year ~= "" and month ~= "" and day ~= "" + then + ret = "20" .. year .. "/" .. month .. "/" .. day .. "
\n" .. ret; + end + end + if ret ~= "" then + return "

available logged days of room: " .. bareRoomJid .. "


" .. ret .. "


"; + else + return generateRoomListSiteContent(); -- fallback + end +end + +local function parseDay(bareRoomJid, query) + local ret = ""; + local year; + local month; + local day; + + for _,str in ipairs(query) do + local name, value; + name, value = str:match("^(%a+)=(%d+)$"); + if name == "year" then + year = value; + elseif name == "month" then + month = value; + elseif name == "day" then + day = value; + else + log("warn", "unknown query value"); + end + end + + if year ~= nil and month ~= nil and day ~= nil then + local file = config.folder .. "/" .. year .. month .. day .. "_" .. bareRoomJid .. ".log"; + local f, err = io.open(file, "r"); + if f ~= nil then + local content = f:read("*a"); + local parsed = lom.parse("" .. content .. ""); + if parsed ~= nil then + for _,stanza in ipairs(parsed) do + module:log("debug", "dump of stanza: \n%s", dump(stanza)) + if stanza.attr ~= nil and stanza.attr.time ~= nil then + ret = ret .. "[" .. stanza.attr.time .. "] "; + if stanza[1] ~= nil then + local nick; + if stanza[1].attr.from ~= nil then + nick = stanza[1].attr.from:match("/(.+)$"); + end + if stanza[1].tag == "presence" and nick ~= nil then + if stanza[1].attr.type == nil then + ret = ret .. " *** " .. nick .. " joins the room
\n"; + elseif stanza[1].attr.type ~= nil and stanza[1].attr.type == "unavailable" then + ret = ret .. " *** " .. nick .. " leaves the room
\n"; + else + ret = ret .. " *** " .. nick .. " changed his/her status to: " .. stanza[1].attr.type .. "
\n"; + end + elseif stanza[1].tag == "message" then + local body; + for _,tag in ipairs(stanza[1]) do + if tag.tag == "body" then + body = tag[1]:gsub("\n", "
\n"); + if nick ~= nil then + break; + end + elseif tag.tag == "nick" and nick == nil then + nick = tag[1]; + if body ~= nil then + break; + end + end + end + if nick ~= nil and body ~= nil then + ret = ret .. "<" .. nick .. "> " .. body .. "
\n"; + end + else + module:log("info", "unknown stanza subtag in log found. room: %s; day: %s", bareRoomJid, year .. "/" .. month .. "/" .. day); + end + end + end + end + else + module:log("warn", "could not parse room log. room: %s; day: %s", bareRoomJid, year .. "/" .. month .. "/" .. day); + end + f:close(); + else + ret = err; + end + return "

room " .. bareRoomJid .. " logging of 20" .. year .. "/" .. month .. "/" .. day .. "


" .. ret .. "


"; + else + return generateDayListSiteContentByRoom(bareRoomJid); -- fallback + end +end + +function handle_request(method, body, request) + module:log("debug", "method: %s, body: %s, request: %s", tostring(method), tostring(body), tostring(request)); + -- module:log("debug", "dump of request:\n%s\n", dump(request)); + local query = splitQuery(request.url.query); + local node, host = grepRoomJid(request.url.path); + + if validateLogFolder() == false then + return createDoc([[ + Muclogging is not configured correctly. Add a section to Host * "muclogging" and configure the value for the logging "folder".
+ Like:
+ Host "*"
+ ....
+     muclogging = {
+         folder = "/opt/local/var/log/prosody/rooms";
+     }
+ ]]); + end + if node ~= nil and host ~= nil then + local bare = node .. "@" .. host; + if prosody.hosts[host] ~= nil and prosody.hosts[host].muc ~= nil and prosody.hosts[host].muc.rooms[bare] ~= nil then + local room = prosody.hosts[host].muc.rooms[bare]; + local logging = config_get(host, "core", "logging"); + if logging == true then + if request.url.query == nil then + return createDoc(generateDayListSiteContentByRoom(bare)); + else + return createDoc(parseDay(bare, query)); + end + else + module:log("debug", "logging not enabled for this room: %s", bare); + end + else + module:log("debug", "room instance not found. bare room jid: %s", tostring(bare)); + end + else + return createDoc(generateRoomListSiteContent()); + end + return; +end + +function module.load() + config = config_get("*", "core", "muclogging"); + module:log("debug", "muclogging config: \n%s", dump(config)); + + if config.http_port ~= nil then + httpserver.new_from_config({ config.http_port }, "muclogging", handle_request); + end + return validateLogFolder(); +end + module:hook("pre-message/bare", logIfNeeded, 500); module:hook("pre-presence/full", logIfNeeded, 500); - -module:log("debug", "loaded ..."); \ No newline at end of file