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