#!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); }