#!/usr/bin/perl
# usage:
#     summarise-mailbox-preserving-privacy \
#          [<our options>] [--] <email>
# our options:
#  -S<subject>       default: "summary of messages on <mailname>"
#  -F<state file>    default: $HOME/.summarise-mailbox/last<from options>
#  -f<mailbox>       passed to from(1)  must use -F if it contains / or |
#  -s<sender>        passed to from(1)  must use -F if it contains / or |
#  -q                throw away stderr and always exit 0
#  --		end of our options

# Copyright 2006 Ian Jackson <ian@chiark.greenend.org.uk>
#
# This script and its documentation (if any) are free software; you
# can redistribute it and/or modify them under the terms of the GNU
# General Public License as published by the Free Software Foundation;
# either version 3, or (at your option) any later version.
# 
# chiark-named-conf and its manpage are distributed in the hope that
# it will be useful, but WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
# PURPOSE.  See the GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License along
# with this program; if not, consult the Free Software Foundation's
# website at www.fsf.org, or the GNU Project website at www.gnu.org.


use strict (qw(refs));
use POSIX;
use IO::Handle;
use Fcntl (qw(:flock));

@from_options=();
$sendmail= '/usr/sbin/sendmail -odi -oee -oi -t';

umask 077;
while (@ARGV && $ARGV[0] =~ m/\-/) {
    $_= shift;
    if (m/^\-[fs]/) {
	push @from_options, $_;
    } elsif (s/^\-S//) {
	$subject= $_;
    } elsif (s/^\-F//) {
	$statefile= $_;
    } elsif (s/^\-q$//) {
	$quiet= 1;
	open STDERR, ">/dev/null";
	eval('END { $?=0; }');
    } elsif (m/^\-\-$/) {
	last;
    } else {
	die "$0: unknown option \`$_'\n";
    }
}
die unless @ARGV==1;
$emailto= shift @ARGV;

unless (defined $subject) {
    my ($our_hostname);
    open M, "/etc/mailname" or die $!;
    defined($our_hostname= <M>) or die $!;
    chomp($our_hostname);
    close M;
    $subject= "summary of messages on $our_hostname";
}

unless (defined $statefile) {
    my ($dir);
    die "$0: -F needed with that -f or -s\n"
	if grep m:[|/]:, @from_options;
    die "$0: no HOME in environment\n" unless defined $ENV{'HOME'};
    $dir= $ENV{'HOME'}.'/.summarise-mailbox';
    mkdir $dir, 02700 or $!==&EEXIST or die "$dir: $!";
    $statefile= $dir.'/last'.
	join('|',@from_options);
}

$statefile= "./$statefile" unless $statefile =~ m,^/,;
$lockfile= $statefile.'.lock';
$errfile= $statefile.'.err';

open L, "+> $lockfile" or die "$lockfile: $!";
flock L, LOCK_EX or die "$lockfile: $!";

if ($quiet) {
    open STDERR, "> $errfile";
}

sub parse($) {
    my ($incr, $lasttime) = @_;
    $lasttime= '';
    while (defined($_= <F>)) {
	print N or die "$statefile.new: $!" if $incr>0;
	$have{$_} += $incr;
	$have += $incr;
	m/^From .* (\w+ \w+ \d+ [0-9:]+ \d+)$/ or die "$_ ?";
	$lasttime= $1;
    }
    die $! if F->error;
    return $lasttime;
}

if (open F, "< $statefile\0") {
    $old_lasttime= parse(-1);
    close F or die $!;
} elsif ($! != &ENOENT) {
    die "$statefile $!";
}

open N, "> $statefile.new" or die "$statefile.new: $!";

$child= open F, "-|"; defined $child or die $!;
if (!$child) {
    exec "from",@from_options;
    die "$!";
}

$did_have= $have;
$new_lasttime= parse(+1);
$?=0; close F or die "$? $!";

close N or die "$statefile.new: $!";

if ($new_lasttime ne $old_lasttime and $new_lasttime ne '') {
    push @reasons, "Timestamp of last message in mailbox changed.";
}

map { $total_more+=$_ if $_>0 } values %have;

if ($total_more) {
    push @reasons, "$total_more message(s)".
	" which were not previously present.";
} elsif ($have>0) {
    push @reasons, "More messages than previously reported.";
}

exit 0 unless @reasons;

$new_have= $have - $did_have;

$msg= <<END
To: $emailto
Subject: $subject

Regarding your mailbox @from_options:

Changes detected since last report:
END
    ;

$msg .= join "\n", map { "    $_" } @reasons;

$msg .= <<END


Now:
    There are $new_have message(s).
    The last is dated: $new_lasttime.

--
generated by $0
END
    ;

open S, "| $sendmail" or die $!;
print S $msg or die $!;
$?=0; close S or die "$? $!";

rename "$statefile.new", $statefile or die $!;
