view mod_message_logging/mod_message_logging.lua @ 1268:854a3933cfcd

mod_muc_log_http: URL-encode room names. This allows special characters in room names to work. Ideally this escaping shouldn’t be done in the user visible content, but the module’s template system doesn’t currently allow that.
author Waqas Hussain <waqas20@gmail.com>
date Sat, 04 Jan 2014 16:50:57 -0500
parents ba220790a59c
children 7dbde05b48a9
line wrap: on
line source

module:set_global();

local jid_bare = require "util.jid".bare;
local jid_split = require "util.jid".split;

local stat, mkdir = require "lfs".attributes, require "lfs".mkdir;

-- Get a filesystem-safe string
local function fsencode_char(c)
	return ("%%%02x"):format(c:byte());
end
local function fsencode(s)
	return (s:gsub("[^%w._-@]", fsencode_char):gsub("^%.", "_"));
end

local log_base_path = module:get_option("message_logging_dir", prosody.paths.data.."/message_logs");
mkdir(log_base_path);

local function get_host_path(host)
	return log_base_path.."/"..fsencode(host);
end

local function get_user_path(jid)
	local username, host = jid_split(jid);
	local base = get_host_path(host)..os.date("/%Y-%m-%d");
	if not stat(base) then
		mkdir(base);
	end
	return base.."/"..fsencode(username)..".msglog";
end

local open_files_mt = { __index = function (open_files, jid)
	local f, err = io.open(get_user_path(jid), "a+");
	if not f then
		module:log("error", "Failed to open message log for writing [%s]: %s", jid, err);
	end
	rawset(open_files, jid, f);
	return f;
end };

-- [user@host] = filehandle
local open_files = setmetatable({}, open_files_mt);

function close_open_files()
	module:log("debug", "Closing all open files");
	for jid, filehandle in pairs(open_files) do
		filehandle:close();
		open_files[jid] = nil;
	end
end
module:hook_global("logging-reloaded", close_open_files);

local function handle_incoming_message(event)
	local origin, stanza = event.origin, event.stanza;
	local message_type = stanza.attr.type;
	
	if message_type == "error" then return; end
	
	local from, to = jid_bare(stanza.attr.from), jid_bare(stanza.attr.to);
	local body = stanza:get_child("body");
	if not body then return; end
	body = body:get_text();
	
	local f = open_files[to];
	if not f then return; end
	if message_type == "groupchat" then
		-- Add the nickname
		from = from.." <"..(select(3, jid_split(stanza.attr.from)) or "")..">";
	end
	body = body:gsub("\n", "\n    "); -- Indent newlines
	f:write("RECV: ", from, ": ", body, "\n");
	f:flush();
end

local function handle_outgoing_message(event)
	local origin, stanza = event.origin, event.stanza;
	local message_type = stanza.attr.type;
	
	if message_type == "error" or message_type == "groupchat" then return; end
	
	local from, to = jid_bare(stanza.attr.from), jid_bare(stanza.attr.to);
	local body = stanza:get_child("body");
	if not body then return; end
	body = body:get_text();
	
	local f = open_files[from];
	if not f then return; end
	body = body:gsub("\n", "\n    "); -- Indent newlines
	f:write("SEND: ", to, ": ", body, "\n");
	f:flush();
end



function module.add_host(module)
	local host_base_path = get_host_path(module.host);
	if not stat(host_base_path) then
		mkdir(host_base_path);
	end

	module:hook("message/bare", handle_incoming_message, 1);
	module:hook("message/full", handle_incoming_message, 1);
	
	module:hook("pre-message/bare", handle_outgoing_message, 1);
	module:hook("pre-message/full", handle_outgoing_message, 1);
	module:hook("pre-message/host", handle_outgoing_message, 1);
	
end

function module.command(arg)
	local command = table.remove(arg, 1);
	if command == "path" then
		print(get_user_path(arg[1]));
	else
		io.stderr:write("Unrecognised command: ", command);
		return 1;
	end
	return 0;
end

function module.save()
	return { open_files = open_files };
end

function module.restore(saved)
	open_files = setmetatable(saved.open_files or {}, open_files_mt);
end