about summary refs log tree commit homepage
diff options
context:
space:
mode:
-rw-r--r--Makefile.PL1
-rw-r--r--lib/PublicInbox.pm33
-rwxr-xr-xpublic-inbox-mda2
-rw-r--r--t/recipient.t59
4 files changed, 95 insertions, 0 deletions
diff --git a/Makefile.PL b/Makefile.PL
index b2fbb18f..5d3ee75c 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -15,6 +15,7 @@ WriteMakefile(
                 'Email::MIME' => 0,
                 'Email::MIME::ContentType' => 0,
                 'Email::Filter' => 0,
+                'Email::Address' => 0,
         },
 );
 
diff --git a/lib/PublicInbox.pm b/lib/PublicInbox.pm
new file mode 100644
index 00000000..b51c000f
--- /dev/null
+++ b/lib/PublicInbox.pm
@@ -0,0 +1,33 @@
+# Copyright (C) 2013, Eric Wong <normalperson@yhbt.net> and all contributors
+# License: AGPLv3 or later (https://www.gnu.org/licenses/agpl-3.0.txt)
+package PublicInbox;
+use strict;
+use warnings;
+use Email::Address;
+
+# drop plus addressing for matching
+sub __drop_plus {
+        my ($str_addr) = @_;
+        $str_addr =~ s/\+.*\@/\@/;
+        $str_addr;
+}
+
+# do not allow Bcc, only Cc and To if ORIGINAL_RECIPIENT (postfix) env is set
+sub recipient_specified {
+        my ($klass, $filter) = @_;
+        my $or = $ENV{ORIGINAL_RECIPIENT};
+        defined($or) or return 1; # for imports
+        my @or = Email::Address->parse($or);
+        my $oaddr = __drop_plus($or[0]->address);
+        $oaddr = qr/\b\Q$oaddr\E\b/i;
+        my @to = Email::Address->parse($filter->to);
+        my @cc = Email::Address->parse($filter->cc);
+        foreach my $addr (@to, @cc) {
+                if (__drop_plus($addr->address) =~ $oaddr) {
+                        return 1;
+                }
+        }
+        return 0;
+}
+
+1;
diff --git a/public-inbox-mda b/public-inbox-mda
index 177c891e..72f1eac4 100755
--- a/public-inbox-mda
+++ b/public-inbox-mda
@@ -4,6 +4,7 @@
 use strict;
 use warnings;
 use Email::Filter;
+use Email::Address;
 use PublicInbox::Filter;
 use IPC::Run qw(run);
 my $usage = "public-inbox-mda main_repo fail_repo < rfc2822_message";
@@ -14,6 +15,7 @@ my $max = 1024 * 500; # same as spamc
 
 my $filtered;
 if (length($filter->simple->as_string) <= $max
+    && PublicInbox->recipient_specified($filter)
     && do_spamc($filter->simple, \$filtered)) {
         # update our message with SA headers (in case our filter rejects it)
         my $simple = Email::Simple->new($filtered);
diff --git a/t/recipient.t b/t/recipient.t
new file mode 100644
index 00000000..9cb1969e
--- /dev/null
+++ b/t/recipient.t
@@ -0,0 +1,59 @@
+# Copyright (C) 2014, Eric Wong <normalperson@yhbt.net> and all contributors
+# License: AGPLv3 or later (https://www.gnu.org/licenses/agpl-3.0.txt)
+use strict;
+use warnings;
+use Test::More;
+use Email::Simple;
+use Email::Filter;
+use PublicInbox;
+
+sub do_checks {
+        my ($s) = @_;
+
+        my $f = Email::Filter->new(data => $s->as_string);
+        local %ENV;
+        delete $ENV{ORIGINAL_RECIPIENT};
+
+        ok(PublicInbox->recipient_specified($f),
+                "ORIGINAL_RECIPIENT unset is OK");
+
+        $ENV{ORIGINAL_RECIPIENT} = 'foo@example.com';
+        ok(!PublicInbox->recipient_specified($f),
+                "wrong ORIGINAL_RECIPIENT rejected");
+
+        $ENV{ORIGINAL_RECIPIENT} = 'b@example.com';
+        ok(PublicInbox->recipient_specified($f),
+                "ORIGINAL_RECIPIENT in To: is OK");
+
+        $ENV{ORIGINAL_RECIPIENT} = 'c@example.com';
+        ok(PublicInbox->recipient_specified($f),
+                "ORIGINAL_RECIPIENT in Cc: is OK");
+}
+
+{
+        do_checks(Email::Simple->create(
+                header => [
+                        From => 'a@example.com',
+                        To => 'b@example.com',
+                        Cc => 'c@example.com',
+                        'Content-Type' => 'text/plain',
+                        Subject => 'this is a subject',
+                ],
+                body => "hello world\n",
+        ));
+}
+
+{
+        do_checks(Email::Simple->create(
+                header => [
+                        From => 'a@example.com',
+                        To => 'b+plus@example.com',
+                        Cc => 'John Doe <c@example.com>',
+                        'Content-Type' => 'text/plain',
+                        Subject => 'this is a subject',
+                ],
+                body => "hello world\n",
+        ));
+}
+
+done_testing();