ssoma-mda: duplicate prevention
authorEric Wong <e@80x24.org>
Tue, 8 Apr 2014 01:36:53 +0000 (01:36 +0000)
committerEric Wong <e@80x24.org>
Tue, 8 Apr 2014 01:36:53 +0000 (01:36 +0000)
This is mainly for public-inbox, as duplicate message IDs are
usually evidence something is suspicious or a misconfigured SMTP
server/client.

Documentation/ssoma-mda.txt
lib/Ssoma/MDA.pm
ssoma-mda
t/all.t

index 0ef1501f4dcfd4adf66b8897176c507bcfe3d730..07cfd126aed54d6fbc9276a0638547b07ebd16c9 100644 (file)
@@ -6,7 +6,7 @@ ssoma-mda - mail delivery agent for ssoma
 
 # SYNOPSIS
 
-ssoma-mda /path/to/ssoma/repository.git < message
+ssoma-mda [-1] /path/to/ssoma/repository.git < message
 
 # DESCRIPTION
 
@@ -20,6 +20,12 @@ ssoma-mda takes no command-line options and does not alter its own
 permissions.  This must be done by the MTA or MDA which invokes
 ssoma-mda.
 
+# OPTIONS
+
+-1
+:      Only allow a Message-ID to appear once in the database.
+       Future messages with an identical Message-ID will not be allowed.
+
 # FILES
 
 See ssoma_repository(5) for details.
index 25d0fd6c80e259a14b9b7347474636134abae2e7..3fe7e0ba22af78c5fa5d2fdca255d0a4803a02ef 100644 (file)
@@ -68,7 +68,7 @@ sub tree_update {
 # this appends the given message-id to the git repo, requires locking
 # (Ssoma::Git::sync_do)
 sub append {
-       my ($self, $path, $simple) = @_;
+       my ($self, $path, $simple, $once) = @_;
 
        my $git = $self->{git};
        my $ref = $self->{ref};
@@ -82,6 +82,10 @@ sub append {
 
        if ($? == 0) { # rare, object already exists
                chomp $type;
+               if ($once) {
+                       my $mid = $simple->header("Message-ID");
+                       die "CONFLICT: Message-ID: $mid exists ($path)\n";
+               }
 
                # we return undef here if the message already exists
                if ($type eq "blob") {
@@ -103,7 +107,7 @@ sub append {
 
 # the main entry point takes an Email::Simple object
 sub deliver {
-       my ($self, $simple) = @_;
+       my ($self, $simple, $once) = @_;
        my $git = $self->{git};
 
        # convert the Message-ID into a path
@@ -124,7 +128,7 @@ sub deliver {
 
        my $sub = sub {
                $git->tmp_index_do(sub {
-                       $self->append($path, $simple);
+                       $self->append($path, $simple, $once);
                });
        };
        $git->sync_do(sub { $git->tmp_git_do($sub) });
index af5f63f2e34d2a7a232ba293cbdc46c205c00e5f..d7632247ab5a039c36cc5af1554af152361f64e8 100755 (executable)
--- a/ssoma-mda
+++ b/ssoma-mda
@@ -4,18 +4,19 @@
 # This is the command-line mail delivery agent for servers.
 # Try to keep this small as it may be invoked frequently for each message
 # delivered.
-my $usage = "ssoma-mda /path/to/git/repo < /path/to/rfc2822_message";
+my $usage = "ssoma-mda [-1] /path/to/git/repo < /path/to/rfc2822_message";
 use strict;
 use warnings;
 use Ssoma::MDA;
 use Ssoma::Git;
 use Email::Simple;
-my $repo = shift @ARGV or die "Usage: $usage\n";
+my $once = $ARGV[0] eq "-1";
+my $repo = pop @ARGV or die "Usage: $usage\n";
 my $git = Ssoma::Git->new($repo);
 my $mda = Ssoma::MDA->new($git);
 my $simple;
 {
        local $/;
-       $simple = Email::Simple->new(<>);
+       $simple = Email::Simple->new(<STDIN>);
 }
-$mda->deliver($simple);
+$mda->deliver($simple, $once);
diff --git a/t/all.t b/t/all.t
index 9c14d59ebe629629780445a43f609447288f8619..bd71713ceaf89a38bba8510a1fdba9eab3816036 100644 (file)
--- a/t/all.t
+++ b/t/all.t
@@ -11,6 +11,7 @@ my $rm = "blib/script/ssoma-rm";
 my $tmp = tempdir(CLEANUP => 1);
 use File::Temp qw/tempdir/;
 use Email::Simple;
+use IPC::Run qw(run);
 
 ok(-x $mda, "$mda is executable");
 ok(-x $cli, "$cli is executable");
@@ -195,4 +196,21 @@ EOF
        is(scalar @tree, 2, "two messages sitting in a tree");
 }
 
+# duplicate detection
+{
+       my $simple = Email::Simple->new(<<'EOF');
+From: moi@example.com
+To: you@example.com
+Message-Id: <666666@example.com>
+Subject: xxx
+
+OMFG
+EOF
+       $simple = $simple->as_string;
+       my ($out, $err) = ("", "");
+       run([$mda, "-1", "$tmp/input.git"], \$simple, \$out, \$err);
+       isnt($?, 0, "$mda exited with failure");
+       like($err, qr/CONFLICT/, "conflict message detected");
+}
+
 done_testing();