3 # onak-mail.pl - Mail processing interface for onak, an OpenPGP Keyserver.
5 # Copyright 2002-2005 Jonathan McDowell <noodles@earth.li>
6 # Released under the GPL.
19 # Read an old pksd-style config file. Currently if both old and new style
20 # files are present the old style will be preferred in order to ensure smooth
24 open(CONFIG, "@CMAKE_INSTALL_FULL_SYSCONFDIR@/onak.conf") or
25 die "Can't read config file: $!";
29 # Ignore; comment line.
30 } elsif (/^this_site (.*)/) {
31 $config{'thissite'} = $1;
32 } elsif (/^logfile (.*)/) {
33 $config{'logfile'} = $1;
34 } elsif (/^maintainer_email (.*)/) {
35 $config{'adminemail'} = $1;
36 } elsif (/^mail_delivery_client (.*)/) {
38 } elsif (/^pks_bin_dir (.*)/) {
39 $config{'pks_bin_dir'} = $1;
40 } elsif (/^mail_dir (.*)/) {
41 $config{'mail_dir'} = $1;
42 } elsif (/^syncsite (.*)/) {
43 push @{$config{'syncsites'}}, $1;
55 # Reads in our config file. Ignores any command it doesn't understand rather
56 # than having to list all the ones that are of no interest to us.
59 # Prefer the old style config if it exists.
60 if (-e "@CMAKE_INSTALL_FULL_SYSCONFDIR@/onak.conf") {
65 open(CONFIG, "@CMAKE_INSTALL_FULL_SYSCONFDIR@/onak.ini") or
66 die "Can't read config file: $!";
71 # Ignore; comment line.
72 } elsif (/^\[(\w+)\]/) {
74 } elsif ($section eq "main") {
75 if (/^logfile\s*=\s*(.*)/) {
76 $config{'logfile'} = $1;
78 } elsif ($section eq "mail") {
79 if (/^this_site\s*=\s*(.*)/) {
80 $config{'thissite'} = $1;
81 } elsif (/^maintainer_email\s*=\s*(.*)/) {
82 $config{'adminemail'} = $1;
83 } elsif (/^mta\s*=\s*(.*)/) {
85 } elsif (/^bin_dir\s*=\s*(.*)/) {
86 $config{'pks_bin_dir'} = $1;
87 } elsif (/^mail_dir\s*=\s*(.*)/) {
88 $config{'mail_dir'} = $1;
89 } elsif (/^syncsite\s*=\s*(.*)/) {
90 push @{$config{'syncsites'}}, $1;
103 # Takes an armored OpenPGP stream and submits it to the keyserver. Returns the
104 # difference between what we just added and what we had before (ie the least
105 # data need to get from what we had to what we have).
107 sub submitupdate($) {
109 my (@errors, @mergedata);
111 my $pid = open3(\*MERGEIN, \*MERGEOUT, \*MERGEERR,
112 $config{'pks_bin_dir'}."/onak", "-u", "add");
114 print MERGEIN @$data;
116 @mergedata = <MERGEOUT>;
118 @errors = <MERGEERR>;
126 sub processmail($$$$$) {
133 # HELP, ADD, INCREMENTAL, VERBOSE INDEX <keyid>, INDEX <keyid>,
134 # GET <keyid>, LAST <days>
136 if ($subject =~ /^(INCREMENTAL|ADD)$/i) {
140 my @newupdate = submitupdate($body);
144 foreach $i (@{$config{'syncsites'}}) {
145 if (! defined($seenby->{$i})) {
150 open (LOG, ">>$config{'logfile'}");
151 @time = localtime(time);
153 print LOG sprintf "%02d/%02d/%04d %02d:%02d:%02d",
154 $time[3], $time[4] + 1, $time[5] + 1900,
155 $time[2], $time[1], $time[0];
156 print LOG "] onak-mail[$$]: Syncing with $count sites.\n";
159 if ($subject =~ /ADD/i) {
160 open(MAIL, "|$config{mta}");
161 print MAIL "From: $config{adminemail}\n";
162 print MAIL "To: $replyto\n";
163 print MAIL "Subject: Reply to ADD\n";
164 print MAIL "Precedence: list\n";
165 print MAIL "MIME-Version: 1.0\n";
166 print MAIL "Content-Type: text/plain\n";
168 print MAIL "Thank you for your recent key submission.",
169 " It has been processed and synced\n",
170 "with ", $count, " other keyservers.\n";
174 if ((! defined($newupdate[0])) || $newupdate[0] eq '') {
175 open (LOG, ">>$config{'logfile'}");
177 print LOG sprintf "%02d/%02d/%04d %02d:%02d:%02d",
178 $time[3], $time[4] + 1, $time[5] + 1900,
179 $time[2], $time[1], $time[0];
180 print LOG "] onak-mail[$$]: Nothing to sync.\n";
186 open(MAIL, "|$config{mta}");
187 print MAIL "From: $config{adminemail}\n";
189 foreach $i (@{$config{'syncsites'}}) {
190 if (! defined($seenby->{$i})) {
199 print MAIL "Subject: incremental\n";
200 foreach $site (keys %$seenby) {
201 print MAIL "X-KeyServer-Sent: $site\n";
203 print MAIL "X-KeyServer-Sent: $config{thissite}\n";
204 print MAIL "Precedence: list\n";
205 print MAIL "MIME-Version: 1.0\n";
206 print MAIL "Content-Type: application/pgp-keys\n";
208 print MAIL @newupdate;
211 } elsif ($subject =~ /^(VERBOSE )?INDEX (.*)$/i) {
212 my (@indexdata, $command);
219 my $pid = open3(\*INDEXIN, \*INDEXOUT, \*INDEXERR,
220 $config{'pks_bin_dir'}."/onak", $command, "$2");
222 @indexdata = <INDEXOUT>;
227 open(MAIL, "|$config{mta}");
228 print MAIL "From: $config{adminemail}\n";
229 print MAIL "To: $replyto\n";
230 print MAIL "Subject: Reply to INDEX $2\n";
231 print MAIL "Precedence: list\n";
232 print MAIL "MIME-Version: 1.0\n";
233 print MAIL "Content-Type: text/plain\n";
235 print MAIL "Below follows the reply to your recent keyserver query:\n";
237 print MAIL @indexdata;
242 my ($inheader, %seenby, $subject, $from, $replyto, @body, @syncmail);
247 # First dump the incoming mail to a file; this means that if we're receiving
248 # loads of updates we don't spawn lots of processes but instead leave the
249 # mails on disk to be dealt with sequentially.
251 my @time = localtime;
252 my $tmpfile = sprintf "%s/%04d%02d%02d-%02d%02d%02d-%d.onak",
261 open(MAILFILE, '>'.$tmpfile.'.tmp');
266 rename $tmpfile.".tmp", $tmpfile;
269 # Lock here to ensure that only one copy of us is processing the incoming
270 # mail queue at any point in time.
272 sysopen(LOCKFILE, $config{'mail_dir'}.'/onak-mail.lck',
273 O_WRONLY|O_CREAT|O_EXCL) or exit;
278 opendir(MAILDIR, $config{'mail_dir'});
279 while ($file = readdir(MAILDIR)) {
280 next if $file !~ /\.onak$/;
283 $subject = $from = $replyto = "";
287 open(FILE, '<'.$config{'mail_dir'}.'/'.$file);
290 if (/^Subject:\s*(.*)\s*$/i) {
292 } elsif (/^X-KeyServer-Sent:\s*(.*)\s*$/i) {
294 } elsif (/^From:\s*(.*)\s*$/i) {
296 } elsif (/^Reply-To:\s*(.*)\s*$/i) {
306 if ($replyto eq '') {
310 unlink $config{'mail_dir'}.'/'.$file;
312 processmail($subject, $from, $replyto, \%seenby, \@body);
315 unlink $config{'mail_dir'}.'/onak-mail.lck';