You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

150 lines
5.6 KiB

#!@l_prefix@/bin/perl
##
## milter-header -- Header Insertion MILTER Program
## Copyright (c) 2007-2009 Ralf S. Engelschall <rse@engelschall.com>
##
# requirements
require 5.008;
use warnings;
use strict;
use IO::All;
use IO::File;
use OSSP::cfg;
use Date::Format;
use Data::Dumper;
use Sendmail::PMilter qw(:all);
# command line handling
my $cfgfile = $ARGV[0] || "@l_prefix@/etc/milter-header/milter-header.cfg";
# parse configuration file
my $txt < io($cfgfile);
my $cfg = new OSSP::cfg::simple;
$cfg->parse($txt);
my $tree = $cfg->unpack();
undef $cfg;
# determine milter parameters
my ($socket) = map { $_->[1] } grep { ref($_) eq 'ARRAY' and $_->[0] eq 'socket' }
map { @{$_->[1]} } grep { ref($_) eq 'ARRAY' and $_->[0] eq 'milter' } @{$tree};
my ($logfile) = map { $_->[1] } grep { ref($_) eq 'ARRAY' and $_->[0] eq 'logfile' }
map { @{$_->[1]} } grep { ref($_) eq 'ARRAY' and $_->[0] eq 'milter' } @{$tree};
my ($max_backlog) = map { $_->[1] } grep { ref($_) eq 'ARRAY' and $_->[0] eq 'max_backlog' }
map { @{$_->[1]} } grep { ref($_) eq 'ARRAY' and $_->[0] eq 'milter' } @{$tree};
my ($max_childs) = map { $_->[1] } grep { ref($_) eq 'ARRAY' and $_->[0] eq 'max_childs' }
map { @{$_->[1]} } grep { ref($_) eq 'ARRAY' and $_->[0] eq 'milter' } @{$tree};
my ($max_requests) = map { $_->[1] } grep { ref($_) eq 'ARRAY' and $_->[0] eq 'max_requests' }
map { @{$_->[1]} } grep { ref($_) eq 'ARRAY' and $_->[0] eq 'milter' } @{$tree};
# determine header actions
my @action = ();
foreach my $action (map { $_->[1] } grep { ref($_) eq 'ARRAY' and $_->[0] eq 'action' } @{$tree}) {
my @require = map { my $value = $_->[2]; $value =~ s/^\s+//s; $value =~ s/\s+$//; my $x = { -name => $_->[1], -value => $value }; $x }
grep { ref($_) eq 'ARRAY' and $_->[0] eq 'require' } @{$action};
my @insert = map { my $value = $_->[2]; $value =~ s/^\s+//s; $value =~ s/\s+$//; my $x = { -name => $_->[1], -value => $value }; $x }
grep { ref($_) eq 'ARRAY' and $_->[0] eq 'insert' } @{$action};
push(@action, { -require => [ @require ], -insert => [ @insert ] });
}
# helper function for logging
sub logbook ($;@) {
my ($fmt, @args) = @_;
my $log = new IO::File ">>$logfile";
if (defined $log) {
$log->printf("%s %05d %s\n", time2str("%Y-%m-%d %H:%M:%S", time()), $$, sprintf($fmt, @args));
$log->close();
}
}
# catch run-time warnings from Sendmail::PMilter
$SIG{__WARN__} = sub {
my ($err) = @_;
$err =~ s/\r?\n$//s;
logbook("MILTER Warning: %s", $err);
};
# create milter
umask(002);
unlink($socket);
my $milter = new Sendmail::PMilter;
my $dispatcher = Sendmail::PMilter::prefork_dispatcher(
max_children => $max_childs,
max_requests_per_child => $max_requests,
);
$milter->set_dispatcher($dispatcher);
$milter->set_listen($max_backlog);
$milter->setconn("local:$socket");
$milter->register(
"milter-header", {
'connect' => \&milter_cb_connect,
'header' => \&milter_cb_header,
'eom' => \&milter_cb_eom,
},
(SMFI_V2_ACTS|SMFIF_ADDHDRS)
);
logbook("started");
$milter->main();
logbook("terminated");
exit(0);
# milter hook: connect
sub milter_cb_connect {
my ($ctx, $hostname, $hostaddr) = @_;
my $status = { -header => [] };
$ctx->setpriv($status);
return SMFIS_CONTINUE;
}
# milter hook: header
sub milter_cb_header {
my ($ctx, $name, $value) = @_;
my $status = $ctx->getpriv();
push(@{$status->{-header}}, { -name => $name, -value => $value });
return SMFIS_CONTINUE;
};
# milter hook: end of message
sub milter_cb_eom ($;@) {
my ($ctx) = @_;
my $status = $ctx->getpriv();
# loop over all header actions
foreach my $action (@action) {
# check number of fulfilled requirements
my $fulfilled = 0;
foreach my $require (@{$action->{-require}}) {
#logbook("check requirement: header \"%s\" regex \"%s\"", $require->{-name}, $require->{-value});
foreach my $header (@{$status->{-header}}) {
#logbook("compare with header \"%s\" value \"%s\"", $header->{-name}, $header->{-value});
my $regex = $require->{-value};
if ( uc($header->{-name}) eq uc($require->{-name})
and $header->{-value} =~ m/$regex/) {
#logbook("requirement fulfilled by header \"%s\" value \"%s\"", $header->{-name}, $header->{-value});
$fulfilled++;
}
}
}
if ($fulfilled == scalar(@{$action->{-require}})) {
# if _all_ requirements are fulfilled
foreach my $insert (@{$action->{-insert}}) {
my $value = $insert->{-value};
$value =~ s/\s*\n+$//s;
$value =~ s/^\s+//s;
$value =~ s/\n\s+/\n\t/sg;
if (grep { $_->{-name} eq $insert->{-name} } @{$status->{-header}}) {
#logbook("change existing header \"%s\" value \"%s\"", $insert->{-name}, $insert->{-value});
logbook("change existing header \"%s\"", $insert->{-name});
$ctx->chgheader($insert->{-name}, 0, $value);
}
else {
#logbook("add new header \"%s\" value \"%s\"", $insert->{-name}, $insert->{-value});
logbook("add new header \"%s\"", $insert->{-name});
$ctx->addheader($insert->{-name}, $value);
}
}
}
}
return SMFIS_CONTINUE;
}