added log messages at each exit point
added tests
added reject_type option (defer -vs- deny)
added named argument parsing
---
 plugins/check_basicheaders        |  117 +++++++++++++++++++++++++------------
 t/plugin_tests/check_basicheaders |   63 ++++++++++++++++++++
 2 files changed, 143 insertions(+), 37 deletions(-)
 create mode 100644 t/plugin_tests/check_basicheaders

diff --git a/plugins/check_basicheaders b/plugins/check_basicheaders
index 973c768..114867a 100644
--- a/plugins/check_basicheaders
+++ b/plugins/check_basicheaders
@@ -15,57 +15,100 @@ 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:
 
-=head1 AUTHOR
-
-Written by Jim Winstead Jr.
+=head2 days
 
-=head1 LICENSE
-
-Released to the public domain, 26 March 2004.
+The number of days in the future or past beyond which to reject messages. When
+unset, messages are not rejected based on the date.
 
-=cut
+  check_basicheaders [ days 3 ]
 
-use Date::Parse qw(str2time);
+=head2 reject_type
 
-sub register {
-  my ($self, $qp, @args) = @_;
+Whether to issue a permanent or temporary rejection. The default is permanent.
 
-  if (@args > 0) {
-    $self->{_days} = $args[0];
-    $self->log(LOGWARN, "WARNING: Ignoring additional arguments.") if (@args > 
1);
-  }
-}
+  check_basicheaders reject_type [ temp | perm ]
 
-sub hook_data_post {
-  my ($self, $transaction) = @_;
+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.
 
-  return (DENY, "You have to send some data first")
-    if $transaction->data_size == 0;
-
-  my $header = $transaction->header;
-  return (DENY, "Mail with no From header not accepted here")
-    unless $header && $header->get('From');
+=head1 AUTHOR
 
-  my $date = $header->get('Date');
+ 2004 - Written by Jim Winstead Jr.
 
-  return (DENY, "Mail with no Date header not accepted here")
-    unless $date;
+ 2012 - added logging, named arguments, reject_type, tests - Matt Simerson
 
-  return (DECLINED) unless defined $self->{_days};
+=head1 LICENSE
 
-  my $ts = str2time($date);
+Released to the public domain, 26 March 2004.
 
-  return (DECLINED) unless $ts;
+=cut
 
-  return (DENY, "The Date in the header was too far in the past")
-    if $ts < time - ($self->{_days}*24*3600);
+use Date::Parse qw(str2time);
 
-  return (DENY, "The Date in the header was too far in the future")
-    if $ts > time + ($self->{_days}*24*3600);
+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 };
+    };
+}
 
-  return (DECLINED);
+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);
 }
diff --git a/t/plugin_tests/check_basicheaders 
b/t/plugin_tests/check_basicheaders
new file mode 100644
index 0000000..82e2f39
--- /dev/null
+++ b/t/plugin_tests/check_basicheaders
@@ -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 = 'm...@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 )" );
+};
-- 
1.7.9.6

Reply via email to