Mercurial > prosody-modules
changeset 52:11d1d4ff8037
mod_muclogging: renamed to mod_muc_log; s/muclogging/muc_log/
author | Thilo Cestonaro <thilo@cestona.ro> |
---|---|
date | Mon, 19 Oct 2009 00:02:32 +0200 |
parents | 6a40e44a2b8a |
children | 5c4dd39a1e99 |
files | mod_muc_log/mod_muc_log.lua mod_muclogging/mod_muclogging.lua |
diffstat | 2 files changed, 316 insertions(+), 316 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_muc_log/mod_muc_log.lua Mon Oct 19 00:02:32 2009 +0200 @@ -0,0 +1,316 @@ +-- Copyright (C) 2009 Thilo Cestonaro +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- +local prosody = prosody; +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", "muc_log 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", "muc_log folder doesn't exist. create it please!"); + return false; + elseif attributes.mode ~= "directory" then + module:log("warn", "muc_log 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 + local node, host, resource = splitJid(stanza.attr.to); + 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 + local today = os.date("%y%m%d"); + local now = os.date("%X") + local fn = config.folder .. "/" .. today .. "_" .. bare .. ".log"; + local mucFrom = nil; + + if stanza.name == "presence" and stanza.attr.type == nil then + mucFrom = stanza.attr.to; + else + for jid, nick in pairs(room._jid_nick) do + if jid == stanza.attr.from then + mucFrom = nick; + end + end + end + + if mucFrom ~= nil then + module:log("debug", "try to open room log: %s", fn); + local f = assert(io.open(fn, "a")); + local realFrom = stanza.attr.from; + local realTo = stanza.attr.to; + stanza.attr.from = mucFrom; + stanza.attr.to = nil; + f:write("<stanza time=\"".. now .. "\">" .. tostring(stanza) .. "</stanza>\n"); + stanza.attr.from = realFrom; + stanza.attr.to = realTo; + f:close() + end + end + end + end + end + return; +end + +function createDoc(body) + return [[<html> + <head> + <title>muc_log</title> + </head> + <style type="text/css"> + <!-- + .timestuff {color: #AAAAAA; text-decoration: none;} + .muc_join {color: #009900; font-style: italic;} + .muc_leave {color: #009900; font-style: italic;} + .muc_kick {color: #009900; font-style: italic;} + .muc_bann {color: #009900; font-style: italic;} + .muc_name {color: #0000AA;} + //--> + </style> + <body> + ]] .. tostring(body) .. [[ + </body> + </html>]]; +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("/muc_log/") + 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 = "<h2>Rooms hosted on this server:</h2><hr /><p>"; + 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 .. "<a href=\"/muc_log/" .. jid .. "/\">" .. jid .."</a><br />\n"; + end + else + module:log("debug", "logging not enabled for muc component: %s", tostring(host)); + end + end + end + return ret .. "</p><hr />"; +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 = "<a href=\"/muc_log/" .. bareRoomJid .. "/?year=" .. year .. "&month=" .. month .. "&day=" .. day .. "\">20" .. year .. "/" .. month .. "/" .. day .. "</a><br />\n" .. ret; + end + end + if ret ~= "" then + return "<h2>available logged days of room: " .. bareRoomJid .. "</h2><hr /><p>" .. ret .. "</p><hr />"; + 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("<xml>" .. content .. "</xml>"); + 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 .. "<a name=\"" .. stanza.attr.time .. "\" href=\"#" .. stanza.attr.time .. "\" class=\"timestuff\">[" .. stanza.attr.time .. "]</a> "; + 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 .. "<font class=\"muc_join\"> *** " .. nick .. " joins the room</font><br />\n"; + elseif stanza[1].attr.type ~= nil and stanza[1].attr.type == "unavailable" then + ret = ret .. "<font class=\"muc_leave\"> *** " .. nick .. " leaves the room</font><br />\n"; + else + ret = ret .. "<font class=\"muc_leave\"> *** " .. nick .. " changed his/her status to: " .. stanza[1].attr.type .. "</font><br />\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", "<br />\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 .. "<font class=\"muc_name\"><" .. nick .. "></font> " .. body .. "<br />\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 "<h2>room " .. bareRoomJid .. " logging of 20" .. year .. "/" .. month .. "/" .. day .. "</h2><hr /><p>" .. ret .. "</p><hr />"; + 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 * "muc_log" and configure the value for the logging "folder".<br /> + Like:<br /> + Host "*"<br /> + ....<br /> + muc_log = {<br /> + folder = "/opt/local/var/log/prosody/rooms";<br /> + }<br /> + ]]); + 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", "muc_log"); + -- module:log("debug", "muc_log config: \n%s", dump(config)); + + if config.http_port ~= nil then + httpserver.new_from_config({ config.http_port }, "muc_log", handle_request); + end + return validateLogFolder(); +end + +module:hook("pre-message/bare", logIfNeeded, 500); +module:hook("pre-presence/full", logIfNeeded, 500);
--- a/mod_muclogging/mod_muclogging.lua Sun Oct 18 23:17:16 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,316 +0,0 @@ --- Copyright (C) 2009 Thilo Cestonaro --- --- This project is MIT/X11 licensed. Please see the --- COPYING file in the source package for more information. --- -local prosody = prosody; -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 - local node, host, resource = splitJid(stanza.attr.to); - 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 - local today = os.date("%y%m%d"); - local now = os.date("%X") - local fn = config.folder .. "/" .. today .. "_" .. bare .. ".log"; - local mucFrom = nil; - - if stanza.name == "presence" and stanza.attr.type == nil then - mucFrom = stanza.attr.to; - else - for jid, nick in pairs(room._jid_nick) do - if jid == stanza.attr.from then - mucFrom = nick; - end - end - end - - if mucFrom ~= nil then - module:log("debug", "try to open room log: %s", fn); - local f = assert(io.open(fn, "a")); - local realFrom = stanza.attr.from; - local realTo = stanza.attr.to; - stanza.attr.from = mucFrom; - stanza.attr.to = nil; - f:write("<stanza time=\"".. now .. "\">" .. tostring(stanza) .. "</stanza>\n"); - stanza.attr.from = realFrom; - stanza.attr.to = realTo; - f:close() - end - end - end - end - end - return; -end - -function createDoc(body) - return [[<html> - <head> - <title>muclogging</title> - </head> - <style type="text/css"> - <!-- - .timestuff {color: #AAAAAA; text-decoration: none;} - .muc_join {color: #009900; font-style: italic;} - .muc_leave {color: #009900; font-style: italic;} - .muc_kick {color: #009900; font-style: italic;} - .muc_bann {color: #009900; font-style: italic;} - .muc_name {color: #0000AA;} - //--> - </style> - <body> - ]] .. tostring(body) .. [[ - </body> - </html>]]; -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 = "<h2>Rooms hosted on this server:</h2><hr /><p>"; - 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 .. "<a href=\"/muclogging/" .. jid .. "/\">" .. jid .."</a><br />\n"; - end - else - module:log("debug", "logging not enabled for muc component: %s", tostring(host)); - end - end - end - return ret .. "</p><hr />"; -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 = "<a href=\"/muclogging/" .. bareRoomJid .. "/?year=" .. year .. "&month=" .. month .. "&day=" .. day .. "\">20" .. year .. "/" .. month .. "/" .. day .. "</a><br />\n" .. ret; - end - end - if ret ~= "" then - return "<h2>available logged days of room: " .. bareRoomJid .. "</h2><hr /><p>" .. ret .. "</p><hr />"; - 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("<xml>" .. content .. "</xml>"); - 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 .. "<a name=\"" .. stanza.attr.time .. "\" href=\"#" .. stanza.attr.time .. "\" class=\"timestuff\">[" .. stanza.attr.time .. "]</a> "; - 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 .. "<font class=\"muc_join\"> *** " .. nick .. " joins the room</font><br />\n"; - elseif stanza[1].attr.type ~= nil and stanza[1].attr.type == "unavailable" then - ret = ret .. "<font class=\"muc_leave\"> *** " .. nick .. " leaves the room</font><br />\n"; - else - ret = ret .. "<font class=\"muc_leave\"> *** " .. nick .. " changed his/her status to: " .. stanza[1].attr.type .. "</font><br />\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", "<br />\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 .. "<font class=\"muc_name\"><" .. nick .. "></font> " .. body .. "<br />\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 "<h2>room " .. bareRoomJid .. " logging of 20" .. year .. "/" .. month .. "/" .. day .. "</h2><hr /><p>" .. ret .. "</p><hr />"; - 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".<br /> - Like:<br /> - Host "*"<br /> - ....<br /> - muclogging = {<br /> - folder = "/opt/local/var/log/prosody/rooms";<br /> - }<br /> - ]]); - 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);