From 71abf270f5b11f147be839a9b057e106d0f8509f Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Tue, 1 Apr 2014 23:07:38 +0000 Subject: flesh out MDA and simplify config setup We will be reusing the config parsing code for the CGI script, too. --- MANIFEST | 14 ++++++++ lib/PublicInbox/Config.pm | 10 +++++- public-inbox-mda | 42 +++++++++++++++++------- t/fail-bin/spamc | 3 ++ t/main-bin/spamc | 3 ++ t/mda.t | 83 +++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 143 insertions(+), 12 deletions(-) create mode 100755 t/fail-bin/spamc create mode 100755 t/main-bin/spamc create mode 100644 t/mda.t diff --git a/MANIFEST b/MANIFEST index cddd320e..3dba6942 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1,9 +1,16 @@ .gitignore COPYING Documentation/design_notes.txt +INSTALL +MANIFEST Makefile.PL README +examples/public-inbox-config +lib/PublicInbox.pm +lib/PublicInbox/Config.pm +lib/PublicInbox/Feed.pm lib/PublicInbox/Filter.pm +lib/PublicInbox/View.pm public-inbox-mda sa_config/Makefile sa_config/README @@ -12,4 +19,11 @@ sa_config/user/.spamassassin/user_prefs scripts/dc-dlvr scripts/import_gmane_spool scripts/report-spam +t/config.t +t/fail-bin/spamc +t/feed.t t/filter.t +t/main-bin/spamc +t/mda.t +t/precheck.t +t/view.t diff --git a/lib/PublicInbox/Config.pm b/lib/PublicInbox/Config.pm index d91c28a9..9ba4ad32 100644 --- a/lib/PublicInbox/Config.pm +++ b/lib/PublicInbox/Config.pm @@ -3,12 +3,13 @@ package PublicInbox::Config; use strict; use warnings; +use File::Path::Expand qw/expand_filename/; # returns key-value pairs of config directives in a hash sub new { my ($class, $file) = @_; - local $ENV{GIT_CONFIG} = $file; + local $ENV{GIT_CONFIG} = defined $file ? $file : default_file(); my @cfg = `git config -l`; $? == 0 or die "git config -l failed: $?\n"; @@ -37,4 +38,11 @@ sub lookup { \%rv; } +sub default_file { + my $f = $ENV{PI_CONFIG}; + return $f if defined $f; + my $pi_dir = $ENV{PI_DIR} || expand_filename('~/.public-inbox/'); + "$pi_dir/config"; +} + 1; diff --git a/public-inbox-mda b/public-inbox-mda index 291b5574..8f63fa7e 100755 --- a/public-inbox-mda +++ b/public-inbox-mda @@ -3,17 +3,37 @@ # License: AGPLv3 or later (https://www.gnu.org/licenses/agpl-3.0.txt) use strict; use warnings; +my $usage = 'public-inbox-mda < rfc2822_message'; + use Email::Filter; use Email::Address; -use PublicInbox::Filter; +use File::Path::Expand qw/expand_filename/; use IPC::Run qw(run); -my $usage = "public-inbox-mda main_repo fail_repo < rfc2822_message"; -my $filter = Email::Filter->new(emergency => "~/emergency.mbox"); -my $main_repo = shift @ARGV or die "Usage: $usage\n"; -my $fail_repo = shift @ARGV or die "Usage: $usage\n"; +use constant MDA => 'ssoma-mda'; +use PublicInbox; +use PublicInbox::Filter; +use PublicInbox::Config; + +# n.b: hopefully we can setup the failbox path without bailing due to +# user error, we really want to setup the emergency destination ASAP +# in case there's bugs in our code or user error. +my $failbox = $ENV{PI_FAILBOX} || '~/public-inbox-fail.mbox'; +$failbox = expand_filename($failbox); + +# this reads the message from stdin +my $filter = Email::Filter->new(emergency => $failbox); +my $config = PublicInbox::Config->new; + +my $recipient = $ENV{RECIPIENT}; +defined $recipient or die "RECIPIENT not defined in ENV\n"; +my $dst = $config->lookup($recipient); +defined $dst or exit(1); +my $main_repo = $dst->{mainrepo} or exit(1); +my $fail_repo = $dst->{failrepo} or exit(1); +my $filtered; # string dest -my $filtered; -if (PublicInbox->precheck($filter) && do_spamc($filter->simple, \$filtered)) { +if (PublicInbox->precheck($filter, $recipient) && + do_spamc($filter->simple, \$filtered)) { # update our message with SA headers (in case our filter rejects it) my $simple = Email::Simple->new($filtered); $filtered = undef; @@ -23,18 +43,18 @@ if (PublicInbox->precheck($filter) && do_spamc($filter->simple, \$filtered)) { # run spamc again on the HTML-free message if (do_spamc($simple, \$filtered)) { $filter->simple(Email::Simple->new($filtered)); - $filter->pipe("ssoma-mda", $main_repo); + $filter->pipe(MDA, $main_repo); } else { - $filter->pipe("ssoma-mda", $fail_repo); + $filter->pipe(MDA, $fail_repo); } } else { # PublicInbox::Filter nuked everything, oops :x - $filter->pipe("ssoma-mda", $fail_repo); + $filter->pipe(MDA, $fail_repo); } } else { # if SA thinks it's spam or there's an error: # don't bother with our own filtering - $filter->pipe("ssoma-mda", $fail_repo); + $filter->pipe(MDA, $fail_repo); } die "Email::Filter failed to exit\n"; diff --git a/t/fail-bin/spamc b/t/fail-bin/spamc new file mode 100755 index 00000000..209e7dc9 --- /dev/null +++ b/t/fail-bin/spamc @@ -0,0 +1,3 @@ +#!/bin/sh +cat +exit 1 diff --git a/t/main-bin/spamc b/t/main-bin/spamc new file mode 100755 index 00000000..480d2f8e --- /dev/null +++ b/t/main-bin/spamc @@ -0,0 +1,3 @@ +#!/bin/sh +# trivial spamc mock +exec cat diff --git a/t/mda.t b/t/mda.t new file mode 100644 index 00000000..9f918d35 --- /dev/null +++ b/t/mda.t @@ -0,0 +1,83 @@ +# Copyright (C) 2014, Eric Wong 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::MIME; +use File::Temp qw/tempdir/; +use Cwd; +use IPC::Run qw(run); + +my $mda = "blib/script/public-inbox-mda"; +my $tmpdir = tempdir(CLEANUP => 1); +my $home = "$tmpdir/pi-home"; +my $pi_home = "$home/.public-inbox"; +my $pi_config = "$pi_home/config"; +my $maindir = "$tmpdir/main.git"; +my $faildir = "$tmpdir/fail.git"; +my $main_bin = getcwd()."/t/main-bin"; +my $main_path = "$main_bin:$ENV{PATH}"; # for spamc ham mock +my $fail_bin = getcwd()."/t/fail-bin"; +my $fail_path = "$fail_bin:$ENV{PATH}"; # for spamc spam mock +my $addr = 'test-public@example.com'; +my $cfgpfx = "publicinbox.test"; + +{ + ok(-x "$main_bin/spamc", + "spamc ham mock found (run in top of source tree"); + ok(-x "$fail_bin/spamc", + "spamc mock found (run in top of source tree"); + ok(-x $mda, "$mda is executable"); + is(1, mkdir($home, 0755), "setup ~/ for testing"); + is(1, mkdir($pi_home, 0755), "setup ~/.public-inbox"); + is(0, system(qw(git init -q --bare), $maindir), "git init (main)"); + is(0, system(qw(git init -q --bare), $faildir), "git init (fail)"); + + my %cfg = ( + "$cfgpfx.address" => $addr, + "$cfgpfx.mainrepo" => $maindir, + "$cfgpfx.failrepo" => $faildir, + ); + while (my ($k,$v) = each %cfg) { + is(0, system(qw(git config --file), $pi_config, $k, $v), + "setup $k"); + } +} + +{ + my $failbox = "$home/fail.mbox"; + local $ENV{PI_FAILBOX} = $failbox; + local $ENV{HOME} = $home; + local $ENV{RECIPIENT} = $addr; + my $simple = Email::Simple->new(< +To: You +Cc: $addr +Message-Id: +Subject: hihi +Date: Thu, 01 Jan 1970 00:00:00 +0000 + +EOF + my $in = $simple->as_string; + + # ensure successful message delivery + { + local $ENV{PATH} = $main_path; + run([$mda], \$in); + local $ENV{GIT_DIR} = $maindir; + my $rev = `git rev-list HEAD`; + like($rev, qr/\A[a-f0-9]{40}/, "good revision committed"); + } + + # ensure failures work + { + local $ENV{PATH} = $fail_path; + run([$mda], \$in); + local $ENV{GIT_DIR} = $faildir; + my $rev = `git rev-list HEAD`; + like($rev, qr/\A[a-f0-9]{40}/, "bad revision committed"); + } + ok(!-e $failbox, "nothing in PI_FAILBOX"); +} + +done_testing(); -- cgit v1.2.3-24-ge0c7