chiark / gitweb /
rcopy-repeatedly debugging wip
[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 # Copyright 2006 Ian Jackson <ian@chiark.greenend.org.uk>
14 #
15 # This script and its documentation (if any) are free software; you
16 # can redistribute it and/or modify them under the terms of the GNU
17 # General Public License as published by the Free Software Foundation;
18 # either version 3, or (at your option) any later version.
19
20 # chiark-named-conf and its manpage are distributed in the hope that
21 # it will be useful, but WITHOUT ANY WARRANTY; without even the
22 # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
23 # PURPOSE.  See the GNU General Public License for more details.
24
25 # You should have received a copy of the GNU General Public License along
26 # with this program; if not, consult the Free Software Foundation's
27 # website at www.fsf.org, or the GNU Project website at www.gnu.org.
28
29
30 use strict (qw(refs));
31 use POSIX;
32 use IO::Handle;
33 use Fcntl (qw(:flock));
34
35 @from_options=();
36 $sendmail= '/usr/sbin/sendmail -odi -oee -oi -t';
37
38 umask 077;
39 while (@ARGV && $ARGV[0] =~ m/\-/) {
40     $_= shift;
41     if (m/^\-[fs]/) {
42         push @from_options, $_;
43     } elsif (s/^\-S//) {
44         $subject= $_;
45     } elsif (s/^\-F//) {
46         $statefile= $_;
47     } elsif (s/^\-q$//) {
48         $quiet= 1;
49         open STDERR, ">/dev/null";
50         eval('END { $?=0; }');
51     } elsif (m/^\-\-$/) {
52         last;
53     } else {
54         die "$0: unknown option \`$_'\n";
55     }
56 }
57 die unless @ARGV==1;
58 $emailto= shift @ARGV;
59
60 unless (defined $subject) {
61     my ($our_hostname);
62     open M, "/etc/mailname" or die $!;
63     defined($our_hostname= <M>) or die $!;
64     chomp($our_hostname);
65     close M;
66     $subject= "summary of messages on $our_hostname";
67 }
68
69 unless (defined $statefile) {
70     my ($dir);
71     die "$0: -F needed with that -f or -s\n"
72         if grep m:[|/]:, @from_options;
73     die "$0: no HOME in environment\n" unless defined $ENV{'HOME'};
74     $dir= $ENV{'HOME'}.'/.summarise-mailbox';
75     mkdir $dir, 02700 or $!==&EEXIST or die "$dir: $!";
76     $statefile= $dir.'/last'.
77         join('|',@from_options);
78 }
79
80 $statefile= "./$statefile" unless $statefile =~ m,^/,;
81 $lockfile= $statefile.'.lock';
82 $errfile= $statefile.'.err';
83
84 open L, "+> $lockfile" or die "$lockfile: $!";
85 flock L, LOCK_EX or die "$lockfile: $!";
86
87 if ($quiet) {
88     open STDERR, "> $errfile";
89 }
90
91 sub parse($) {
92     my ($incr, $lasttime) = @_;
93     $lasttime= '';
94     while (defined($_= <F>)) {
95         print N or die "$statefile.new: $!" if $incr>0;
96         $have{$_} += $incr;
97         $have += $incr;
98         m/^From .* (\w+ \w+ \d+ [0-9:]+ \d+)$/ or die "$_ ?";
99         $lasttime= $1;
100     }
101     die $! if F->error;
102     return $lasttime;
103 }
104
105 if (open F, "< $statefile\0") {
106     $old_lasttime= parse(-1);
107     close F or die $!;
108 } elsif ($! != &ENOENT) {
109     die "$statefile $!";
110 }
111
112 open N, "> $statefile.new" or die "$statefile.new: $!";
113
114 $child= open F, "-|"; defined $child or die $!;
115 if (!$child) {
116     exec "from",@from_options;
117     die "$!";
118 }
119
120 $did_have= $have;
121 $new_lasttime= parse(+1);
122 $?=0; close F or die "$? $!";
123
124 close N or die "$statefile.new: $!";
125
126 if ($new_lasttime ne $old_lasttime and $new_lasttime ne '') {
127     push @reasons, "Timestamp of last message in mailbox changed.";
128 }
129
130 map { $total_more+=$_ if $_>0 } values %have;
131
132 if ($total_more) {
133     push @reasons, "$total_more message(s)".
134         " which were not previously present.";
135 } elsif ($have>0) {
136     push @reasons, "More messages than previously reported.";
137 }
138
139 exit 0 unless @reasons;
140
141 $new_have= $have - $did_have;
142
143 $msg= <<END
144 To: $emailto
145 Subject: $subject
146
147 Regarding your mailbox @from_options:
148
149 Changes detected since last report:
150 END
151     ;
152
153 $msg .= join "\n", map { "    $_" } @reasons;
154
155 $msg .= <<END
156
157
158 Now:
159     There are $new_have message(s).
160     The last is dated: $new_lasttime.
161
162 --
163 generated by $0
164 END
165     ;
166
167 open S, "| $sendmail" or die $!;
168 print S $msg or die $!;
169 $?=0; close S or die "$? $!";
170
171 rename "$statefile.new", $statefile or die $!;