Packages
ejabberd
26.2.0
26.4.0
26.3.0
26.2.0
26.1.0
25.10.0
25.8.0
25.7.0
25.4.0
25.3.0
24.12.0
24.10.0
24.7.0
24.6.0
24.2.6
23.10.0
23.4.0
23.1.0
22.10.0
22.5.0
21.12.0
21.7.0
21.4.0
21.1.0
20.12.0
20.7.0
20.4.0
20.3.0
20.2.0
20.1.0
19.9.1
19.9.0
19.8.0
19.5.0
19.2.0
18.12.1
18.12.0
18.6.0
18.4.0
18.3.0
18.1.0
17.11.0
17.9.0
17.6.0
17.3.0
17.1.0
16.12.0-beta1
16.9.0
16.8.0
16.6.2
16.6.1
16.6.0
16.4.1
16.4.0
16.3.0
16.2.0
16.1.0-beta1
Robust, Ubiquitous and Massively Scalable Messaging Platform (XMPP, MQTT, SIP Server)
Current section
Files
Jump to
Current section
Files
src/mod_antispam_dump.erl
%%%----------------------------------------------------------------------
%%% File : mod_antispam_dump.erl
%%% Author : Holger Weiss <holger@zedat.fu-berlin.de>
%%% Author : Stefan Strigler <stefan@strigler.de>
%%% Purpose : Manage dump file for filtered spam messages
%%% Created : 31 Mar 2019 by Holger Weiss <holger@zedat.fu-berlin.de>
%%%
%%%
%%% ejabberd, Copyright (C) 2019-2026 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
%%% published by the Free Software Foundation; either version 2 of the
%%% License, or (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
%%% General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License along
%%% with this program; if not, write to the Free Software Foundation, Inc.,
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
%%%
%%%----------------------------------------------------------------------
%%| Definitions
%% @format-begin
-module(mod_antispam_dump).
-author('holger@zedat.fu-berlin.de').
-author('stefan@strigler.de').
-export([init_dumping/1, terminate_dumping/2, reload_dumping/4, reopen_dump_file/2,
write_stanza_dump/2]).
%% ejabberd_hooks callbacks
-export([dump_spam_stanza/1, reopen_log/0]).
-include("logger.hrl").
-include("mod_antispam.hrl").
-include("translate.hrl").
-include_lib("xmpp/include/xmpp.hrl").
%%--------------------------------------------------------------------
%%| Exported
init_dumping(Host) ->
case get_path_option(Host) of
false ->
undefined;
DumpFile when is_binary(DumpFile) ->
case filelib:ensure_dir(DumpFile) of
ok ->
ejabberd_hooks:add(spam_stanza_rejected, Host, ?MODULE, dump_spam_stanza, 50),
ejabberd_hooks:add(reopen_log_hook, ?MODULE, reopen_log, 50),
open_dump_file(DumpFile);
{error, Reason} ->
Dirname = filename:dirname(DumpFile),
throw({open, Dirname, Reason})
end
end.
terminate_dumping(_Host, false) ->
ok;
terminate_dumping(Host, Fd) ->
DumpFile1 = get_path_option(Host),
close_dump_file(Fd, DumpFile1),
ejabberd_hooks:delete(spam_stanza_rejected, Host, ?MODULE, dump_spam_stanza, 50),
case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of
false ->
ejabberd_hooks:delete(reopen_log_hook, ?MODULE, reopen_log, 50);
true ->
ok
end.
reload_dumping(Host, Fd, OldOpts, NewOpts) ->
case {get_path_option(Host, OldOpts), get_path_option(Host, NewOpts)} of
{Old, Old} ->
Fd;
{Old, New} ->
reopen_dump_file(Fd, Old, New)
end.
-spec reopen_dump_file(binary(), file:io_device()) -> file:io_device().
reopen_dump_file(Host, Fd) ->
DumpFile1 = get_path_option(Host),
reopen_dump_file(Fd, DumpFile1, DumpFile1).
%%--------------------------------------------------------------------
%%| Hook callbacks
-spec dump_spam_stanza(message()) -> ok.
dump_spam_stanza(#message{to = #jid{lserver = LServer}} = Msg) ->
By = jid:make(<<>>, LServer),
Proc = get_proc_name(LServer),
Time = erlang:timestamp(),
Msg1 = misc:add_delay_info(Msg, By, Time),
XML = fxml:element_to_binary(
xmpp:encode(Msg1)),
gen_server:cast(Proc, {dump_stanza, XML}).
-spec reopen_log() -> ok.
reopen_log() ->
lists:foreach(fun(Host) ->
Proc = get_proc_name(Host),
gen_server:cast(Proc, reopen_log)
end,
get_spam_filter_hosts()).
%%--------------------------------------------------------------------
%%| File management
-spec open_dump_file(filename()) -> undefined | file:io_device().
open_dump_file(false) ->
undefined;
open_dump_file(Name) ->
Modes = [append, raw, binary, delayed_write],
case file:open(Name, Modes) of
{ok, Fd} ->
?DEBUG("Opened ~s", [Name]),
Fd;
{error, Reason} ->
?ERROR_MSG("Cannot open dump file ~s: ~s", [Name, file:format_error(Reason)]),
undefined
end.
-spec close_dump_file(undefined | file:io_device(), filename()) -> ok.
close_dump_file(undefined, false) ->
ok;
close_dump_file(Fd, Name) ->
case file:close(Fd) of
ok ->
?DEBUG("Closed ~s", [Name]);
{error, Reason} ->
?ERROR_MSG("Cannot close ~s: ~s", [Name, file:format_error(Reason)])
end.
-spec reopen_dump_file(file:io_device(), binary(), binary()) -> file:io_device().
reopen_dump_file(Fd, OldDumpFile, NewDumpFile) ->
close_dump_file(Fd, OldDumpFile),
open_dump_file(NewDumpFile).
write_stanza_dump(Fd, XML) ->
case file:write(Fd, [XML, <<$\n>>]) of
ok ->
ok;
{error, Reason} ->
?ERROR_MSG("Cannot write spam to dump file: ~s", [file:format_error(Reason)])
end.
%%--------------------------------------------------------------------
%%| Auxiliary
get_path_option(Host) ->
Opts = gen_mod:get_module_opts(Host, ?MODULE_ANTISPAM),
get_path_option(Host, Opts).
get_path_option(Host, Opts) ->
case gen_mod:get_opt(spam_dump_file, Opts) of
false ->
false;
true ->
LogDirPath =
iolist_to_binary(filename:dirname(
ejabberd_logger:get_log_path())),
filename:join([LogDirPath, <<"spam_dump_", Host/binary, ".log">>]);
B when is_binary(B) ->
B
end.
%%--------------------------------------------------------------------
%%| Copied from mod_antispam.erl
-spec get_proc_name(binary()) -> atom().
get_proc_name(Host) ->
gen_mod:get_module_proc(Host, ?MODULE_ANTISPAM).
-spec get_spam_filter_hosts() -> [binary()].
get_spam_filter_hosts() ->
[H || H <- ejabberd_option:hosts(), gen_mod:is_loaded(H, ?MODULE_ANTISPAM)].
%%--------------------------------------------------------------------
%%| vim: set foldmethod=marker foldmarker=%%|,%%-: