./mimedefang-filter-select
# -*- Perl -*-
#
# $Id: mimedefang-filter-select,v 1.5 2005/05/23 21:55:32 johnpc Exp $
#
# mimedefang filter selector
# loads settings from mimedefang-filter.conf,
# and dispatches various mimedefang filter_* calls to all mentioned
# modules.
use strict;
use lib "/usr/local/etc/mimedefang";
use warnings;
use Module::Load;
use Mimedefang qw(filterchain_reset filterchain_tookaction);
use MailFilter::Settings qw(:config);
### routines to call in modules
### grouped per interface style
### routines that do not return anything:
my @routines_noret = qw(
filter_begin
filter_initialize
filter_cleanup
filter_tick
);
### routines that return a $code indicating the action
my @routines_code = qw(
filter_relay
filter_sender
filter_recipient
);
### routines that indicate an action by calling action_* functions
my @routines_action = qw(
filter
filter_multipart
filter_end
);
### XXX TODO: unsupported filter callbacks are:
### filter_unknown_cmd
### filter_map
### regex that matched all valid returns from @routines_code
my $valid_filter_code = qr{^
(?:REJECT
|CONTINUE
|TEMPFAIL
|DISCARD
|ACCEPT_AND_NO_MORE_FILTERING
)$
}x;
### all routines
my @routines = (@routines_noret, @routines_code, @routines_action);
# hash per filter_* routine, contains arrayref of [coderef, name] to call, eg:
# $call{"filter_begin"} = [
# [ \&MailFilter::Virusscan::filter_begin,
# "MailFilter::Virusscan::filter_begin" ],
# [ \&MailFilter::Blacklists::filter_begin,
# "MailFilter::Blacklists::filter_begin" ],
# ];
my %call;
###
for my $module ( @FilterModules ) {
load $module;
for my $r ( @routines ) {
my $name = $module . "::" . $r;
if ( exists &$name ) {
push @{ $call{$r} }, [ \&$name, $name ];
}
}
}
### call all filters, for those that do not return anything
### these filter calls also do not take arguments, and do not return
### anything special.
for my $fun ( @routines_noret ) {
no strict 'refs';
*$fun = sub {
for my $c ( @{ $call{$fun} } ) {
warn "Calling " . $c->[1] . "()\n" if $modules_debug;
$c->[0]->();
}
} if $call{$fun};
}
### call filters while they return 'CONTINUE'
for my $fun ( @routines_code ) {
no strict 'refs';
*$fun = sub {
my($code, $msg, $smtp_code, $smtp_dsn, $delay);
$code = 'CONTINUE';
for my $c ( @{ $call{$fun} } ) {
warn "Calling " . $c->[1] . "(" . join(",", @_) . ")\n"
if $modules_debug;
($code, $msg, $smtp_code, $smtp_dsn, $delay) = $c->[0]->(@_);
### verify the routine returned a valid return value.
if ( !defined $code ) {
warn "Warning: " . $c->[1] . " did not return a return code\n";
$code = 'CONTINUE';
}
elsif ( $code =~ /^-?\d+$/ ) {
warn "Warning: " . $c->[1] .
" returned a deprecated numeric return code: $code\n";
$code = 'CONTINUE' if $code > 0;
}
elsif ( $code !~ /$valid_filter_code/ ) {
warn "Warning: " . $c->[1] .
" returned an invalid return code: $code\n";
### treat this like a compile-time failure, so tempfail.
### mimedefang default would be to reject
$code = 'TEMPFAIL';
}
last if $code ne 'CONTINUE';
}
return ($code, $msg, $smtp_code, $smtp_dsn, $delay);
} if $call{$fun};
}
### call filters while they do not take any action_*
for my $fun ( @routines_action ) {
no strict 'refs';
*$fun = sub {
filterchain_reset();
for my $c ( @{ $call{$fun} } ) {
warn "Calling " . $c->[1] . "(" . join(",", @_) . ")\n"
if $modules_debug;
$c->[0]->(@_);
last if filterchain_tookaction();
}
} if $call{$fun};
}
1;