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