Implement auto-history
authorHeiko Schlittermann <hs@schlittermann.de>
Sat, 04 Jun 2016 23:05:29 +0200
changeset 72 f095f28db247
parent 71 46ba051d29bd
child 73 e2559ee78cb3
Implement auto-history
lib/Exim/Grey.pm
t/00-basic.t
--- a/lib/Exim/Grey.pm	Sat Jun 04 22:08:13 2016 +0200
+++ b/lib/Exim/Grey.pm	Sat Jun 04 23:05:29 2016 +0200
@@ -52,11 +52,13 @@
 
     # we do not know anything about the client -> unknown
     if (not exists $h{$key}) {
-        $h{$key} = "$now $now 0\0";
+        $h{$key} = "$now $now 0"
+            . (defined $auto ? " auto=$auto" : '')
+            . "\0";
         return 'yes';
     }
 
-    my ($created, undef, $count) = split /[ \0]/, $h{$key};
+    my ($created, undef, $count, $flags) = split /[ \0]/, $h{$key};
 
     # we know the client, but last contact was recently (too fast)
     if ($now - $created < $delay) {
@@ -66,7 +68,7 @@
     # we know the client, was patiently enough
     ++$count;
     $h{$key} = "$created $now $count\0";
-    whitelist($auto, \%h) if defined $auto;
+    whitelist(\%h, $auto, $flags =~ /auto=(\S+)/) if defined $auto;
     return 'no';
 }
 
@@ -91,9 +93,15 @@
 # same greylist criteria
 
 sub whitelist {
-    my ($item, $h) = @_;
+    my ($h, @items) = @_;
     my $now = time;
-    $h->{"$item\0"} = "$now $now 1 auto\0";
+    $h->{"$_\0"} = "$now $now 1 auto\0"
+        foreach uniq(@items);
+}
+
+sub uniq {
+    my %h = map { $_, undef } @_;
+    return keys %h;
 }
 
 sub is_whitelisted {
--- a/t/00-basic.t	Sat Jun 04 22:08:13 2016 +0200
+++ b/t/00-basic.t	Sat Jun 04 23:05:29 2016 +0200
@@ -6,19 +6,33 @@
 
 use_ok 'Exim::Grey' => qw(unseen seen) or BAIL_OUT;
 
-my $db = File::Temp->new();
-
-is seen('a->x', 1, "$db"), 'no' => 'not seen a->x';
-is unseen('a->b', 1, "$db"), 'yes' => 'unseen a->b';
-is unseen('a->b', 1, "$db"), 'yes' => 'unseen a->b';
+subtest 'simple' => sub {
+    my $db = File::Temp->new();
+    is seen('a->x', 0, "$db"), 'no' => 'not seen a->x';
+    is unseen('a->b', 0,   "$db"), 'yes' => 'unseen a->b';
+    is unseen('a->b', 600, "$db"), 'yes' => 'unseen a->b with 600s delay';
+    is unseen('a->b', 0,   "$db"), 'no'  => 'not unseen a->b';
+    is seen('a->b', 600, "$db"), 'no' => 'not seen a->b with 600s delay';
+};
 
-is unseen('x->y/1.1.1.1', 1, "$db"), 'yes' => 'unseen x->y/1.1.1.1';
-is unseen('x->y/1.1.1.1', 1, "$db"), 'yes' => 'unseen x->y/1.1.1.1';
+subtest 'whitelist' => sub {
+    my $db = File::Temp->new();
+    is unseen('x->y/1.1.1.1', 1, "$db"), 'yes' => 'unseen x->y/1.1.1.1';
+    is unseen('x->y/1.1.1.1', 1, "$db"), 'yes' => 'unseen x->y/1.1.1.1';
 
-is unseen('a->b',         0, "$db"), 'no' => 'not unseen a->b';
-is unseen('x->y/1.1.1.1', 0, "$db"), 'no' => 'not unseen x->y/1.1.1.1';
-is unseen('x->z/1.1.1.1', 0, "$db"), 'no' => 'not unseen x->z/1.1.1.1';
-is seen('x->z/1.1.1.1', 0, "$db"), 'yes' => 'seen x->z/1.1.1.1';
+    is unseen('x->y/1.1.1.1', 0, "$db"), 'no' => 'not unseen x->y/1.1.1.1';
+    is unseen('x->z/1.1.1.1', 0, "$db"), 'no' => 'not unseen x->z/1.1.1.1';
+    is seen('x->z/1.1.1.1', 0, "$db"), 'yes' => 'seen x->z/1.1.1.1';
 
-is unseen('a->b', 600, "$db"), 'yes' => 'unseen a->b with 600s delay';
-is seen('a->b', 600, "$db"), 'no' => 'not seen a->b with 600s delay';
+};
+
+subtest 'whitelist multiple subkeys' => sub {
+    my $db = File::Temp->new();
+
+    is unseen('x->y/1.1.1.1', 0, "$db"), 'yes' => 'unseen x->y/1.1.1.1';
+    is unseen('x->y/2.2.2.2', 0, "$db"), 'no' => 'not unseen x->y/2.2.2.2';
+
+    is unseen('a->b/1.1.1.1', 0, "$db"), 'no' => 'not unseen (whitelisted source)';
+    is unseen('a->c/2.2.2.2', 0, "$db"), 'no' => 'not unseen (whitelisted source)';
+    is unseen('x->y/3.3.3.3', 0, "$db"), 'no' => 'not unseen x->y/3.3.3.3 (known key)';
+};