mod_log_forensic - Message Archiving Module

Name: mod_log_forensic
Purpose: Message archiving
Author: James Tait
Type: Module
Requirements: ejabberd 0.7.5
Download: -
Links: Bugzilla #12 - Message archiving support

Check also mod_logxml for a similar module.

As some of you will be aware, I have been trying to find time in between my paid project work (i.e. my actual job) to add message archiving to ejabberd. The reason for doing this was so that we could use ejabberd on our internal corporate network as a productivity tool, but meet our legal obligations (Sarbanes-Oxley and similar).

Well, I'll be leaving this job in just under three weeks, and I'm unsure what to do with the small piece of code I've managed to write. I'm not an Erlang programmer and my life has been very busy, so progress has been slow.

It may be that with my new job I actually get more time to work on this. However, I don't want to make any promises that I can't keep. It currently consists of only one file of 91 lines, including comments, some of which is re-used from other ejabberd files.

It logs all messages sent to it to an XML file. The name of the file is configurable. The location is the same as for the other log files. Presence and IQ packets are not logged, as I haven't had the time yet to figure out how to also pass them to the logging component (bandersnatch also has this problem -- I believe it uses the bcc functionality on jabberd to achieve this). There is no log rotation.

I had intended to add log rotation, the ability to log messages to mnesia and odbc back-ends, and accessibility via the web admin interface, but I simply haven't had the time.

Is there any value in me contributing the small piece of work I've done to the project? If there's a more robust solution in the works, I'll be happy to wait for that since, after all, I won't be using the Jabber server at my current workplace for much longer and I'm not desperately in need of the functionality on my home server.

The (still rather raw) code is included below:

%%%----------------------------------------------------------------------
%%% File    : mod_log_forensic.erl
%%% Author  : James Tait <james.tait at wyrddreams.org>
%%% Purpose : Forensic message/presence/iq logging for use in
%%%           corporate environments
%%% Created : 21 Sep 2004 by James Tait <james.tait at wyrddreams.org>
%%% Id      : $Id$
%%%----------------------------------------------------------------------

-module(mod_log_forensic).
-author('james.tait at wyrddreams.org').
-vsn('$Revision$ ').

-behaviour(gen_mod).

-export([start/1, init/2, stop/0]).

-include("ejabberd.hrl").
-include("jlib.hrl").

start(Opts) ->
    Host = gen_mod:get_module_opt(?MODULE, host, "logger." ++ ?MYNAME),
    io:fwrite("~s~n", [Host]),
    Logfile = gen_mod:get_module_opt(?MODULE, forensic_log, "forensic_log.xml"),
    io:fwrite("~s~n", [Logfile]),
    register(ejabberd_mod_log_forensic, spawn(?MODULE, init, [Host, Logfile])).

init(Host, Logfile) ->
    ejabberd_router:register_route(Host),
    LogPath =
        case application:get_env(log_path) of
            {ok, Path} ->
                filename:join([filename:dirname(Path), Logfile]);
            undefined ->
                case os:getenv("EJABBERD_LOG_PATH") of
                    false ->
                        filename:join([filename:dirname(?LOG_PATH), Logfile]);
                    Path ->
                        filename:join([filename:dirname(Path), Logfile])
                end
        end,
    io:fwrite("~s~n", [LogPath]),
    case file:rename(LogPath, LogPath ++ ".old") of
        ok ->
            % This is fine
            ok;
        enoent ->
            % This is fine too
            ok;
        Error ->
            % This isn't, log the error
            {error, [movefailed, Error]}
    end,
    % Use append mode, just in case something nasty happened
    % and we didn't notice, so we don't entirely trash the
    % old logs
    case file:open(LogPath, [append]) of
        {ok, IoDevice} ->
            % Cool, our file is open, begin the log
            io:fwrite(IoDevice, "~s~n", ["<?xml version=\"1.0\"?>"]),
            io:fwrite(IoDevice, "~s~n", ["<?xml-stylesheet href=\"xmpp.xsl\" type=\"text/xsl\"?>"]),
            io:fwrite(IoDevice, "~s~n", ["<log>"]),
            loop(Host, IoDevice);
        {error, Reason} ->
            % Ouch
            {error, [openfailed, Reason]}
    end.

loop(Host, IoDevice) ->
    receive
        {route, From, To, Packet} ->
            % This is where we do our logging
            % {xmlelement,"route",[],[{xmlelement,"iq",[{"from", "someone"}], []}]}
            {xmlelement, "route", _, [ Stanza | _ ]}  = Packet,
            io:fwrite(IoDevice, "~s~n", [xml:element_to_string(Stanza)]),
            loop(Host, IoDevice);
        stop ->
            io:fwrite(IoDevice, "~s~n", ["</log>"]),
            file:close(IoDevice),
            ejabberd_router:unregister_route(Host),

            ok;
        _ ->
            loop(Host, IoDevice)
    end.

stop() ->
    io:fwrite("~s~n", ["Stop() called"]),
    ejabberd_mod_log_forensic ! stop,
    ok.
Syndicate content