]> git.donarmstrong.com Git - debbugs.git/blob - scripts/process.in
[project @ 2005-07-19 11:00:28 by cjwatson]
[debbugs.git] / scripts / process.in
1 #!/usr/bin/perl
2 # $Id: process.in,v 1.95 2005/07/19 11:00:28 cjwatson Exp $
3 #
4 # Usage: process nn
5 # Temps:  incoming/Pnn
6
7 use POSIX qw(strftime tzset);
8 $ENV{"TZ"} = 'UTC';
9 tzset();
10
11 use MIME::Parser;
12 use Debbugs::MIME qw(decode_rfc1522);
13
14 $config_path = '/etc/debbugs';
15 $lib_path = '/usr/lib/debbugs';
16
17 require "$config_path/config";
18 require "$lib_path/errorlib";
19 $ENV{'PATH'} = $lib_path.':'.$ENV{'PATH'};
20
21 chdir( "$gSpoolDir" ) || die "chdir spool: $!\n";
22
23 #open(DEBUG,"> /tmp/debbugs.debug");
24 umask(002);
25 open DEBUG, ">/dev/null";
26
27 defined( $intdate= time ) || &quit( "failed to get time: $!" );
28
29 $_=shift;
30 m/^([BMQFDU])(\d*)\.\d+$/ || &quit("bad argument");
31 $codeletter= $1;
32 $tryref= length($2) ? $2+0 : -1;
33 $nn= $_;
34
35 if (!rename("incoming/G$nn","incoming/P$nn")) 
36 {
37     $_=$!.'';  m/no such file or directory/i && exit 0;
38     &quit("renaming to lock: $!");
39 }
40
41 $baddress= 'submit' if $codeletter eq 'B';
42 $baddress= 'maintonly' if $codeletter eq 'M';
43 $baddress= 'quiet' if $codeletter eq 'Q';
44 $baddress= 'forwarded' if $codeletter eq 'F';
45 $baddress= 'done' if $codeletter eq 'D';
46 $baddress= 'submitter' if $codeletter eq 'U';
47 $baddress || &quit("bad codeletter $codeletter");
48 $baddressroot= $baddress;
49 $baddress= "$tryref-$baddress" if $tryref>=0;
50
51 open(M,"incoming/P$nn");
52 @log=<M>;
53 close(M);
54
55 @msg=@log;
56 chomp @msg;
57
58 print DEBUG "###\n",join("##\n",@msg),"\n###\n";
59
60 $tdate = strftime "%a, %d %h %Y %T UTC", localtime;
61 $fwd= <<END;
62 Received: via spool by $baddress\@$gEmailDomain id=$nn
63           (code $codeletter ref $tryref); $tdate
64 END
65
66 # header and decoded body respectively
67 my (@headerlines, @bodylines);
68
69 my $parser = new MIME::Parser;
70 mkdir "$gSpoolDir/mime.tmp", 0777;
71 $parser->output_under("$gSpoolDir/mime.tmp");
72 my $entity = eval { $parser->parse_data(join('',@log)) };
73
74 if ($entity and $entity->head->tags) {
75     @headerlines = @{$entity->head->header};
76     chomp @headerlines;
77
78     my $entity_body = getmailbody($entity);
79     @bodylines = $entity_body ? $entity_body->as_lines() : ();
80     chomp @bodylines;
81
82     # set $i to beginning of encoded body data, so we can dump it out
83     # verbatim later
84     $i = 0;
85     ++$i while $msg[$i] =~ /./;
86 } else {
87     # Legacy pre-MIME code, kept around in case MIME::Parser fails.
88     for ($i = 0; $i <= $#msg; $i++) {
89         $_ = $msg[$i];
90         last unless length($_);
91         while ($msg[$i+1] =~ m/^\s/) {
92             $i++;
93             $_ .= "\n".$msg[$i];
94         }
95         push @headerlines, $_;
96     }
97
98     @bodylines = @msg[$i..$#msg];
99 }
100
101 for my $hdr (@headerlines) {
102     $_ = $hdr;
103     s/\n\s/ /g;
104     &finish if m/^x-loop: (\S+)$/i && $1 eq "$gMaintainerEmail";
105     my $ins = !m/^subject:/i && !m/^reply-to:/i && !m/^return-path:/i
106            && !m/^From / && !m/^X-Debbugs-/i;
107     $fwd .= $hdr."\n" if $ins;
108     # print DEBUG ">$_<\n";
109     if (s/^(\S+):\s*//) {
110         my $v = lc $1;
111         print DEBUG ">$v=$_<\n";
112         $header{$v} = decode_rfc1522($_);
113     } else {
114         print DEBUG "!>$_<\n";
115     }
116 }
117
118 # remove blank lines
119 shift @bodylines while @bodylines and $bodylines[0] !~ /\S/;
120
121 # Strip off RFC2440-style PGP clearsigning.
122 if (@bodylines and $bodylines[0] =~ /^-----BEGIN PGP SIGNED/) {
123     shift @bodylines while @bodylines and length $bodylines[0];
124     shift @bodylines while @bodylines and $bodylines[0] !~ /\S/;
125     for my $findsig (0 .. $#bodylines) {
126         if ($bodylines[$findsig] =~ /^-----BEGIN PGP SIGNATURE/) {
127             $#bodylines = $findsig - 1;
128             last;
129         }
130     }
131     map { s/^- // } @bodylines;
132 }
133
134 # extract pseudo-headers
135 for my $phline (@bodylines)
136 {
137     last if $phline !~ m/^([\w-]+):\s*(\S.*)/;
138     my ($fn, $fv) = ($1, $2);
139     $fv =~ s/\s*$//;
140     print DEBUG ">$fn|$fv|\n";
141     $fn = lc $fn;
142     $fv = lc $fv;
143     $pheader{$fn} = $fv;
144     print DEBUG ">$fn~$fv<\n";
145 }
146
147
148 $fwd .= join("\n",@msg[$i..$#msg]);
149
150 print DEBUG "***\n$fwd\n***\n";
151
152 if (defined $header{'resent-from'} && !defined $header{'from'}) {
153     $header{'from'} = $header{'resent-from'};
154 }
155 defined($header{'from'}) || &quit("no From header");
156
157 $replyto = $header{'reply-to'};
158 $replyto = '' unless defined $replyto;
159 $replyto =~ s/^ +//;
160 $replyto =~ s/ +$//;
161 unless (length $replyto) {
162     $replyto = $header{'from'};
163 }
164
165 $_= $replyto;
166 $_= "$2 <$1>" if m/^([^\<\> \t\n\(\)]+) \(([^\(\)\<\>]+)\)$/;
167 $replytocompare= $_;
168 print DEBUG "replytocompare >$replytocompare<\n";
169     
170 if (!defined($header{'subject'})) 
171 {
172         $brokenness.= <<END;
173
174 Your message did not contain a Subject field. They are recommended and
175 useful because the title of a $gBug is determined using this field.
176 Please remember to include a Subject field in your messages in future.
177 END
178
179 # RFC822 actually lists it as an `optional-field'.
180
181     $subject= '(no subject)';
182 } else { 
183     $subject= $header{'subject'}; 
184 }
185
186 $ref=-1;
187 $subject =~ s/^Re:\s*//i; $_= $subject."\n";
188 if ($tryref < 0 && m/^Bug ?\#(\d+)\D/i) {
189     $tryref= $1+0; 
190 }
191
192 if ($tryref >= 0) 
193 {
194     ($bfound, $data)= &lockreadbugmerge($tryref);
195     if ($bfound) { 
196         $ref= $tryref; 
197     } else {
198         &htmllog("Reply","sent", $replyto,"Unknown problem report number <code>$tryref</code>.");
199         my $archivenote = '';
200         if ($gRemoveAge) {
201             $archivenote = <<END;
202 This may be because that $gBug report has been resolved for more than $gRemoveAge
203 days, and the record of it has been archived and made read-only, or
204 because you mistyped the $gBug report number.
205
206 END
207         }
208         &sendmessage(<<END, '');
209 From: $gMaintainerEmail ($gProject $gBug Tracking System)
210 To: $replyto
211 Subject: Unknown problem report $gBug#$tryref ($subject)
212 Message-ID: <handler.x.$nn.unknown\@$gEmailDomain>
213 In-Reply-To: $header{'message-id'}
214 References: $header{'message-id'} $data->{msgid}
215 Precedence: bulk
216 X-$gProject-PR-Message: error
217
218 You sent a message to the $gBug tracking system which gave (in the
219 Subject line or encoded into the recipient at $gEmailDomain),
220 the number of a nonexistent $gBug report (#$tryref).
221
222 ${archivenote}Your message was dated $header{'date'} and was sent to
223 $baddress\@$gEmailDomain.  It had
224 Message-ID $header{'message-id'}
225 and Subject $subject.
226
227 It has been filed (under junk) but otherwise ignored.
228
229 Please consult your records to find the correct $gBug report number, or
230 contact me, the system administrator, for assistance.
231
232 $gMaintainer
233 (administrator, $gProject $gBugs database)
234
235 (NB: If you are a system administrator and have no idea what I am
236 talking about this indicates a serious mail system misconfiguration
237 somewhere.  Please contact me immediately.)
238
239 END
240         &appendlog;
241         &finish;
242     }
243 } else { 
244     &filelock('lock/-1'); 
245 }
246
247 if ($codeletter eq 'D' || $codeletter eq 'F') 
248 {
249     if ($replyto =~ m/$gBounceFroms/o ||
250         $header{'from'} =~ m/$gBounceFroms/o)
251     { 
252         &quit("bounce detected !  Mwaap! Mwaap!"); 
253     }
254     $markedby= $header{'from'} eq $replyto ? $replyto :
255                "$header{'from'} (reply to $replyto)";
256     if ($codeletter eq 'F') {
257         (&appendlog,&finish) if length($data->{forwarded});
258         $receivedat= "forwarded\@$gEmailDomain";
259         $markaswhat= 'forwarded';
260         $set_forwarded= $header{'to'};
261         if ( length( $gListDomain ) > 0 && length( $gForwardList ) > 0 ) {
262             $generalcc= "$gForwardList\@$gListDomain";
263         } else { 
264             $generalcc=''; 
265         }
266     } else {
267         if (length($data->{done}) and
268                 not defined $pheader{'source-version'} and
269                 not defined $pheader{'version'}) {
270             &appendlog;
271             &finish;
272         }
273         $receivedat= "done\@$gEmailDomain";
274         $markaswhat= 'done';
275         $set_done= $header{'from'};
276         if ( length( $gListDomain ) > 0 && length( $gDoneList ) > 0 ) {
277             $generalcc= "$gDoneList\@$gListDomain";
278         } else { 
279             $generalcc=''; 
280         }
281     }
282     if (defined $gStrongList and isstrongseverity($data->{severity})) {
283         $generalcc = join ', ', $generalcc, "$gStrongList\@$gListDomain";
284     }
285     if ($ref<0) {
286         &htmllog("Warning","sent",$replyto,"Message ignored.");
287         &sendmessage(<<END, '');
288 From: $gMaintainerEmail ($gProject $gBug Tracking System)
289 To: $replyto
290 Subject: Message with no $gBug number ignored by $receivedat
291          ($subject)
292 Message-ID: <header.x.$nn.warnignore\@$gEmailDomain>
293 In-Reply-To: $header{'message-id'}
294 References: $header{'message-id'} $data->{msgid}
295 Precedence: bulk
296 X-$gProject-PR-Message: error
297
298 You sent a message to the $gProject $gBug tracking system old-style
299 unified mark as $markaswhat address ($receivedat),
300 without a recognisable $gBug number in the Subject.
301 Your message has been filed under junk but otherwise ignored.
302
303 If you don't know what I'm talking about then probably either:
304
305 (a) you unwittingly sent a message to done\@$gEmailDomain
306 because you replied to all recipients of the message a developer used
307 to mark a $gBug as done and you modified the Subject.  In this case,
308 please do not be alarmed.  To avoid confusion do not do it again, but
309 there is no need to apologise or mail anyone asking for an explanation.
310
311 (b) you are a system administrator, reading this because the $gBug 
312 tracking system is responding to a misdirected bounce message.  In this
313 case there is a serious mail system misconfiguration somewhere - please
314 contact me immediately.
315
316 Your message was dated $header{'date'} and had
317 message-id $header{'message-id'}
318 and subject $subject.
319
320 If you need any assistance or explanation please contact me.
321
322 $gMaintainer
323 (administrator, $gProject $gBugs database)
324
325 END
326         &appendlog;
327         &finish;
328     }
329
330     &checkmaintainers;
331
332     $noticeccval.= join(', ', grep($_ ne $replyto,@maintaddrs));
333     $noticeccval =~ s/\s+\n\s+/ /g; 
334     $noticeccval =~ s/^\s+/ /; $noticeccval =~ s/\s+$//;
335
336     $generalcc = join(', ', $generalcc, @addsrcaddrs);
337     $generalcc =~ s/\s+\n\s+/ /g; 
338     $generalcc =~ s/^\s+/ /; $generalcc =~ s/\s+$//;
339
340     if (length($noticeccval)) { $noticecc= "Cc: $noticeccval\n"; }
341     if (length($generalcc)) { $noticecc.= "Bcc: $generalcc\n"; }
342
343     @process= ($ref,split(/ /,$data->{mergedwith}));
344     $orgref= $ref;
345
346     for $ref (@process) {
347         if ($ref != $orgref) {
348             &unfilelock;
349             $data = &lockreadbug($ref)
350                 || die "huh ? $ref from $orgref out of @process";
351         }
352         $data->{done}= $set_done if defined($set_done);
353         $data->{forwarded}= $set_forwarded if defined($set_forwarded);
354         if ($codeletter eq 'D') {
355             $data->{keywords} = join ' ', grep $_ ne 'pending',
356                                      split ' ', $data->{keywords};
357             if (defined $pheader{'source-version'}) {
358                 addfixedversions($data, $pheader{source}, $pheader{'source-version'});
359             } elsif (defined $pheader{version}) {
360                 addfixedversions($data, undef, $pheader{version});
361             }
362         }
363
364         writebug($ref, $data);
365
366         my $hash = get_hashname($ref);
367         open(O,"db-h/$hash/$ref.report") || &quit("read original report: $!");
368         $x= join('',<O>); close(O);
369         if ($codeletter eq 'F') {
370             &htmllog("Reply","sent",$replyto,"You have marked $gBug as forwarded.");
371             &sendmessage(<<END."---------------------------------------\n".join( "\n", @msg ), '');
372 From: $gMaintainerEmail ($gProject $gBug Tracking System)
373 To: $replyto
374 ${noticecc}Subject: $gBug#$ref: marked as forwarded ($data->{subject})
375 Message-ID: <header.$ref.$nn.ackfwdd\@$gEmailDomain>
376 In-Reply-To: $header{'message-id'}
377 References: $header{'message-id'} $data->{msgid}
378 Precedence: bulk
379 X-$gProject-PR-Message: forwarded $ref
380 X-$gProject-PR-Package: $data->{package}
381 X-$gProject-PR-Keywords: $data->{keywords}
382
383 Your message dated $header{'date'}
384 with message-id $header{'message-id'}
385 has caused the $gProject $gBug report #$ref,
386 regarding $data->{subject}
387 to be marked as having been forwarded to the upstream software
388 author(s) $data->{forwarded}.
389
390 (NB: If you are a system administrator and have no idea what I am
391 talking about this indicates a serious mail system misconfiguration
392 somewhere.  Please contact me immediately.)
393
394 $gMaintainer
395 (administrator, $gProject $gBugs database)
396
397 END
398
399         } else {
400             &htmllog("Reply","sent",$replyto,"You have taken responsibility.");
401             &sendmessage(<<END."--------------------------------------\n".$x."---------------------------------------\n".join( "\n", @msg ), '');
402 From: $gMaintainerEmail ($gProject $gBug Tracking System)
403 To: $replyto
404 ${noticecc}Subject: $gBug#$ref: marked as done ($data->{subject})
405 Message-ID: <handler.$ref.$nn.ackdone\@$gEmailDomain>
406 In-Reply-To: $header{'message-id'}
407 References: $header{'message-id'} $data->{msgid}
408 Precedence: bulk
409 X-$gProject-PR-Message: closed $ref
410 X-$gProject-PR-Package: $data->{package}
411 X-$gProject-PR-Keywords: $data->{keywords}
412
413 Your message dated $header{'date'}
414 with message-id $header{'message-id'}
415 and subject line $subject
416 has caused the attached $gBug report to be marked as done.
417
418 This means that you claim that the problem has been dealt with.
419 If this is not the case it is now your responsibility to reopen the
420 $gBug report if necessary, and/or fix the problem forthwith.
421
422 (NB: If you are a system administrator and have no idea what I am
423 talking about this indicates a serious mail system misconfiguration
424 somewhere.  Please contact me immediately.)
425
426 $gMaintainer
427 (administrator, $gProject $gBugs database)
428
429 END
430             &htmllog("Notification","sent",$data->{originator}, 
431                 "$gBug acknowledged by developer.");
432             &sendmessage(<<END.join("\n",@msg),'');
433 From: $gMaintainerEmail ($gProject $gBug Tracking System)
434 To: $data->{originator}
435 Subject: $gBug#$ref acknowledged by developer
436          ($header{'subject'})
437 Message-ID: <handler.$ref.$nn.notifdone\@$gEmailDomain>
438 In-Reply-To: $data->{msgid}
439 References: $header{'message-id'} $data->{msgid}
440 X-$gProject-PR-Message: they-closed $ref
441 X-$gProject-PR-Package: $data->{package}
442 X-$gProject-PR-Keywords: $data->{keywords}
443 Reply-To: $ref\@$gEmailDomain
444
445 This is an automatic notification regarding your $gBug report
446 #$ref: $data->{subject},
447 which was filed against the $data->{package} package.
448
449 It has been closed by one of the developers, namely
450 $markedby.
451
452 Their explanation is attached below.  If this explanation is
453 unsatisfactory and you have not received a better one in a separate
454 message then please contact the developer, by replying to this email.
455
456 $gMaintainer
457 (administrator, $gProject $gBugs database)
458
459 END
460         }
461         &appendlog;
462     }
463     &finish;
464 }
465
466 if ($ref<0) {
467     if ($codeletter eq 'U') {
468         &htmllog("Warning","sent",$replyto,"Message not forwarded.");
469         &sendmessage(<<END, '');
470 From: $gMaintainerEmail ($gProject $gBug Tracking System)
471 To: $replyto
472 Subject: Message with no $gBug number cannot be sent to submitter !
473          ($subject)
474 Message-ID: <handler.x.$nn.nonumnosub\@$gEmailDomain>
475 In-Reply-To: $header{'message-id'}
476 References: $header{'message-id'} $data->{msgid}
477 Precedence: bulk
478 X-$gProject-PR-Message: error
479
480 You sent a message to the $gProject $gBug tracking system's $gBug 
481 report submitter address $baddress\@$gEmailDomain, without a
482 recognisable $gBug number in the Subject.  Your message has been filed
483 under junk but otherwise ignored.
484
485 If you don't know what I'm talking about then probably either:
486
487 (a) you unwittingly sent a message to $baddress\@$gEmailDomain
488 because you replied to all recipients of the message a developer sent
489 to a $gBug\'s submitter and you modified the Subject.  In this case,
490 please do not be alarmed.  To avoid confusion do not do it again, but
491 there is no need to apologise or mail anyone asking for an
492 explanation.
493
494 (b) you are a system administrator, reading this because the $gBug 
495 tracking system is responding to a misdirected bounce message.  In this
496 case there is a serious mail system misconfiguration somewhere - please
497 contact me immediately.
498
499 Your message was dated $header{'date'} and had
500 message-id $header{'message-id'}
501 and subject $subject.
502
503 If you need any assistance or explanation please contact me.
504
505 $gMaintainer
506 (administrator, $gProject $gBugs database)
507
508 END
509         &appendlog;
510         &finish;
511     }
512
513     $data->{found_versions} = [];
514     $data->{fixed_versions} = [];
515
516     if (defined $pheader{source}) {
517         $data->{package} = $pheader{source};
518     } elsif (defined $pheader{package}) {
519         $data->{package} = $pheader{package};
520     } else {
521         &htmllog("Warning","sent",$replyto,"Message not forwarded.");
522         &sendmessage(<<END."---------------------------------------------------------------------------\n".join("\n", @msg), '');
523 From: $gMaintainerEmail ($gProject $gBug Tracking System)
524 To: $replyto
525 Subject: Message with no Package: tag cannot be processed!
526          ($subject)
527 Message-ID: <handler.x.$nn.nonumnosub\@$gEmailDomain>
528 In-Reply-To: $header{'message-id'}
529 References: $header{'message-id'} $data->{msgid}
530 Precedence: bulk
531 X-$gProject-PR-Message: error
532
533 Your message didn't have a Package: line at the start (in the
534 pseudo-header following the real mail header), or didn't have a
535 pseudo-header at all.  Your message has been filed under junk but
536 otherwise ignored.
537
538 This makes it much harder for us to categorise and deal with your
539 problem report. Please _resubmit_ your report to $baddress\@$gEmailDomain
540 and tell us which package the report is on. For help, check out
541 http://$gWebDomain/Reporting$gHTMLSuffix.
542
543 Your message was dated $header{'date'} and had
544 message-id $header{'message-id'}
545 and subject $subject.
546 The complete text of it is attached to this message.
547
548 If you need any assistance or explanation please contact me.
549
550 $gMaintainer
551 (administrator, $gProject $gBugs database)
552
553 END
554         &appendlog;
555         &finish;
556     }
557
558     $data->{keywords}= '';
559     if (defined($pheader{'keywords'})) {
560         $data->{keywords}= $pheader{'keywords'};
561     } elsif (defined($pheader{'tags'})) {
562         $data->{keywords}= $pheader{'tags'};
563     }
564     if (length($data->{keywords})) {
565         my @kws;
566         my %gkws = map { ($_, 1) } @gTags;
567         foreach my $kw (sort split(/[,\s]+/, lc($data->{keywords}))) {
568             push @kws, $kw if (defined $gkws{$kw});
569         }
570         $data->{keywords} = join(" ", @kws);
571     }
572     $data->{severity}= '';
573     if (defined($pheader{'severity'}) || defined($pheader{'priority'})) {
574         $data->{severity}= $pheader{'severity'};
575         $data->{severity}= $pheader{'priority'} unless ($data->{severity});
576         $data->{severity} =~ s/^\s*(.+)\s*$/$1/;
577
578         if (!grep($_ eq $data->{severity}, @severities, "$gDefaultSeverity")) {
579             $brokenness.= <<END;
580
581 Your message specified a Severity: in the pseudo-header, but
582 the severity value $data->{severity} was not recognised.
583 The default severity $gDefaultSeverity is being used instead.
584 The recognised values are: $gShowSeverities.
585 END
586 # if we use @gSeverityList array in the above line, perl -c gives:
587 # In string, @gSeverityList now must be written as \@gSeverityList at
588 #          process line 452, near "$gDefaultSeverity is being used instead.
589             $data->{severity}= '';
590         }
591     }
592     if (defined($pheader{owner})) {
593         $data->{owner}= $pheader{owner};
594     }
595     &filelock("nextnumber.lock");
596     open(N,"nextnumber") || &quit("nextnumber: read: $!");
597     $v=<N>; $v =~ s/\n$// || &quit("nextnumber bad format");
598     $ref= $v+0;  $v += 1;  $newref=1;
599     &overwrite('nextnumber', "$v\n");
600     &unfilelock;
601     my $hash = get_hashname($ref);
602     &overwrite("db-h/$hash/$ref.log",'');
603     $data->{originator} = $replyto;
604     $data->{date} = $intdate;
605     $data->{subject} = $subject;
606     $data->{msgid} = $header{'message-id'};
607     writebug($ref, $data);
608     &overwrite("db-h/$hash/$ref.report",
609                join("\n",@msg)."\n");
610 }
611
612 &checkmaintainers;
613
614 print DEBUG "maintainers >@maintaddrs<\n";
615
616 $orgsender= defined($header{'sender'}) ? "Original-Sender: $header{'sender'}\n" : '';
617 $newsubject= $subject;  $newsubject =~ s/^$gBug#$ref:*\s*//;
618
619 $xcchdr= $header{ 'x-debbugs-cc' };
620 if ($xcchdr =~ m/\S/) {
621     push(@resentccs,$xcchdr);
622     $resentccexplain.= <<END;
623
624 As you requested using X-Debbugs-CC, your message was also forwarded to
625    $xcchdr
626 (after having been given a $gBug report number, if it did not have one).
627 END
628 }
629
630 if (@maintaddrs && ($codeletter eq 'B' || $codeletter eq 'M')) {
631     push(@resentccs,@maintaddrs);
632     $resentccexplain.= <<END." ".join("\n ",@maintaddrs)."\n";
633
634 Your message has been sent to the package maintainer(s):
635 END
636 }
637
638 @bccs = @addsrcaddrs;
639 if (defined $gStrongList and isstrongseverity($data->{severity})) {
640     push @bccs, "$gStrongList\@$gListDomain";
641 }
642
643 if (defined $pheader{source}) {
644     # Prefix source versions with the name of the source package. They
645     # appear that way in version trees so that we can deal with binary
646     # packages moving from one source package to another.
647     if (defined $pheader{'source-version'}) {
648         addfoundversions($data, $pheader{source}, $pheader{'source-version'});
649     } elsif (defined $pheader{version}) {
650         addfoundversions($data, $pheader{source}, $pheader{version});
651     }
652     writebug($ref, $data);
653 } elsif (defined $pheader{package}) {
654     # TODO: could handle Source-Version: by looking up the source package?
655     addfoundversions($data, undef, $pheader{version}) if defined($pheader{version});
656     writebug($ref, $data);
657 }
658
659 $veryquiet= $codeletter eq 'Q';
660 if ($codeletter eq 'M' && !@maintaddrs) {
661     $veryquiet= 1;
662     $brokenness.= <<END;
663
664 You requested that the message be sent to the package maintainer(s)
665 but either the $gBug report is not associated with any package (probably
666 because of a missing Package pseudo-header field in the original $gBug
667 report), or the package(s) specified do not have any maintainer(s).
668
669 Your message has *not* been sent to any package maintainers; it has
670 merely been filed in the $gBug tracking system.  If you require assistance
671 please contact $gMaintainerEmail quoting the $gBug number $ref.
672 END
673 }
674
675 $resentccval.= join(', ',@resentccs);
676 $resentccval =~ s/\s+\n\s+/ /g; $resentccval =~ s/^\s+/ /; $resentccval =~ s/\s+$//;
677 if (length($resentccval)) { 
678     $resentcc= "Resent-CC: $resentccval\n"; 
679 }
680
681 if ($codeletter eq 'U') {
682     &htmllog("Message", "sent on", $data->{originator}, "$gBug#$ref.");
683     &sendmessage(<<END,[$data->{originator},@resentccs],[@bccs]);
684 Subject: $gBug#$ref: $newsubject
685 Reply-To: $replyto, $ref-quiet\@$gEmailDomain
686 ${orgsender}Resent-To: $data->{originator}
687 ${resentcc}Resent-Date: $tdate
688 Resent-Message-ID: <handler.$ref.$nn\@$gEmailDomain>
689 Resent-Sender: $gMaintainerEmail
690 X-$gProject-PR-Message: report $ref
691 X-$gProject-PR-Package: $data->{package}
692 X-$gProject-PR-Keywords: $data->{keywords}
693 $fwd
694 END
695 } elsif ($codeletter eq 'B') {
696     &htmllog($newref ? "Report" : "Information", "forwarded",
697              join(', ',"$gSubmitList\@$gListDomain",@resentccs),
698              "<code>$gBug#$ref</code>".
699              (length($data->{package})? "; Package <code>".&sani($data->{package})."</code>" : '').
700              ".");
701     &sendmessage(<<END,["$gSubmitList\@$gListDomain",@resentccs],[@bccs]);
702 Subject: $gBug#$ref: $newsubject
703 Reply-To: $replyto, $ref\@$gEmailDomain
704 Resent-From: $header{'from'}
705 ${orgsender}Resent-To: $gSubmitList\@$gListDomain
706 ${resentcc}Resent-Date: $tdate
707 Resent-Message-ID: <handler.$ref.$nn\@$gEmailDomain>
708 Resent-Sender: $gMaintainerEmail
709 X-$gProject-PR-Message: report $ref
710 X-$gProject-PR-Package: $data->{package}
711 X-$gProject-PR-Keywords: $data->{keywords}
712 $fwd
713 END
714 } elsif (@resentccs or @bccs) {
715     # D and F done far earlier; B just done - so this must be M or Q
716     # We preserve whichever it was in the Reply-To (possibly adding
717     # the $gBug#).
718     if (@resentccs) {
719         &htmllog($newref ? "Report" : "Information", "forwarded",
720                  $resentccval,
721                  "<code>$gBug#$ref</code>".
722                  (length($data->{package}) ? "; Package <code>".&sani($data->{package})."</code>" : '').
723                  ".");
724     } else {
725         &htmllog($newref ? "Report" : "Information", "stored",
726                  "",
727                  "<code>$gBug#$ref</code>".
728                  (length($data->{package}) ? "; Package <code>".&sani($data->{package})."</code>" : '').
729                  ".");
730     }
731     &sendmessage(<<END,[@resentccs],[@bccs]);
732 Subject: $gBug#$ref: $newsubject
733 Reply-To: $replyto, $ref-$baddressroot\@$gEmailDomain
734 Resent-From: $header{'from'}
735 ${orgsender}Resent-To: $resentccval
736 Resent-Date: $tdate
737 Resent-Message-ID: <handler.$ref.$nn\@$gEmailDomain>
738 Resent-Sender: $gMaintainerEmail
739 X-$gProject-PR-Message: report $ref
740 X-$gProject-PR-Package: $data->{package}
741 X-$gProject-PR-Keywords: $data->{keywords}
742 $fwd
743 END
744 }
745
746 $htmlbreak= length($brokenness) ? "<p>\n".&sani($brokenness)."\n<p>\n" : '';
747 $htmlbreak =~ s/\n\n/\n<P>\n\n/g;
748 if (length($resentccval)) {
749     $htmlbreak = "  Copy sent to <code>".&sani($resentccval)."</code>.".
750         $htmlbreak;
751 }
752 unless (exists $header{'x-debbugs-no-ack'}) {
753     if ($newref) {
754         &htmllog("Acknowledgement","sent",$replyto,
755                  ($veryquiet ?
756                   "New $gBug report received and filed, but not forwarded." :
757                   "New $gBug report received and forwarded."). $htmlbreak);
758         &sendmessage($veryquiet ? <<END : $codeletter eq 'M' ? <<END : <<END,'');
759 From: $gMaintainerEmail ($gProject $gBug Tracking System)
760 To: $replyto
761 Subject: $gBug#$ref: Acknowledgement of QUIET report
762          ($subject)
763 Message-ID: <handler.$ref.$nn.ackquiet\@$gEmailDomain>
764 In-Reply-To: $header{'message-id'}
765 References: $header{'message-id'}
766 Precedence: bulk
767 X-$gProject-PR-Message: ack-quiet $ref
768 X-$gProject-PR-Package: $data->{package}
769 X-$gProject-PR-Keywords: $data->{keywords}
770 Reply-To: $ref-quiet\@$gEmailDomain
771
772 Thank you for the problem report you have sent regarding $gProject.
773 This is an automatically generated reply, to let you know your message
774 has been received.  It has not been forwarded to the package maintainers
775 or other interested parties; you should ensure that the developers are
776 aware of the problem you have entered into the system - preferably
777 quoting the $gBug reference number, #$ref.
778 $resentccexplain
779 If you wish to submit further information on your problem, please send it
780 to $ref-$baddressroot\@$gEmailDomain (and *not*
781 to $baddress\@$gEmailDomain).
782
783 Please do not reply to the address at the top of this message,
784 unless you wish to report a problem with the $gBug-tracking system.
785 $brokenness
786 $gMaintainer
787 (administrator, $gProject $gBugs database)
788 END
789 From: $gMaintainerEmail ($gProject $gBug Tracking System)
790 To: $replyto
791 Subject: $gBug#$ref: Acknowledgement of maintainer-only report
792          ($subject)
793 Message-ID: <handler.$ref.$nn.ackmaint\@$gEmailDomain>
794 In-Reply-To: $header{'message-id'}
795 References: $header{'message-id'}
796 Precedence: bulk
797 X-$gProject-PR-Message: ack-maintonly $ref
798 X-$gProject-PR-Package: $data->{package}
799 X-$gProject-PR-Keywords: $data->{keywords}
800 Reply-To: $ref-maintonly\@$gEmailDomain
801
802 Thank you for the problem report you have sent regarding $gProject.
803 This is an automatically generated reply, to let you know your message has
804 been received.  It is being forwarded to the package maintainers (but not
805 other interested parties, as you requested) for their attention; they will
806 reply in due course.
807 $resentccexplain
808 If you wish to submit further information on your problem, please send
809 it to $ref-$baddressroot\@$gEmailDomain (and *not*
810 to $baddress\@$gEmailDomain).
811
812 Please do not reply to the address at the top of this message,
813 unless you wish to report a problem with the $gBug-tracking system.
814 $brokenness
815 $gMaintainer
816 (administrator, $gProject $gBugs database)
817 END
818 From: $gMaintainerEmail ($gProject $gBug Tracking System)
819 To: $replyto
820 Subject: $gBug#$ref: Acknowledgement ($subject)
821 Message-ID: <handler.$ref.$nn.ack\@$gEmailDomain>
822 In-Reply-To: $header{'message-id'}
823 References: $header{'message-id'}
824 Precedence: bulk
825 X-$gProject-PR-Message: ack $ref
826 X-$gProject-PR-Package: $data->{package}
827 X-$gProject-PR-Keywords: $data->{keywords}
828 Reply-To: $ref\@$gEmailDomain
829
830 Thank you for the problem report you have sent regarding $gProject.
831 This is an automatically generated reply, to let you know your message has
832 been received.  It is being forwarded to the package maintainers and other
833 interested parties for their attention; they will reply in due course.
834 $resentccexplain
835 If you wish to submit further information on your problem, please send
836 it to $ref\@$gEmailDomain (and *not* to
837 $baddress\@$gEmailDomain).
838
839 Please do not reply to the address at the top of this message,
840 unless you wish to report a problem with the $gBug-tracking system.
841 $brokenness
842 $gMaintainer
843 (administrator, $gProject $gBugs database)
844 END
845     } elsif ($codeletter ne 'U' and
846              $header{'precedence'} !~ /\b(?:bulk|junk|list)\b/) {
847         &htmllog("Acknowledgement","sent",$replyto,
848                  ($veryquiet ? "Extra info received and filed, but not forwarded." :
849                   $codeletter eq 'M' ? "Extra info received and forwarded to maintainer." :
850                   "Extra info received and forwarded to list."). $htmlbreak);
851         &sendmessage($veryquiet ? <<END : $codeletter eq 'M' ? <<END : <<END,'');
852 From: $gMaintainerEmail ($gProject $gBug Tracking System)
853 To: $replyto
854 Subject: $gBug#$ref: Info received and FILED only
855          (was $subject)
856 Message-ID: <handler.$ref.$nn.ackinfoquiet\@$gEmailDomain>
857 In-Reply-To: $header{'message-id'}
858 References: $header{'message-id'}
859 Precedence: bulk
860 X-$gProject-PR-Message: ack-info-quiet $ref
861 X-$gProject-PR-Package: $data->{package}
862 X-$gProject-PR-Keywords: $data->{keywords}
863 Reply-To: $ref-quiet\@$gEmailDomain
864
865 Thank you for the additional information you have supplied regarding
866 this problem report.  It has NOT been forwarded to the package
867 maintainers, but will accompany the original report in the $gBug
868 tracking system.  Please ensure that you yourself have sent a copy of
869 the additional information to any relevant developers or mailing lists.
870 $resentccexplain
871 If you wish to continue to submit further information on your problem,
872 please send it to $ref-$baddressroot\@$gEmailDomain, as before.
873
874 Please do not reply to the address at the top of this message,
875 unless you wish to report a problem with the $gBug-tracking system.
876 $brokenness
877 $gMaintainer
878 (administrator, $gProject $gBugs database)
879 END
880 From: $gMaintainerEmail ($gProject $gBug Tracking System)
881 To: $replyto
882 Subject: $gBug#$ref: Info received for maintainer only
883          (was $subject)
884 Message-ID: <handler.$ref.$nn.ackinfomaint\@$gEmailDomain>
885 In-Reply-To: $header{'message-id'}
886 References: $header{'message-id'}
887 Precedence: bulk
888 X-$gProject-PR-Message: ack-info-maintonly $ref
889 X-$gProject-PR-Package: $data->{package}
890 X-$gProject-PR-Keywords: $data->{keywords}
891 Reply-To: $ref-maintonly\@$gEmailDomain
892
893 Thank you for the additional information you have supplied regarding
894 this problem report.  It has been forwarded to the package maintainer(s)
895 (but not to other interested parties) to accompany the original report.
896 $resentccexplain
897 If you wish to continue to submit further information on your problem,
898 please send it to $ref-$baddressroot\@$gEmailDomain, as before.
899
900 Please do not reply to the address at the top of this message,
901 unless you wish to report a problem with the $gBug-tracking system.
902 $brokenness
903 $gMaintainer
904 (administrator, $gProject $gBugs database)
905 END
906 From: $gMaintainerEmail ($gProject $gBug Tracking System)
907 To: $replyto
908 Subject: $gBug#$ref: Info received (was $subject)
909 Message-ID: <handler.$ref.$nn.ackinfo\@$gEmailDomain>
910 In-Reply-To: $header{'message-id'}
911 References: $header{'message-id'}
912 Precedence: bulk
913 X-$gProject-PR-Message: ack-info $ref
914 X-$gProject-PR-Package: $data->{package}
915 X-$gProject-PR-Keywords: $data->{keywords}
916
917 Thank you for the additional information you have supplied regarding
918 this problem report.  It has been forwarded to the package maintainer(s)
919 and to other interested parties to accompany the original report.
920 $resentccexplain
921 If you wish to continue to submit further information on your problem,
922 please send it to $ref\@$gEmailDomain, as before.
923
924 Please do not reply to the address at the top of this message,
925 unless you wish to report a problem with the $gBug-tracking system.
926 $brokenness
927 $gMaintainer
928 (administrator, $gProject $gBugs database)
929 END
930 # Reply-To: in previous ack disabled by doogie due to mail loops.
931 # Are these still a concern?
932 # Reply-To: $ref\@$gEmailDomain
933     }
934 }
935
936 &appendlog;
937 &finish;
938
939 sub overwrite {
940     local ($f,$v) = @_;
941     open(NEW,">$f.new") || &quit("$f.new: create: $!");
942     print(NEW "$v") || &quit("$f.new: write: $!");
943     close(NEW) || &quit("$f.new: close: $!");
944     rename("$f.new","$f") || &quit("rename $f.new to $f: $!");
945 }
946
947 sub appendlog {
948     my $hash = get_hashname($ref);
949     if (!open(AP,">>db-h/$hash/$ref.log")) {
950         print DEBUG "failed open log<\n";
951         print DEBUG "failed open log err $!<\n";
952         &quit("opening db-h/$hash/$ref.log (li): $!");
953     }
954     print(AP "\7\n",@{escapelog(@log)},"\n\3\n") || &quit("writing db-h/$hash/$ref.log (li): $!");
955     close(AP) || &quit("closing db-h/$hash/$ref.log (li): $!");
956 }
957
958 sub finish {
959     utime(time,time,"db");
960     local ($u);
961     while ($u= $cleanups[$#cleanups]) { &$u; }
962     unlink("incoming/P$nn") || &quit("unlinking incoming/P$nn: $!");
963     exit $_[0];
964 }
965
966 &quit("wot no exit");
967
968 sub chldhandle { $chldexit = 'yes'; }
969
970 sub htmllog {
971     local ($whatobj,$whatverb,$where,$desc) = @_;
972     my $hash = get_hashname($ref);
973     open(AP,">>db-h/$hash/$ref.log") || &quit("opening db-h/$hash/$ref.log (lh): $!");
974     print(AP
975           "\6\n".
976           "<strong>$whatobj $whatverb</strong>".
977           ($where eq '' ? "" : " to <code>".&sani($where)."</code>").
978           ":<br>\n". $desc.
979           "\n\3\n") || &quit("writing db-h/$hash/$ref.log (lh): $!");
980     close(AP) || &quit("closing db-h/$hash/$ref.log (lh): $!");
981 }    
982
983 sub stripbccs {
984     my $msg = shift;
985     my $ret = '';
986     my $bcc = 0;
987     while ($msg =~ s/(.*\n)//) {
988         local $_ = $1;
989         if (/^$/) {
990             $ret .= $_;
991             last;
992         }
993         if ($bcc) {
994             # strip continuation lines too
995             next if /^\s/;
996             $bcc = 0;
997         }
998         if (/^Bcc:/i) {
999             $bcc = 1;
1000         } else {
1001             $ret .= $_;
1002         }
1003     }
1004     return $ret . $msg;
1005 }
1006
1007 sub sendmessage {
1008     local ($msg,$recips,$bcc) = @_;
1009     if ((!ref($recips) && $recips eq '') || @$recips == 0) {
1010         $recips = ['-t'];
1011     }
1012     $msg = "X-Loop: $gMaintainerEmail\n" . $msg;
1013
1014     my $hash = get_hashname($ref);
1015     #save email to the log
1016     open(AP,">>db-h/$hash/$ref.log") || &quit("opening db-h/$hash/$ref.log (lo): $!");
1017     print(AP "\2\n",join("\4",@$recips),"\n\5\n",
1018           @{escapelog(stripbccs($msg))},"\n\3\n") ||
1019         &quit("writing db-h/$hash/$ref.log (lo): $!");
1020     close(AP) || &quit("closing db-h/$hash/$ref.log (lo): $!");
1021
1022     if (ref($bcc)) {
1023         shift @$recips if $recips->[0] eq '-t';
1024         push @$recips, @$bcc;
1025     }
1026
1027 #if debugging.. save email to a log
1028 #    open AP, ">>debug";
1029 #    print AP join( '|', @$recips )."\n>>";
1030 #    print AP get_addresses( @$recips );
1031 #    print AP "<<\n".$msg;
1032 #    print AP "\n--------------------------------------------------------\n";
1033 #    close AP;
1034
1035     #start mailing
1036     $_ = '';
1037     $SIG{'CHLD'}='chldhandle';
1038     #print DEBUG "mailing sigchild set up<\n";
1039     $chldexit = 'no';
1040     $c= open(U,"-|");
1041     #print DEBUG "mailing opened pipe fork<\n";
1042     defined($c) || die $!;
1043     #print DEBUG "mailing opened pipe fork ok $c<\n";
1044     if (!$c) { # ie, we are in the child process
1045         #print DEBUG "mailing child<\n";
1046         unless (open(STDERR,">&STDOUT")) {
1047             #print DEBUG "mailing child opened stderr<\n";
1048             print STDOUT "redirect stderr: $!\n";
1049             #print DEBUG "mailing child opened stderr fail<\n";
1050             exit 1;
1051             #print DEBUG "mailing child opened stderr fail exit !?<\n";
1052         }
1053         #print DEBUG "mailing child opened stderr ok<\n";
1054         $c= open(D,"|-");
1055         #print DEBUG "mailing child forked again<\n";
1056         defined($c) || die $!;
1057         #print DEBUG "mailing child forked again ok $c<\n";
1058         if (!$c) { # ie, we are the child process
1059             #print DEBUG "mailing grandchild<\n";
1060             exec '/usr/lib/sendmail','-f'."$gMaintainerEmail",'-odq','-oem','-oi',get_addresses(@$recips);
1061             #print DEBUG "mailing grandchild exec failed<\n";
1062             die $!;
1063             #print DEBUG "mailing grandchild died !?<\n";
1064         }
1065         #print DEBUG "mailing child not grandchild<\n";
1066         print(D $msg) || die $!;
1067         #print DEBUG "mailing child printed msg<\n";
1068         close(D);
1069         #print DEBUG "mailing child closed pipe<\n";
1070         die "\n*** command returned exit status $?\n" if $?;
1071         #print DEBUG "mailing child exit status ok<\n";
1072         exit 0;
1073         #print DEBUG "mailing child exited ?!<\n";
1074     }
1075     #print DEBUG "mailing parent<\n";
1076     $results='';
1077     #print DEBUG "mailing parent results emptied<\n";
1078     while( $chldexit eq 'no' ) { $results.= $_; }
1079     #print DEBUG "mailing parent results read >$results<\n";
1080     close(U);
1081     #print DEBUG "mailing parent results closed<\n";
1082     $results.= "\n*** child returned exit status $?\n" if $?;
1083     #print DEBUG "mailing parent exit status ok<\n";
1084     $SIG{'CHLD'}='DEFAULT';
1085     #print DEBUG "mailing parent sigchild default<\n";
1086     if (length($results)) { &quit("running sendmail: $results"); }
1087     #print DEBUG "mailing parent results ok<\n";
1088 }
1089
1090 sub checkmaintainers {
1091     return if $maintainerschecked++;
1092     return if !length($data->{package});
1093     open(MAINT,"$gMaintainerFile") || die &quit("maintainers open: $!");
1094     while (<MAINT>) {
1095         m/^\n$/ && next;
1096         m/^\s*$/ && next;
1097         m/^(\S+)\s+(\S.*\S)\s*\n$/ || &quit("maintainers bogus \`$_'");
1098         $a= $1; $b= $2; $a =~ y/A-Z/a-z/;
1099         $maintainerof{$1}= $2;
1100     }
1101     close(MAINT);
1102     open(MAINT,"$gMaintainerFileOverride") || die &quit("maintainers.override open: $!");
1103     while (<MAINT>) {
1104         m/^\n$/ && next;
1105         m/^\s*$/ && next;
1106         m/^(\S+)\s+(\S.*\S)\s*\n$/ || &quit("maintainers.override bogus \`$_'");
1107         $a= $1; $b= $2; $a =~ y/A-Z/a-z/;
1108         $maintainerof{$1}= $2;
1109     }
1110     close(MAINT);
1111     open(SOURCES,"$gPackageSource") || &quit("pkgsrc open: $!");
1112     while (<SOURCES>) {
1113         next unless m/^(\S+)\s+\S+\s+(\S.*\S)\s*$/;
1114         ($a,$b)=($1,$2);
1115         $a =~ y/A-Z/a-z/;
1116         $pkgsrc{$a} = $b;
1117     }
1118     close(SOURCES);
1119     $anymaintfound=0; $anymaintnotfound=0;
1120     for $p (split(m/[ \t?,():]+/,$data->{package})) {
1121         $p =~ y/A-Z/a-z/;
1122         $p =~ /([a-z0-9.+-]+)/;
1123         $p = $1;
1124         next unless defined $p;
1125         if (defined $gSubscriptionDomain) {
1126             if (defined($pkgsrc{$p})) {
1127                 push @addsrcaddrs, "$pkgsrc{$p}\@$gSubscriptionDomain";
1128             } else {
1129                 push @addsrcaddrs, "$p\@$gSubscriptionDomain";
1130             }
1131         }
1132         if (defined($maintainerof{$p})) {
1133             print DEBUG "maintainer add >$p|$maintainerof{$p}<\n";
1134             $addmaint= $maintainerof{$p};
1135             push(@maintaddrs,$addmaint) unless
1136                 ($addmaint eq $replyto and $codeletter ne 'M') || 
1137                      grep($_ eq $addmaint, @maintaddrs);
1138             $anymaintfound++;
1139         } else {
1140             print DEBUG "maintainer none >$p<\n";
1141             push(@maintaddrs,$gUnknownMaintainerEmail) unless $anymaintnotfound;
1142             $anymaintnotfound++;
1143             last;
1144         }
1145     }
1146
1147     if (length $data->{owner}) {
1148         print DEBUG "owner add >$data->{package}|$data->{owner}<\n";
1149         $addmaint = $data->{owner};
1150         push(@maintaddrs, $addmaint) unless
1151             $addmaint eq $replyto or grep($_ eq $addmaint, @maintaddrs);
1152     }
1153 }