basicheaders: added log messages, tests, named args
added log messages at each exit point added tests added reject_type option (defer -vs- deny) added named argument parsing
This commit is contained in:
parent
f37fba7c2b
commit
49dc8bc117
@ -15,13 +15,30 @@ some number of the days in the past or future.
|
||||
|
||||
=head1 CONFIGURATION
|
||||
|
||||
Takes one optional parameter, the number of days in the future or past
|
||||
beyond which to reject messages. (The default is to not reject messages
|
||||
based on the date.)
|
||||
The following optional parameters exist:
|
||||
|
||||
=head2 days
|
||||
|
||||
The number of days in the future or past beyond which to reject messages. When
|
||||
unset, messages are not rejected based on the date.
|
||||
|
||||
check_basicheaders [ days 3 ]
|
||||
|
||||
=head2 reject_type
|
||||
|
||||
Whether to issue a permanent or temporary rejection. The default is permanent.
|
||||
|
||||
check_basicheaders reject_type [ temp | perm ]
|
||||
|
||||
Switching to a temporary rejection is most useful when testing the plugin. It
|
||||
allows an administrator to watch for a test period and make sure no valid mail
|
||||
is getting rejected.
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Written by Jim Winstead Jr.
|
||||
2004 - Written by Jim Winstead Jr.
|
||||
|
||||
2012 - added logging, named arguments, reject_type, tests - Matt Simerson
|
||||
|
||||
=head1 LICENSE
|
||||
|
||||
@ -32,40 +49,66 @@ Released to the public domain, 26 March 2004.
|
||||
use Date::Parse qw(str2time);
|
||||
|
||||
sub register {
|
||||
my ($self, $qp, @args) = @_;
|
||||
my ($self, $qp, @args) = @_;
|
||||
|
||||
if (@args > 0) {
|
||||
$self->{_days} = $args[0];
|
||||
$self->log(LOGWARN, "WARNING: Ignoring additional arguments.") if (@args > 1);
|
||||
}
|
||||
if ( @args == 1 ) {
|
||||
$self->log(LOGWARN, "deprecated arguments. Update your arguments to this plugin");
|
||||
$self->{_args}{days} = $args[0];
|
||||
}
|
||||
elsif ( @args % 2 ) {
|
||||
$self->log(LOGWARN, "invalid arguments");
|
||||
}
|
||||
else {
|
||||
$self->{_args} = { @args };
|
||||
};
|
||||
}
|
||||
|
||||
sub hook_data_post {
|
||||
my ($self, $transaction) = @_;
|
||||
my ($self, $transaction) = @_;
|
||||
|
||||
return (DENY, "You have to send some data first")
|
||||
if $transaction->data_size == 0;
|
||||
my $deny = $self->{_args}{reject_type} eq 'temp' ? DENYSOFT : DENY;
|
||||
|
||||
my $header = $transaction->header;
|
||||
return (DENY, "Mail with no From header not accepted here")
|
||||
unless $header && $header->get('From');
|
||||
if ( $transaction->data_size == 0 ) {
|
||||
$self->log(LOGINFO, "fail: no data");
|
||||
return ($deny, "You have to send some data first");
|
||||
};
|
||||
|
||||
my $date = $header->get('Date');
|
||||
my $header = $transaction->header or do {
|
||||
$self->log(LOGINFO, "fail: no headers");
|
||||
return ($deny, "missing header");
|
||||
};
|
||||
|
||||
return (DENY, "Mail with no Date header not accepted here")
|
||||
unless $date;
|
||||
if ( ! $header->get('From') ) {
|
||||
$self->log(LOGINFO, "fail: no from");
|
||||
return ($deny, "We require a valid From header")
|
||||
};
|
||||
|
||||
return (DECLINED) unless defined $self->{_days};
|
||||
my $date = $header->get('Date') or do {
|
||||
$self->log(LOGINFO, "fail: no date");
|
||||
return ($deny, "We require a valid Date header");
|
||||
};
|
||||
|
||||
my $ts = str2time($date);
|
||||
my $days = $self->{_args}{days};
|
||||
if ( ! defined $days ) {
|
||||
$self->log(LOGINFO, "pass: no days arg");
|
||||
return (DECLINED);
|
||||
};
|
||||
|
||||
return (DECLINED) unless $ts;
|
||||
my $ts = str2time($date) or do {
|
||||
$self->log(LOGINFO, "skip: date not parseable ($date)");
|
||||
return (DECLINED);
|
||||
};
|
||||
|
||||
return (DENY, "The Date in the header was too far in the past")
|
||||
if $ts < time - ($self->{_days}*24*3600);
|
||||
if ( $ts < time - ($days*24*3600) ) {
|
||||
$self->log(LOGINFO, "fail: date too old ($date)");
|
||||
return ($deny, "The Date in the header is too far in the past")
|
||||
};
|
||||
|
||||
return (DENY, "The Date in the header was too far in the future")
|
||||
if $ts > time + ($self->{_days}*24*3600);
|
||||
if ( $ts > time + ($days*24*3600) ) {
|
||||
$self->log(LOGINFO, "fail: date in future ($date)");
|
||||
return ($deny, "The Date in the header is too far in the future")
|
||||
};
|
||||
|
||||
return (DECLINED);
|
||||
$self->log(LOGINFO, "pass");
|
||||
return (DECLINED);
|
||||
}
|
||||
|
63
t/plugin_tests/check_basicheaders
Normal file
63
t/plugin_tests/check_basicheaders
Normal file
@ -0,0 +1,63 @@
|
||||
#!perl -w
|
||||
|
||||
use strict;
|
||||
use Data::Dumper;
|
||||
|
||||
use Qpsmtpd::Address;
|
||||
use Qpsmtpd::Constants;
|
||||
|
||||
sub register_tests {
|
||||
my $self = shift;
|
||||
|
||||
$self->register_test("test_hook_data_post", 5);
|
||||
}
|
||||
|
||||
sub test_hook_data_post {
|
||||
my $self = shift;
|
||||
|
||||
my $transaction = $self->qp->transaction;
|
||||
my $test_email = 'matt@example.com';
|
||||
my $address = Qpsmtpd::Address->new( "<$test_email>" );
|
||||
my $header = Mail::Header->new(Modify => 0, MailFrom => "COERCE");
|
||||
my $now = `date`;
|
||||
my $future = `date -v +6d`;
|
||||
my $past = `date -v -6d`;
|
||||
$self->{_args}{days} = 5;
|
||||
|
||||
$transaction->sender($address);
|
||||
$transaction->header($header);
|
||||
$transaction->header->add('From', "<$test_email>");
|
||||
$transaction->header->add('Date', $now );
|
||||
$transaction->body_write( "test message body " );
|
||||
|
||||
my ($code, $mess) = $self->hook_data_post( $transaction );
|
||||
cmp_ok( DECLINED, '==', $code, "okay" );
|
||||
|
||||
$transaction->header->delete('Date');
|
||||
($code, $mess) = $self->hook_data_post( $transaction );
|
||||
cmp_ok( DENY, '==', $code, "missing date ( $mess )" );
|
||||
|
||||
$transaction->header->delete('From');
|
||||
$transaction->header->add('Date', $now );
|
||||
($code, $mess) = $self->hook_data_post( $transaction );
|
||||
cmp_ok( DENY, '==', $code, "missing from ( $mess )" );
|
||||
|
||||
if ( $future ) {
|
||||
$transaction->header->replace('Date', $future );
|
||||
($code, $mess) = $self->hook_data_post( $transaction );
|
||||
cmp_ok( DENY, '==', $code, "too new ( $mess )" );
|
||||
|
||||
$transaction->header->replace('Date', $past );
|
||||
($code, $mess) = $self->hook_data_post( $transaction );
|
||||
cmp_ok( DENY, '==', $code, "too old ( $mess )" );
|
||||
|
||||
}
|
||||
else {
|
||||
ok( 1, "skip: unable to use 'date' output");
|
||||
ok( 1, "skip: unable to use 'date' output");
|
||||
}
|
||||
|
||||
$self->{_args}{reject_type} = 'temp';
|
||||
($code, $mess) = $self->hook_data_post( $transaction );
|
||||
cmp_ok( DENYSOFT, '==', $code, "defer, not deny ( $mess )" );
|
||||
};
|
Loading…
Reference in New Issue
Block a user