chiark / gitweb /
fe39a6e62dbe52fe213404528d0ef6be423cfa37
[chiark-utils.git] / scripts / summarise-mailbox-preserving-privacy
1 #!/usr/bin/perl
2 # usage:
3 #     summarise-mailbox-preserving-privacy \
4 #          [<our options>] [--] <email>
5 # our options:
6 #  -S<subject>       default: "summary of messages on <mailname>"
7 #  -F<state file>    default: $HOME/.summarise-mailbox/last<from options>
8 #  -f<mailbox>       passed to from(1)  must use -F if it contains / or |
9 #  -s<sender>        passed to from(1)  must use -F if it contains / or |
10 #  -q                throw away stderr and always exit 0
11 #  --           end of our options
12
13 use strict (qw(refs));
14 use POSIX;
15 use IO::Handle;
16 use Fcntl (qw(:flock));
17
18 @from_options=();
19 $sendmail= '/usr/sbin/sendmail -odi -oee -oi -t';
20
21 umask 077;
22 while (@ARGV && $ARGV[0] =~ m/\-/) {
23     $_= shift;
24     if (m/^\-[fs]/) {
25         push @from_options, $_;
26     } elsif (s/^\-S//) {
27         $subject= $_;
28     } elsif (s/^\-F//) {
29         $statefile= $_;
30     } elsif (s/^\-q$//) {
31         $quiet= 1;
32         open STDERR, ">/dev/null";
33         eval('END { $?=0; }');
34     } elsif (m/^\-\-$/) {
35         last;
36     } else {
37         die "$0: unknown option \`$_'\n";
38     }
39 }
40 die unless @ARGV==1;
41 $emailto= shift @ARGV;
42
43 unless (defined $subject) {
44     my ($our_hostname);
45     open M, "/etc/mailname" or die $!;
46     defined($our_hostname= <M>) or die $!;
47     chomp($our_hostname);
48     close M;
49     $subject= "summary of messages on $our_hostname";
50 }
51
52 unless (defined $statefile) {
53     my ($dir);
54     die "$0: -F needed with that -f or -s\n"
55         if grep m:[|/]:, @from_options;
56     die "$0: no HOME in environment\n" unless defined $ENV{'HOME'};
57     $dir= $ENV{'HOME'}.'/.summarise-mailbox';
58     mkdir $dir, 02700 or $!==&EEXIST or die "$dir: $!";
59     $statefile= $dir.'/last'.
60         join('|',@from_options);
61 }
62
63 $statefile= "./$statefile" unless $statefile =~ m,^/,;
64 $lockfile= $statefile.'.lock';
65 $errfile= $statefile.'.err';
66
67 open L, "+> $lockfile" or die "$lockfile: $!";
68 flock L, LOCK_EX or die "$lockfile: $!";
69
70 if ($quiet) {
71     open STDERR, "> $errfile";
72 }
73
74 sub parse($) {
75     my ($incr, $lasttime) = @_;
76     $lasttime= '';
77     while (defined($_= <F>)) {
78         print N or die "$statefile.new: $!" if $incr>0;
79         $have{$_} += $incr;
80         $have += $incr;
81         m/^From .* (\w+ \w+ \d+ [0-9:]+ \d+)$/ or die "$_ ?";
82         $lasttime= $1;
83     }
84     die $! if F->error;
85     return $lasttime;
86 }
87
88 if (open F, "< $statefile\0") {
89     $old_lasttime= parse(-1);
90     close F or die $!;
91 } elsif ($! != &ENOENT) {
92     die "$statefile $!";
93 }
94
95 open N, "> $statefile.new" or die "$statefile.new: $!";
96
97 $child= open F, "-|"; defined $child or die $!;
98 if (!$child) {
99     exec "from",@from_options;
100     die "$!";
101 }
102
103 $did_have= $have;
104 $new_lasttime= parse(+1);
105 $?=0; close F or die "$? $!";
106
107 close N or die "$statefile.new: $!";
108
109 if ($new_lasttime ne $old_lasttime and $new_lasttime ne '') {
110     push @reasons, "Timestamp of last message in mailbox changed.";
111 }
112
113 map { $total_more+=$_ if $_>0 } values %have;
114
115 if ($total_more) {
116     push @reasons, "$total_more message(s)".
117         " which were not previously present.";
118 } elsif ($have>0) {
119     push @reasons, "More messages than previously reported.";
120 }
121
122 exit 0 unless @reasons;
123
124 $new_have= $have - $did_have;
125
126 $msg= <<END
127 To: $emailto
128 Subject: $subject
129
130 Regarding your mailbox @from_options:
131
132 Changes detected since last report:
133 END
134     ;
135
136 $msg .= join "\n", map { "    $_" } @reasons;
137
138 $msg .= <<END
139
140
141 Now:
142     There are $new_have message(s).
143     The last is dated: $new_lasttime.
144
145 --
146 generated by $0
147 END
148     ;
149
150 open S, "| $sendmail" or die $!;
151 print S $msg or die $!;
152 $?=0; close S or die "$? $!";
153
154 rename "$statefile.new", $statefile or die $!;