diff -r 6a40e44a2b8a -r 11d1d4ff8037 mod_muc_log/mod_muc_log.lua
--- /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("" .. tostring(stanza) .. "\n");
+ stanza.attr.from = realFrom;
+ stanza.attr.to = realTo;
+ f:close()
+ end
+ end
+ end
+ end
+ end
+ return;
+end
+
+function createDoc(body)
+ return [[
+
+ muc_log
+
+
+
+ ]] .. 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("/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 = "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 * "muc_log" and configure the value for the logging "folder".
+ Like:
+ Host "*"
+ ....
+ muc_log = {
+ 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", "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);