]> git.donarmstrong.com Git - debbugs.git/blob - scripts/process.in
[project @ 2005-07-17 16:06:26 by cjwatson]
[debbugs.git] / scripts / process.in
1 #!/usr/bin/perl
2 # $Id: process.in,v 1.93 2005/07/17 16:06:26 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         (&appendlog,&finish) if length($data->{done});
268         $receivedat= "done\@$gEmailDomain";
269         $markaswhat= 'done';
270         $set_done= $header{'from'};
271         if ( length( $gListDomain ) > 0 && length( $gDoneList ) > 0 ) {
272             $generalcc= "$gDoneList\@$gListDomain";
273         } else { 
274             $generalcc=''; 
275         }
276     }
277     if (defined $gStrongList and isstrongseverity($data->{severity})) {
278         $generalcc = join ', ', $generalcc, "$gStrongList\@$gListDomain";
279     }
280     if ($ref<0) {
281         &htmllog("Warning","sent",$replyto,"Message ignored.");
282         &sendmessage(<<END, '');
283 From: $gMaintainerEmail ($gProject $gBug Tracking System)
284 To: $replyto
285 Subject: Message with no $gBug number ignored by $receivedat
286          ($subject)
287 Message-ID: <header.x.$nn.warnignore\@$gEmailDomain>
288 In-Reply-To: $header{'message-id'}
289 References: $header{'message-id'} $data->{msgid}
290 Precedence: bulk
291 X-$gProject-PR-Message: error
292
293 You sent a message to the $gProject $gBug tracking system old-style
294 unified mark as $markaswhat address ($receivedat),
295 without a recognisable $gBug number in the Subject.
296 Your message has been filed under junk but otherwise ignored.
297
298 If you don't know what I'm talking about then probably either:
299
300 (a) you unwittingly sent a message to done\@$gEmailDomain
301 because you replied to all recipients of the message a developer used
302 to mark a $gBug as done and you modified the Subject.  In this case,
303 please do not be alarmed.  To avoid confusion do not do it again, but
304 there is no need to apologise or mail anyone asking for an explanation.
305
306 (b) you are a system administrator, reading this because the $gBug 
307 tracking system is responding to a misdirected bounce message.  In this
308 case there is a serious mail system misconfiguration somewhere - please
309 contact me immediately.
310
311 Your message was dated $header{'date'} and had
312 message-id $header{'message-id'}
313 and subject $subject.
314
315 If you need any assistance or explanation please contact me.
316
317 $gMaintainer
318 (administrator, $gProject $gBugs database)
319
320 END
321         &appendlog;
322         &finish;
323     }
324
325     &checkmaintainers;
326
327     $noticeccval.= join(', ', grep($_ ne $replyto,@maintaddrs));
328     $noticeccval =~ s/\s+\n\s+/ /g; 
329     $noticeccval =~ s/^\s+/ /; $noticeccval =~ s/\s+$//;
330
331     $generalcc = join(', ', $generalcc, @addsrcaddrs);
332     $generalcc =~ s/\s+\n\s+/ /g; 
333     $generalcc =~ s/^\s+/ /; $generalcc =~ s/\s+$//;
334
335     if (length($noticeccval)) { $noticecc= "Cc: $noticeccval\n"; }
336     if (length($generalcc)) { $noticecc.= "Bcc: $generalcc\n"; }
337
338     @process= ($ref,split(/ /,$data->{mergedwith}));
339     $orgref= $ref;
340
341     for $ref (@process) {
342         if ($ref != $orgref) {
343             &unfilelock;
344             $data = &lockreadbug($ref)
345                 || die "huh ? $ref from $orgref out of @process";
346         }
347         $data->{done}= $set_done if defined($set_done);
348         $data->{forwarded}= $set_forwarded if defined($set_forwarded);
349         if ($codeletter eq 'D') {
350             $data->{keywords} = join ' ', grep $_ ne 'pending',
351                                      split ' ', $data->{keywords};
352             if (defined $pheader{'source-version'}) {
353                 addfixedversions($data, $pheader{source}, $pheader{'source-version'});
354             } elsif (defined $pheader{version}) {
355                 addfixedversions($data, undef, $pheader{version});
356             }
357         }
358
359         writebug($ref, $data);
360
361         my $hash = get_hashname($ref);
362         open(O,"db-h/$hash/$ref.report") || &quit("read original report: $!");
363         $x= join('',<O>); close(O);
364         if ($codeletter eq 'F') {
365             &htmllog("Reply","sent",$replyto,"You have marked $gBug as forwarded.");
366             &sendmessage(<<END."---------------------------------------\n".join( "\n", @msg ), '');
367 From: $gMaintainerEmail ($gProject $gBug Tracking System)
368 To: $replyto
369 ${noticecc}Subject: $gBug#$ref: marked as forwarded ($data->{subject})
370 Message-ID: <header.$ref.$nn.ackfwdd\@$gEmailDomain>
371 In-Reply-To: $header{'message-id'}
372 References: $header{'message-id'} $data->{msgid}
373 Precedence: bulk
374 X-$gProject-PR-Message: forwarded $ref
375 X-$gProject-PR-Package: $data->{package}
376 X-$gProject-PR-Keywords: $data->{keywords}
377
378 Your message dated $header{'date'}
379 with message-id $header{'message-id'}
380 has caused the $gProject $gBug report #$ref,
381 regarding $data->{subject}
382 to be marked as having been forwarded to the upstream software
383 author(s) $data->{forwarded}.
384
385 (NB: If you are a system administrator and have no idea what I am
386 talking about this indicates a serious mail system misconfiguration
387 somewhere.  Please contact me immediately.)
388
389 $gMaintainer
390 (administrator, $gProject $gBugs database)
391
392 END
393
394         } else {
395             &htmllog("Reply","sent",$replyto,"You have taken responsibility.");
396             &sendmessage(<<END."--------------------------------------\n".$x."---------------------------------------\n".join( "\n", @msg ), '');
397 From: $gMaintainerEmail ($gProject $gBug Tracking System)
398 To: $replyto
399 ${noticecc}Subject: $gBug#$ref: marked as done ($data->{subject})
400 Message-ID: <handler.$ref.$nn.ackdone\@$gEmailDomain>
401 In-Reply-To: $header{'message-id'}
402 References: $header{'message-id'} $data->{msgid}
403 Precedence: bulk
404 X-$gProject-PR-Message: closed $ref
405 X-$gProject-PR-Package: $data->{package}
406 X-$gProject-PR-Keywords: $data->{keywords}
407
408 Your message dated $header{'date'}
409 with message-id $header{'message-id'}
410 and subject line $subject
411 has caused the attached $gBug report to be marked as done.
412
413 This means that you claim that the problem has been dealt with.
414 If this is not the case it is now your responsibility to reopen the
415 $gBug report if necessary, and/or fix the problem forthwith.
416
417 (NB: If you are a system administrator and have no idea what I am
418 talking about this indicates a serious mail system misconfiguration
419 somewhere.  Please contact me immediately.)
420
421 $gMaintainer
422 (administrator, $gProject $gBugs database)
423
424 END
425             &htmllog("Notification","sent",$data->{originator}, 
426                 "$gBug acknowledged by developer.");
427             &sendmessage(<<END.join("\n",@msg),'');
428 From: $gMaintainerEmail ($gProject $gBug Tracking System)
429 To: $data->{originator}
430 Subject: $gBug#$ref acknowledged by developer
431          ($header{'subject'})
432 Message-ID: <handler.$ref.$nn.notifdone\@$gEmailDomain>
433 In-Reply-To: $data->{msgid}
434 References: $header{'message-id'} $data->{msgid}
435 X-$gProject-PR-Message: they-closed $ref
436 X-$gProject-PR-Package: $data->{package}
437 X-$gProject-PR-Keywords: $data->{keywords}
438 Reply-To: $ref\@$gEmailDomain
439
440 This is an automatic notification regarding your $gBug report
441 #$ref: $data->{subject},
442 which was filed against the $data->{package} package.
443
444 It has been closed by one of the developers, namely
445 $markedby.
446
447 Their explanation is attached below.  If this explanation is
448 unsatisfactory and you have not received a better one in a separate
449 message then please contact the developer, by replying to this email.
450
451 $gMaintainer
452 (administrator, $gProject $gBugs database)
453
454 END
455         }
456         &appendlog;
457     }
458     &finish;
459 }
460
461 if ($ref<0) {
462     if ($codeletter eq 'U') {
463         &htmllog("Warning","sent",$replyto,"Message not forwarded.");
464         &sendmessage(<<END, '');
465 From: $gMaintainerEmail ($gProject $gBug Tracking System)
466 To: $replyto
467 Subject: Message with no $gBug number cannot be sent to submitter !
468          ($subject)
469 Message-ID: <handler.x.$nn.nonumnosub\@$gEmailDomain>
470 In-Reply-To: $header{'message-id'}
471 References: $header{'message-id'} $data->{msgid}
472 Precedence: bulk
473 X-$gProject-PR-Message: error
474
475 You sent a message to the $gProject $gBug tracking system's $gBug 
476 report submitter address $baddress\@$gEmailDomain, without a
477 recognisable $gBug number in the Subject.  Your message has been filed
478 under junk but otherwise ignored.
479
480 If you don't know what I'm talking about then probably either:
481
482 (a) you unwittingly sent a message to $baddress\@$gEmailDomain
483 because you replied to all recipients of the message a developer sent
484 to a $gBug's submitter and you modified the Subject.  In this case,
485 please do not be alarmed.  To avoid confusion do not do it again, but
486 there is no need to apologise or mail anyone asking for an
487 explanation.
488
489 (b) you are a system administrator, reading this because the $gBug 
490 tracking system is responding to a misdirected bounce message.  In this
491 case there is a serious mail system misconfiguration somewhere - please
492 contact me immediately.
493
494 Your message was dated $header{'date'} and had
495 message-id $header{'message-id'}
496 and subject $subject.
497
498 If you need any assistance or explanation please contact me.
499
500 $gMaintainer
501 (administrator, $gProject $gBugs database)
502
503 END
504         &appendlog;
505         &finish;
506     }
507
508     $data->{found_versions} = [];
509     $data->{fixed_versions} = [];
510
511     if (defined $pheader{source}) {
512         $data->{package} = $pheader{source};
513     } elsif (defined $pheader{package}) {
514         $data->{package} = $pheader{package};
515     } else {
516         &htmllog("Warning","sent",$replyto,"Message not forwarded.");
517         &sendmessage(<<END."---------------------------------------------------------------------------\n".join("\n", @msg), '');
518 From: $gMaintainerEmail ($gProject $gBug Tracking System)
519 To: $replyto
520 Subject: Message with no Package: tag cannot be processed!
521          ($subject)
522 Message-ID: <handler.x.$nn.nonumnosub\@$gEmailDomain>
523 In-Reply-To: $header{'message-id'}
524 References: $header{'message-id'} $data->{msgid}
525 Precedence: bulk
526 X-$gProject-PR-Message: error
527
528 Your message didn't have a Package: line at the start (in the
529 pseudo-header following the real mail header), or didn't have a
530 pseudo-header at all.  Your message has been filed under junk but
531 otherwise ignored.
532
533 This makes it much harder for us to categorise and deal with your
534 problem report. Please _resubmit_ your report to $baddress\@$gEmailDomain
535 and tell us which package the report is on. For help, check out
536 http://$gWebDomain/Reporting$gHTMLSuffix.
537
538 Your message was dated $header{'date'} and had
539 message-id $header{'message-id'}
540 and subject $subject.
541 The complete text of it is attached to this message.
542
543 If you need any assistance or explanation please contact me.
544
545 $gMaintainer
546 (administrator, $gProject $gBugs database)
547
548 END
549         &appendlog;
550         &finish;
551     }
552
553     $data->{keywords}= '';
554     if (defined($pheader{'keywords'})) {
555         $data->{keywords}= $pheader{'keywords'};
556     } elsif (defined($pheader{'tags'})) {
557         $data->{keywords}= $pheader{'tags'};
558     }
559     if (length($data->{keywords})) {
560         my @kws;
561         my %gkws = map { ($_, 1) } @gTags;
562         foreach my $kw (sort split(/[,\s]+/, lc($data->{keywords}))) {
563             push @kws, $kw if (defined $gkws{$kw});
564         }
565         $data->{keywords} = join(" ", @kws);
566     }
567     $data->{severity}= '';
568     if (defined($pheader{'severity'}) || defined($pheader{'priority'})) {
569         $data->{severity}= $pheader{'severity'};
570         $data->{severity}= $pheader{'priority'} unless ($data->{severity});
571         $data->{severity} =~ s/^\s*(.+)\s*$/$1/;
572
573         if (!grep($_ eq $data->{severity}, @severities, "$gDefaultSeverity")) {
574             $brokenness.= <<END;
575
576 Your message specified a Severity: in the pseudo-header, but
577 the severity value $data->{severity} was not recognised.
578 The default severity $gDefaultSeverity is being used instead.
579 The recognised values are: $gShowSeverities.
580 END
581 # if we use @gSeverityList array in the above line, perl -c gives:
582 # In string, @gSeverityList now must be written as \@gSeverityList at
583 #          process line 452, near "$gDefaultSeverity is being used instead.
584             $data->{severity}= '';
585         }
586     }
587     if (defined($pheader{owner})) {
588         $data->{owner}= $pheader{owner};
589     }
590     &filelock("nextnumber.lock");
591     open(N,"nextnumber") || &quit("nextnumber: read: $!");
592     $v=<N>; $v =~ s/\n$// || &quit("nextnumber bad format");
593     $ref= $v+0;  $v += 1;  $newref=1;
594     &overwrite('nextnumber', "$v\n");
595     &unfilelock;
596     my $hash = get_hashname($ref);
597     &overwrite("db-h/$hash/$ref.log",'');
598     $data->{originator} = $replyto;
599     $data->{date} = $intdate;
600     $data->{subject} = $subject;
601     $data->{msgid} = $header{'message-id'};
602     writebug($ref, $data);
603     &overwrite("db-h/$hash/$ref.report",
604                join("\n",@msg)."\n");
605 }
606
607 &checkmaintainers;
608
609 print DEBUG "maintainers >@maintaddrs<\n";
610
611 $orgsender= defined($header{'sender'}) ? "Original-Sender: $header{'sender'}\n" : '';
612 $newsubject= $subject;  $newsubject =~ s/^$gBug#$ref:*\s*//;
613
614 $xcchdr= $header{ 'x-debbugs-cc' };
615 if ($xcchdr =~ m/\S/) {
616     push(@resentccs,$xcchdr);
617     $resentccexplain.= <<END;
618
619 As you requested using X-Debbugs-CC, your message was also forwarded to
620    $xcchdr
621 (after having been given a $gBug report number, if it did not have one).
622 END
623 }
624
625 if (@maintaddrs && ($codeletter eq 'B' || $codeletter eq 'M')) {
626     push(@resentccs,@maintaddrs);
627     $resentccexplain.= <<END." ".join("\n ",@maintaddrs)."\n";
628
629 Your message has been sent to the package maintainer(s):
630 END
631 }
632
633 @bccs = @addsrcaddrs;
634 if (defined $gStrongList and isstrongseverity($data->{severity})) {
635     push @bccs, "$gStrongList\@$gListDomain";
636 }
637
638 if (defined $pheader{source}) {
639     # Prefix source versions with the name of the source package. They
640     # appear that way in version trees so that we can deal with binary
641     # packages moving from one source package to another.
642     if (defined $pheader{'source-version'}) {
643         addfoundversions($data, $pheader{source}, $pheader{'source-version'});
644     } elsif (defined $pheader{version}) {
645         addfoundversions($data, $pheader{source}, $pheader{version});
646     }
647     writebug($ref, $data);
648 } elsif (defined $pheader{package}) {
649     # TODO: could handle Source-Version: by looking up the source package?
650     addfoundversions($data, undef, $pheader{version}) if defined($pheader{version});
651     writebug($ref, $data);
652 }
653
654 $veryquiet= $codeletter eq 'Q';
655 if ($codeletter eq 'M' && !@maintaddrs) {
656     $veryquiet= 1;
657     $brokenness.= <<END;
658
659 You requested that the message be sent to the package maintainer(s)
660 but either the $gBug report is not associated with any package (probably
661 because of a missing Package pseudo-header field in the original $gBug
662 report), or the package(s) specified do not have any maintainer(s).
663
664 Your message has *not* been sent to any package maintainers; it has
665 merely been filed in the $gBug tracking system.  If you require assistance
666 please contact $gMaintainerEmail quoting the $gBug number $ref.
667 END
668 }
669
670 $resentccval.= join(', ',@resentccs);
671 $resentccval =~ s/\s+\n\s+/ /g; $resentccval =~ s/^\s+/ /; $resentccval =~ s/\s+$//;
672 if (length($resentccval)) { 
673     $resentcc= "Resent-CC: $resentccval\n"; 
674 }
675
676 if ($codeletter eq 'U') {
677     &htmllog("Message", "sent on", $data->{originator}, "$gBug#$ref.");
678     &sendmessage(<<END,[$data->{originator},@resentccs],[@bccs]);
679 Subject: $gBug#$ref: $newsubject
680 Reply-To: $replyto, $ref-quiet\@$gEmailDomain
681 ${orgsender}Resent-To: $data->{originator}
682 ${resentcc}Resent-Date: $tdate
683 Resent-Message-ID: <handler.$ref.$nn\@$gEmailDomain>
684 Resent-Sender: $gMaintainerEmail
685 X-$gProject-PR-Message: report $ref
686 X-$gProject-PR-Package: $data->{package}
687 X-$gProject-PR-Keywords: $data->{keywords}
688 $fwd
689 END
690 } elsif ($codeletter eq 'B') {
691     &htmllog($newref ? "Report" : "Information", "forwarded",
692              join(', ',"$gSubmitList\@$gListDomain",@resentccs),
693              "<code>$gBug#$ref</code>".
694              (length($data->{package})? "; Package <code>".&sani($data->{package})."</code>" : '').
695              ".");
696     &sendmessage(<<END,["$gSubmitList\@$gListDomain",@resentccs],[@bccs]);
697 Subject: $gBug#$ref: $newsubject
698 Reply-To: $replyto, $ref\@$gEmailDomain
699 Resent-From: $header{'from'}
700 ${orgsender}Resent-To: $gSubmitList\@$gListDomain
701 ${resentcc}Resent-Date: $tdate
702 Resent-Message-ID: <handler.$ref.$nn\@$gEmailDomain>
703 Resent-Sender: $gMaintainerEmail
704 X-$gProject-PR-Message: report $ref
705 X-$gProject-PR-Package: $data->{package}
706 X-$gProject-PR-Keywords: $data->{keywords}
707 $fwd
708 END
709 } elsif (@resentccs or @bccs) {
710     # D and F done far earlier; B just done - so this must be M or Q
711     # We preserve whichever it was in the Reply-To (possibly adding
712     # the $gBug#).
713     if (@resentccs) {
714         &htmllog($newref ? "Report" : "Information", "forwarded",
715                  $resentccval,
716                  "<code>$gBug#$ref</code>".
717                  (length($data->{package}) ? "; Package <code>".&sani($data->{package})."</code>" : '').
718                  ".");
719     } else {
720         &htmllog($newref ? "Report" : "Information", "stored",
721                  "",
722                  "<code>$gBug#$ref</code>".
723                  (length($data->{package}) ? "; Package <code>".&sani($data->{package})."</code>" : '').
724                  ".");
725     }
726     &sendmessage(<<END,[@resentccs],[@bccs]);
727 Subject: $gBug#$ref: $newsubject
728 Reply-To: $replyto, $ref-$baddressroot\@$gEmailDomain
729 Resent-From: $header{'from'}
730 ${orgsender}Resent-To: $resentccval
731 Resent-Date: $tdate
732 Resent-Message-ID: <handler.$ref.$nn\@$gEmailDomain>
733 Resent-Sender: $gMaintainerEmail
734 X-$gProject-PR-Message: report $ref
735 X-$gProject-PR-Package: $data->{package}
736 X-$gProject-PR-Keywords: $data->{keywords}
737 $fwd
738 END
739 }
740
741 $htmlbreak= length($brokenness) ? "<p>\n".&sani($brokenness)."\n<p>\n" : '';
742 $htmlbreak =~ s/\n\n/\n<P>\n\n/g;
743 if (length($resentccval)) {
744     $htmlbreak = "  Copy sent to <code>".&sani($resentccval)."</code>.".
745         $htmlbreak;
746 }
747 unless (exists $header{'x-debbugs-no-ack'}) {
748     if ($newref) {
749         &htmllog("Acknowledgement","sent",$replyto,
750                  ($veryquiet ?
751                   "New $gBug report received and filed, but not forwarded." :
752                   "New $gBug report received and forwarded."). $htmlbreak);
753         &sendmessage($veryquiet ? <<END : $codeletter eq 'M' ? <<END : <<END,'');
754 From: $gMaintainerEmail ($gProject $gBug Tracking System)
755 To: $replyto
756 Subject: $gBug#$ref: Acknowledgement of QUIET report
757          ($subject)
758 Message-ID: <handler.$ref.$nn.ackquiet\@$gEmailDomain>
759 In-Reply-To: $header{'message-id'}
760 References: $header{'message-id'}
761 Precedence: bulk
762 X-$gProject-PR-Message: ack-quiet $ref
763 X-$gProject-PR-Package: $data->{package}
764 X-$gProject-PR-Keywords: $data->{keywords}
765 Reply-To: $ref-quiet\@$gEmailDomain
766
767 Thank you for the problem report you have sent regarding $gProject.
768 This is an automatically generated reply, to let you know your message
769 has been received.  It has not been forwarded to the package maintainers
770 or other interested parties; you should ensure that the developers are
771 aware of the problem you have entered into the system - preferably
772 quoting the $gBug reference number, #$ref.
773 $resentccexplain
774 If you wish to submit further information on your problem, please send it
775 to $ref-$baddressroot\@$gEmailDomain (and *not*
776 to $baddress\@$gEmailDomain).
777
778 Please do not reply to the address at the top of this message,
779 unless you wish to report a problem with the $gBug-tracking system.
780 $brokenness
781 $gMaintainer
782 (administrator, $gProject $gBugs database)
783 END
784 From: $gMaintainerEmail ($gProject $gBug Tracking System)
785 To: $replyto
786 Subject: $gBug#$ref: Acknowledgement of maintainer-only report
787          ($subject)
788 Message-ID: <handler.$ref.$nn.ackmaint\@$gEmailDomain>
789 In-Reply-To: $header{'message-id'}
790 References: $header{'message-id'}
791 Precedence: bulk
792 X-$gProject-PR-Message: ack-maintonly $ref
793 X-$gProject-PR-Package: $data->{package}
794 X-$gProject-PR-Keywords: $data->{keywords}
795 Reply-To: $ref-maintonly\@$gEmailDomain
796
797 Thank you for the problem report you have sent regarding $gProject.
798 This is an automatically generated reply, to let you know your message has
799 been received.  It is being forwarded to the package maintainers (but not
800 other interested parties, as you requested) for their attention; they will
801 reply in due course.
802 $resentccexplain
803 If you wish to submit further information on your problem, please send
804 it to $ref-$baddressroot\@$gEmailDomain (and *not*
805 to $baddress\@$gEmailDomain).
806
807 Please do not reply to the address at the top of this message,
808 unless you wish to report a problem with the $gBug-tracking system.
809 $brokenness
810 $gMaintainer
811 (administrator, $gProject $gBugs database)
812 END
813 From: $gMaintainerEmail ($gProject $gBug Tracking System)
814 To: $replyto
815 Subject: $gBug#$ref: Acknowledgement ($subject)
816 Message-ID: <handler.$ref.$nn.ack\@$gEmailDomain>
817 In-Reply-To: $header{'message-id'}
818 References: $header{'message-id'}
819 Precedence: bulk
820 X-$gProject-PR-Message: ack $ref
821 X-$gProject-PR-Package: $data->{package}
822 X-$gProject-PR-Keywords: $data->{keywords}
823 Reply-To: $ref\@$gEmailDomain
824
825 Thank you for the problem report you have sent regarding $gProject.
826 This is an automatically generated reply, to let you know your message has
827 been received.  It is being forwarded to the package maintainers and other
828 interested parties for their attention; they will reply in due course.
829 $resentccexplain
830 If you wish to submit further information on your problem, please send
831 it to $ref\@$gEmailDomain (and *not* to
832 $baddress\@$gEmailDomain).
833
834 Please do not reply to the address at the top of this message,
835 unless you wish to report a problem with the $gBug-tracking system.
836 $brokenness
837 $gMaintainer
838 (administrator, $gProject $gBugs database)
839 END
840     } elsif ($codeletter ne 'U' and
841              $header{'precedence'} !~ /\b(?:bulk|junk|list)\b/) {
842         &htmllog("Acknowledgement","sent",$replyto,
843                  ($veryquiet ? "Extra info received and filed, but not forwarded." :
844                   $codeletter eq 'M' ? "Extra info received and forwarded to maintainer." :
845                   "Extra info received and forwarded to list."). $htmlbreak);
846         &sendmessage($veryquiet ? <<END : $codeletter eq 'M' ? <<END : <<END,'');
847 From: $gMaintainerEmail ($gProject $gBug Tracking System)
848 To: $replyto
849 Subject: $gBug#$ref: Info received and FILED only
850          (was $subject)
851 Message-ID: <handler.$ref.$nn.ackinfoquiet\@$gEmailDomain>
852 In-Reply-To: $header{'message-id'}
853 References: $header{'message-id'}
854 Precedence: bulk
855 X-$gProject-PR-Message: ack-info-quiet $ref
856 X-$gProject-PR-Package: $data->{package}
857 X-$gProject-PR-Keywords: $data->{keywords}
858 Reply-To: $ref-quiet\@$gEmailDomain
859
860 Thank you for the additional information you have supplied regarding
861 this problem report.  It has NOT been forwarded to the package
862 maintainers, but will accompany the original report in the $gBug
863 tracking system.  Please ensure that you yourself have sent a copy of
864 the additional information to any relevant developers or mailing lists.
865 $resentccexplain
866 If you wish to continue to submit further information on your problem,
867 please send it to $ref-$baddressroot\@$gEmailDomain, as before.
868
869 Please do not reply to the address at the top of this message,
870 unless you wish to report a problem with the $gBug-tracking system.
871 $brokenness
872 $gMaintainer
873 (administrator, $gProject $gBugs database)
874 END
875 From: $gMaintainerEmail ($gProject $gBug Tracking System)
876 To: $replyto
877 Subject: $gBug#$ref: Info received for maintainer only
878          (was $subject)
879 Message-ID: <handler.$ref.$nn.ackinfomaint\@$gEmailDomain>
880 In-Reply-To: $header{'message-id'}
881 References: $header{'message-id'}
882 Precedence: bulk
883 X-$gProject-PR-Message: ack-info-maintonly $ref
884 X-$gProject-PR-Package: $data->{package}
885 X-$gProject-PR-Keywords: $data->{keywords}
886 Reply-To: $ref-maintonly\@$gEmailDomain
887
888 Thank you for the additional information you have supplied regarding
889 this problem report.  It has been forwarded to the package maintainer(s)
890 (but not to other interested parties) to accompany the original report.
891 $resentccexplain
892 If you wish to continue to submit further information on your problem,
893 please send it to $ref-$baddressroot\@$gEmailDomain, as before.
894
895 Please do not reply to the address at the top of this message,
896 unless you wish to report a problem with the $gBug-tracking system.
897 $brokenness
898 $gMaintainer
899 (administrator, $gProject $gBugs database)
900 END
901 From: $gMaintainerEmail ($gProject $gBug Tracking System)
902 To: $replyto
903 Subject: $gBug#$ref: Info received (was $subject)
904 Message-ID: <handler.$ref.$nn.ackinfo\@$gEmailDomain>
905 In-Reply-To: $header{'message-id'}
906 References: $header{'message-id'}
907 Precedence: bulk
908 X-$gProject-PR-Message: ack-info $ref
909 X-$gProject-PR-Package: $data->{package}
910 X-$gProject-PR-Keywords: $data->{keywords}
911
912 Thank you for the additional information you have supplied regarding
913 this problem report.  It has been forwarded to the package maintainer(s)
914 and to other interested parties to accompany the original report.
915 $resentccexplain
916 If you wish to continue to submit further information on your problem,
917 please send it to $ref\@$gEmailDomain, as before.
918
919 Please do not reply to the address at the top of this message,
920 unless you wish to report a problem with the $gBug-tracking system.
921 $brokenness
922 $gMaintainer
923 (administrator, $gProject $gBugs database)
924 END
925 # Reply-To: in previous ack disabled by doogie due to mail loops.
926 # Are these still a concern?
927 # Reply-To: $ref\@$gEmailDomain
928     }
929 }
930
931 &appendlog;
932 &finish;
933
934 sub overwrite {
935     local ($f,$v) = @_;
936     open(NEW,">$f.new") || &quit("$f.new: create: $!");
937     print(NEW "$v") || &quit("$f.new: write: $!");
938     close(NEW) || &quit("$f.new: close: $!");
939     rename("$f.new","$f") || &quit("rename $f.new to $f: $!");
940 }
941
942 sub appendlog {
943     my $hash = get_hashname($ref);
944     if (!open(AP,">>db-h/$hash/$ref.log")) {
945         print DEBUG "failed open log<\n";
946         print DEBUG "failed open log err $!<\n";
947         &quit("opening db-h/$hash/$ref.log (li): $!");
948     }
949     print(AP "\7\n",@{escapelog(@log)},"\n\3\n") || &quit("writing db-h/$hash/$ref.log (li): $!");
950     close(AP) || &quit("closing db-h/$hash/$ref.log (li): $!");
951 }
952
953 sub finish {
954     utime(time,time,"db");
955     local ($u);
956     while ($u= $cleanups[$#cleanups]) { &$u; }
957     unlink("incoming/P$nn") || &quit("unlinking incoming/P$nn: $!");
958     exit $_[0];
959 }
960
961 &quit("wot no exit");
962
963 sub chldhandle { $chldexit = 'yes'; }
964
965 sub htmllog {
966     local ($whatobj,$whatverb,$where,$desc) = @_;
967     my $hash = get_hashname($ref);
968     open(AP,">>db-h/$hash/$ref.log") || &quit("opening db-h/$hash/$ref.log (lh): $!");
969     print(AP
970           "\6\n".
971           "<strong>$whatobj $whatverb</strong>".
972           ($where eq '' ? "" : " to <code>".&sani($where)."</code>").
973           ":<br>\n". $desc.
974           "\n\3\n") || &quit("writing db-h/$hash/$ref.log (lh): $!");
975     close(AP) || &quit("closing db-h/$hash/$ref.log (lh): $!");
976 }    
977
978 sub stripbccs {
979     my $msg = shift;
980     my $ret = '';
981     my $bcc = 0;
982     while ($msg =~ s/(.*\n)//) {
983         local $_ = $1;
984         if (/^$/) {
985             $ret .= $_;
986             last;
987         }
988         if ($bcc) {
989             # strip continuation lines too
990             next if /^\s/;
991             $bcc = 0;
992         }
993         if (/^Bcc:/i) {
994             $bcc = 1;
995         } else {
996             $ret .= $_;
997         }
998     }
999     return $ret . $msg;
1000 }
1001
1002 sub sendmessage {
1003     local ($msg,$recips,$bcc) = @_;
1004     if ((!ref($recips) && $recips eq '') || @$recips == 0) {
1005         $recips = ['-t'];
1006     }
1007     $msg = "X-Loop: $gMaintainerEmail\n" . $msg;
1008
1009     my $hash = get_hashname($ref);
1010     #save email to the log
1011     open(AP,">>db-h/$hash/$ref.log") || &quit("opening db-h/$hash/$ref.log (lo): $!");
1012     print(AP "\2\n",join("\4",@$recips),"\n\5\n",
1013           @{escapelog(stripbccs($msg))},"\n\3\n") ||
1014         &quit("writing db-h/$hash/$ref.log (lo): $!");
1015     close(AP) || &quit("closing db-h/$hash/$ref.log (lo): $!");
1016
1017     if (ref($bcc)) {
1018         shift @$recips if $recips->[0] eq '-t';
1019         push @$recips, @$bcc;
1020     }
1021
1022 #if debugging.. save email to a log
1023 #    open AP, ">>debug";
1024 #    print AP join( '|', @$recips )."\n>>";
1025 #    print AP get_addresses( @$recips );
1026 #    print AP "<<\n".$msg;
1027 #    print AP "\n--------------------------------------------------------\n";
1028 #    close AP;
1029
1030     #start mailing
1031     $_ = '';
1032     $SIG{'CHLD'}='chldhandle';
1033     #print DEBUG "mailing sigchild set up<\n";
1034     $chldexit = 'no';
1035     $c= open(U,"-|");
1036     #print DEBUG "mailing opened pipe fork<\n";
1037     defined($c) || die $!;
1038     #print DEBUG "mailing opened pipe fork ok $c<\n";
1039     if (!$c) { # ie, we are in the child process
1040         #print DEBUG "mailing child<\n";
1041         unless (open(STDERR,">&STDOUT")) {
1042             #print DEBUG "mailing child opened stderr<\n";
1043             print STDOUT "redirect stderr: $!\n";
1044             #print DEBUG "mailing child opened stderr fail<\n";
1045             exit 1;
1046             #print DEBUG "mailing child opened stderr fail exit !?<\n";
1047         }
1048         #print DEBUG "mailing child opened stderr ok<\n";
1049         $c= open(D,"|-");
1050         #print DEBUG "mailing child forked again<\n";
1051         defined($c) || die $!;
1052         #print DEBUG "mailing child forked again ok $c<\n";
1053         if (!$c) { # ie, we are the child process
1054             #print DEBUG "mailing grandchild<\n";
1055             exec '/usr/lib/sendmail','-f'."$gMaintainerEmail",'-odq','-oem','-oi',get_addresses(@$recips);
1056             #print DEBUG "mailing grandchild exec failed<\n";
1057             die $!;
1058             #print DEBUG "mailing grandchild died !?<\n";
1059         }
1060         #print DEBUG "mailing child not grandchild<\n";
1061         print(D $msg) || die $!;
1062         #print DEBUG "mailing child printed msg<\n";
1063         close(D);
1064         #print DEBUG "mailing child closed pipe<\n";
1065         die "\n*** command returned exit status $?\n" if $?;
1066         #print DEBUG "mailing child exit status ok<\n";
1067         exit 0;
1068         #print DEBUG "mailing child exited ?!<\n";
1069     }
1070     #print DEBUG "mailing parent<\n";
1071     $results='';
1072     #print DEBUG "mailing parent results emptied<\n";
1073     while( $chldexit eq 'no' ) { $results.= $_; }
1074     #print DEBUG "mailing parent results read >$results<\n";
1075     close(U);
1076     #print DEBUG "mailing parent results closed<\n";
1077     $results.= "\n*** child returned exit status $?\n" if $?;
1078     #print DEBUG "mailing parent exit status ok<\n";
1079     $SIG{'CHLD'}='DEFAULT';
1080     #print DEBUG "mailing parent sigchild default<\n";
1081     if (length($results)) { &quit("running sendmail: $results"); }
1082     #print DEBUG "mailing parent results ok<\n";
1083 }
1084
1085 sub checkmaintainers {
1086     return if $maintainerschecked++;
1087     return if !length($data->{package});
1088     open(MAINT,"$gMaintainerFile") || die &quit("maintainers open: $!");
1089     while (<MAINT>) {
1090         m/^\n$/ && next;
1091         m/^\s*$/ && next;
1092         m/^(\S+)\s+(\S.*\S)\s*\n$/ || &quit("maintainers bogus \`$_'");
1093         $a= $1; $b= $2; $a =~ y/A-Z/a-z/;
1094         $maintainerof{$1}= $2;
1095     }
1096     close(MAINT);
1097     open(MAINT,"$gMaintainerFileOverride") || die &quit("maintainers.override open: $!");
1098     while (<MAINT>) {
1099         m/^\n$/ && next;
1100         m/^\s*$/ && next;
1101         m/^(\S+)\s+(\S.*\S)\s*\n$/ || &quit("maintainers.override bogus \`$_'");
1102         $a= $1; $b= $2; $a =~ y/A-Z/a-z/;
1103         $maintainerof{$1}= $2;
1104     }
1105     close(MAINT);
1106     open(SOURCES,"$gPackageSource") || &quit("pkgsrc open: $!");
1107     while (<SOURCES>) {
1108         next unless m/^(\S+)\s+\S+\s+(\S.*\S)\s*$/;
1109         ($a,$b)=($1,$2);
1110         $a =~ y/A-Z/a-z/;
1111         $pkgsrc{$a} = $b;
1112     }
1113     close(SOURCES);
1114     $anymaintfound=0; $anymaintnotfound=0;
1115     for $p (split(m/[ \t?,():]+/,$data->{package})) {
1116         $p =~ y/A-Z/a-z/;
1117         $p =~ /([a-z0-9.+-]+)/;
1118         $p = $1;
1119         next unless defined $p;
1120         if (defined $gSubscriptionDomain) {
1121             if (defined($pkgsrc{$p})) {
1122                 push @addsrcaddrs, "$pkgsrc{$p}\@$gSubscriptionDomain";
1123             } else {
1124                 push @addsrcaddrs, "$p\@$gSubscriptionDomain";
1125             }
1126         }
1127         if (defined($maintainerof{$p})) {
1128             print DEBUG "maintainer add >$p|$maintainerof{$p}<\n";
1129             $addmaint= $maintainerof{$p};
1130             push(@maintaddrs,$addmaint) unless
1131                 ($addmaint eq $replyto and $codeletter ne 'M') || 
1132                      grep($_ eq $addmaint, @maintaddrs);
1133             $anymaintfound++;
1134         } else {
1135             print DEBUG "maintainer none >$p<\n";
1136             push(@maintaddrs,$gUnknownMaintainerEmail) unless $anymaintnotfound;
1137             $anymaintnotfound++;
1138             last;
1139         }
1140     }
1141
1142     if (length $data->{owner}) {
1143         print DEBUG "owner add >$data->{package}|$data->{owner}<\n";
1144         $addmaint = $data->{owner};
1145         push(@maintaddrs, $addmaint) unless
1146             $addmaint eq $replyto or grep($_ eq $addmaint, @maintaddrs);
1147     }
1148 }