qpsmtpd/plugins/check_basicheaders

115 lines
2.9 KiB
Plaintext
Raw Normal View History

#!perl -w
=head1 NAME
check_basicheaders - Make sure both From and Date headers are present, and
do optional range checking on the Date header.
=head1 DESCRIPTION
Rejects messages that do not have a From or Date header or are completely
empty.
Can also reject messages where the date in the Date header is more than
some number of the days in the past or future.
=head1 CONFIGURATION
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
2004 - Written by Jim Winstead Jr.
2012 - added logging, named arguments, reject_type, tests - Matt Simerson
=head1 LICENSE
Released to the public domain, 26 March 2004.
=cut
use Date::Parse qw(str2time);
sub register {
my ($self, $qp, @args) = @_;
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 $deny = $self->{_args}{reject_type} eq 'temp' ? DENYSOFT : DENY;
if ( $transaction->data_size == 0 ) {
$self->log(LOGINFO, "fail: no data");
return ($deny, "You have to send some data first");
};
my $header = $transaction->header or do {
$self->log(LOGINFO, "fail: no headers");
return ($deny, "missing header");
};
if ( ! $header->get('From') ) {
$self->log(LOGINFO, "fail: no from");
return ($deny, "We require a valid From header")
};
my $date = $header->get('Date') or do {
$self->log(LOGINFO, "fail: no date");
return ($deny, "We require a valid Date header");
};
my $days = $self->{_args}{days};
if ( ! defined $days ) {
$self->log(LOGINFO, "pass: no days arg");
return (DECLINED);
};
my $ts = str2time($date) or do {
$self->log(LOGINFO, "skip: date not parseable ($date)");
return (DECLINED);
};
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")
};
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")
};
$self->log(LOGINFO, "pass");
return (DECLINED);
}