]> git.donarmstrong.com Git - debbugs.git/blobdiff - scripts/process.in
add default package support
[debbugs.git] / scripts / process.in
index 9f3fffc1dcf3e26225089638d9a5ee9f933f882e..deaadf2a56d0166ec363c9ed80711b04454e7a93 100755 (executable)
@@ -4,26 +4,27 @@
 # Usage: process nn
 # Temps:  incoming/Pnn
 
+use warnings;
+use strict;
+
 use POSIX qw(strftime);
 
 use MIME::Parser;
-use Debbugs::MIME qw(decode_rfc1522 create_mime_message);
+use Debbugs::MIME qw(decode_rfc1522 create_mime_message getmailbody);
 use Debbugs::Mail qw(send_mail_message encode_headers);
 use Debbugs::Packages qw(getpkgsrc);
 use Debbugs::User qw(read_usertags write_usertags);
+use Debbugs::Common qw(:lock get_hashname);
+use Debbugs::Status qw(writebug isstrongseverity lockreadbugmerge lockreadbug);
 
-use HTML::Entities qw(encode_entities);
+use Debbugs::CGI qw(html_escape bug_url);
 
-# TODO DLA; needs config reworking and errorlib reworking
-# use warnings;
-# use strict;
+use Debbugs::Log qw(:misc);
 
-use Debbugs::Status qw(:versions);
-use Debbugs::Config qw(:globals);
-my $lib_path = $gLibPath;
+use Debbugs::Text qw(:templates);
 
-require "$lib_path/errorlib";
-$ENV{'PATH'} = $lib_path.':'.$ENV{'PATH'};
+use Debbugs::Status qw(:versions);
+use Debbugs::Config qw(:globals :config);
 
 chdir( "$gSpoolDir" ) || die "chdir spool: $!\n";
 
@@ -74,6 +75,22 @@ END
 # header and decoded body respectively
 my (@headerlines, @bodylines);
 
+# whether maintainer addresses have been checked
+our $maintainerschecked = 0;
+#maintainer address for this message
+our @maintaddrs;
+# other src addresses
+our @addsrcaddrs;
+our @resentccs;
+our @bccs;
+
+my $resentccexplain='';
+
+# whether there's a new reference with this email
+our $newref = 0;
+
+our $brokenness = '';
+
 my $parser = new MIME::Parser;
 mkdir "$gSpoolDir/mime.tmp", 0777;
 $parser->output_under("$gSpoolDir/mime.tmp");
@@ -126,6 +143,7 @@ for my $hdr (@headerlines) {
        print DEBUG "!>$_<\n";
     }
 }
+$header{'message-id'} = '' if not defined $header{'message-id'};
 
 # remove blank lines
 shift @bodylines while @bodylines and $bodylines[0] !~ /\S/;
@@ -143,6 +161,8 @@ if (@bodylines and $bodylines[0] =~ /^-----BEGIN PGP SIGNED/) {
     map { s/^- // } @bodylines;
 }
 
+#psuedoheaders
+my %pheader;
 # extract pseudo-headers
 for my $phline (@bodylines)
 {
@@ -152,7 +172,7 @@ for my $phline (@bodylines)
     print DEBUG ">$fn|$fv|\n";
     $fn = lc $fn;
     # Don't lc owner or forwarded
-    $fv = lc $fv unless $fh =~ /^(?:owner|forwarded|usertags)$/;
+    $fv = lc $fv unless $fn =~ /^(?:owner|forwarded|usertags|version|source-version)$/;
     $pheader{$fn} = $fv;
     print DEBUG ">$fn~$fv<\n";
 }
@@ -182,12 +202,7 @@ unless (length $replyto) {
 my $subject = '(no subject)';
 if (!defined($header{'subject'})) 
 {
-       $brokenness.= <<END;
-
-Your message did not contain a Subject field. They are recommended and
-useful because the title of a $gBug is determined using this field.
-Please remember to include a Subject field in your messages in future.
-END
+       $brokenness.= fill_template('mail/process_broken_subject');
 
 } else { 
     $subject= $header{'subject'}; 
@@ -198,55 +213,32 @@ $subject =~ s/^Re:\s*//i; $_= $subject."\n";
 if ($tryref < 0 && m/^Bug ?\#(\d+)\D/i) {
     $tryref= $1+0; 
 }
-
+my $data;
 if ($tryref >= 0) 
 {
+     my $bfound;
     ($bfound, $data)= &lockreadbugmerge($tryref);
     if ($bfound) { 
         $ref= $tryref; 
     } else {
         &htmllog("Reply","sent", $replyto,"Unknown problem report number <code>$tryref</code>.");
-        my $archivenote = '';
-        if ($gRemoveAge) {
-            $archivenote = <<END;
-This may be because that $gBug report has been resolved for more than $gRemoveAge
-days, and the record of it has been archived and made read-only, or
-because you mistyped the $gBug report number.
-
-END
-        }
-        &sendmessage(<<END, '');
-From: $gMaintainerEmail ($gProject $gBug Tracking System)
-To: $replyto
-Subject: Unknown problem report $gBug#$tryref ($subject)
-Message-ID: <handler.x.$nn.unknown\@$gEmailDomain>
-In-Reply-To: $header{'message-id'}
-References: $header{'message-id'} $data->{msgid}
-Precedence: bulk
-X-$gProject-PR-Message: error
-
-You sent a message to the $gBug tracking system which gave (in the
-Subject line or encoded into the recipient at $gEmailDomain),
-the number of a nonexistent $gBug report (#$tryref).
-
-${archivenote}Your message was dated $header{'date'} and was sent to
-$baddress\@$gEmailDomain.  It had
-Message-ID $header{'message-id'}
-and Subject $subject.
-
-It has been filed (under junk) but otherwise ignored.
-
-Please consult your records to find the correct $gBug report number, or
-contact me, the system administrator, for assistance.
-
-$gMaintainer
-(administrator, $gProject $gBugs database)
-
-(NB: If you are a system administrator and have no idea what I am
-talking about this indicates a serious mail system misconfiguration
-somewhere.  Please contact me immediately.)
-
-END
+        &sendmessage(create_mime_message(
+          [From          => "$gMaintainerEmail ($gProject $gBug Tracking System)",
+          To            => $replyto,
+          Subject       => "Unknown problem report $gBug#$tryref ($subject)",
+          'Message-ID'  => "<handler.x.$nn.unknown\@$gEmailDomain>",
+          'In-Reply-To' => $header{'message-id'},
+          References    => join(' ',grep {defined $_} $header{'message-id'},$data->{msgid}),
+          Precedence    => 'bulk',
+          "X-$gProject-PR-Message" => 'error',
+         ],message_body_template('process_unknown_bug_number',
+                                 {subject => $subject,
+                                  date    => $header{date},
+                                  baddress => $baddress,
+                                  tryref   => $tryref,
+                                  messageid => $header{'message-id'},
+                                 },
+                                )),'');
         &appendlog;
         &finish;
     }
@@ -272,14 +264,20 @@ if ($codeletter eq 'D' || $codeletter eq 'F')
 {
     if ($replyto =~ m/$gBounceFroms/o ||
         $header{'from'} =~ m/$gBounceFroms/o)
-    { 
-        &quit("bounce detected !  Mwaap! Mwaap!"); 
+    {
+        print STDERR "bounce detected !  Mwaap! Mwaap!";
+        exit 1;
     }
-    $markedby= $header{'from'} eq $replyto ? $replyto :
+    my $markedby= $header{'from'} eq $replyto ? $replyto :
                "$header{'from'} (reply to $replyto)";
     my @generalcc;
+    my $receivedat;
+    my $markaswhat;
+    my $set_forwarded;
+    my $generalcc;
+    my $set_done;
     if ($codeletter eq 'F') { # Forwarded
-        (&appendlog,&finish) if length($data->{forwarded});
+        (&appendlog,&finish) if defined $data->{forwarded} and length($data->{forwarded});
         $receivedat= "forwarded\@$gEmailDomain";
         $markaswhat= 'forwarded';
         $set_forwarded= $header{'to'};
@@ -294,7 +292,7 @@ if ($codeletter eq 'D' || $codeletter eq 'F')
            $generalcc=''; 
         }
     } else { # Done
-        if (length($data->{done}) and
+        if (defined $data->{done} and length($data->{done}) and
                 not defined $pheader{'source-version'} and
                 not defined $pheader{'version'}) {
             &appendlog;
@@ -316,45 +314,23 @@ if ($codeletter eq 'D' || $codeletter eq 'F')
     }
     if ($ref<0) {
        &htmllog("Warning","sent",$replyto,"Message ignored.");
-        &sendmessage(<<END, '');
-From: $gMaintainerEmail ($gProject $gBug Tracking System)
-To: $replyto
-Subject: Message with no $gBug number ignored by $receivedat
-         ($subject)
-Message-ID: <header.x.$nn.warnignore\@$gEmailDomain>
-In-Reply-To: $header{'message-id'}
-References: $header{'message-id'} $data->{msgid}
-Precedence: bulk
-X-$gProject-PR-Message: error
-
-You sent a message to the $gProject $gBug tracking system old-style
-unified mark as $markaswhat address ($receivedat),
-without a recognisable $gBug number in the Subject.
-Your message has been filed under junk but otherwise ignored.
-
-If you don't know what I'm talking about then probably either:
-
-(a) you unwittingly sent a message to done\@$gEmailDomain
-because you replied to all recipients of the message a developer used
-to mark a $gBug as done and you modified the Subject.  In this case,
-please do not be alarmed.  To avoid confusion do not do it again, but
-there is no need to apologise or mail anyone asking for an explanation.
-
-(b) you are a system administrator, reading this because the $gBug 
-tracking system is responding to a misdirected bounce message.  In this
-case there is a serious mail system misconfiguration somewhere - please
-contact me immediately.
-
-Your message was dated $header{'date'} and had
-message-id $header{'message-id'}
-and subject $subject.
-
-If you need any assistance or explanation please contact me.
-
-$gMaintainer
-(administrator, $gProject $gBugs database)
-
-END
+       &sendmessage(create_mime_message(
+          [From          => "$gMaintainerEmail ($gProject $gBug Tracking System)",
+          To            => $replyto,
+          Subject       => "Message with no $gBug number ignored by $receivedat ($subject)",
+          'Message-ID'  => "<handler.x.$nn.warnignore\@$gEmailDomain>",
+          'In-Reply-To' => $header{'message-id'},
+          References    => join(' ',grep {defined $_} $header{'message-id'},$data->{msgid}),
+          Precedence    => 'bulk',
+          "X-$gProject-PR-Message" => 'error',
+         ],message_body_template('mail/process_no_bug_number',
+                                 {subject => $subject,
+                                  date    => $header{date},
+                                  markaswhat => $markaswhat,
+                                  receivedat => $receivedat,
+                                  messageid => $header{'message-id'},
+                                 },
+                                )),'');
        &appendlog;
        &finish;
     }
@@ -362,18 +338,18 @@ END
     &checkmaintainers;
 
     my @noticecc = grep($_ ne $replyto,@maintaddrs);
-    $noticeccval.= join(', ', grep($_ ne $replyto,@maintaddrs));
+    my $noticeccval.= join(', ', grep($_ ne $replyto,@maintaddrs));
     $noticeccval =~ s/\s+\n\s+/ /g; 
     $noticeccval =~ s/^\s+/ /; $noticeccval =~ s/\s+$//;
 
-    @process= ($ref,split(/ /,$data->{mergedwith}));
-    $orgref= $ref;
+    my @process= ($ref,split(/ /,$data->{mergedwith}));
+    my $orgref= $ref;
 
     for $ref (@process) {
        if ($ref != $orgref) {
            &unfilelock;
            $data = &lockreadbug($ref)
-               || die "huh ? $ref from $orgref out of @process";
+               || die "huh ? $ref from $orgref out of ".join(' ',@process);
        }
         $data->{done}= $set_done if defined($set_done);
         $data->{forwarded}= $set_forwarded if defined($set_forwarded);
@@ -399,7 +375,7 @@ END
 
        my $hash = get_hashname($ref);
         open(O,"db-h/$hash/$ref.report") || &quit("read original report: $!");
-        $x= join('',<O>); close(O);
+        my $orig_report= join('',<O>); close(O);
         if ($codeletter eq 'F') {
            &htmllog("Reply","sent",$replyto,"You have marked $gBug as forwarded.");
             &sendmessage(create_mime_message(
@@ -409,30 +385,20 @@ END
               Subject       => "$gBug#$ref: marked as forwarded ($data->{subject})",
               "Message-ID"  => "<header.$ref.$nn.ackfwdd\@$gEmailDomain>",
               "In-Reply-To" => $header{'message-id'},
-              References    => "$header{'message-id'} $data->{msgid}",
+              References    => join(' ',grep {defined $_} $header{'message-id'},$data->{msgid}),
               Precedence    => 'bulk',
               "X-$gProject-PR-Message"  => "forwarded $ref",
               "X-$gProject-PR-Package"  => $data->{package},
               "X-$gProject-PR-Keywords" => $data->{keywords},
              # Only have a X-$gProject-PR-Source when we know the source package
-             length($source_package)?("X-$gProject-PR-Source" => $source_package):(),
-             ],<<END ,[join("\n",@msg)]),'',[$replyto,@generalbcc,@noticecc],1);
-Your message dated $header{'date'}
-with message-id $header{'message-id'}
-has caused the $gProject $gBug report #$ref,
-regarding $data->{subject}
-to be marked as having been forwarded to the upstream software
-author(s) $data->{forwarded}.
-
-(NB: If you are a system administrator and have no idea what I am
-talking about this indicates a serious mail system misconfiguration
-somewhere.  Please contact me immediately.)
-
-$gMaintainer
-(administrator, $gProject $gBugs database)
-
-END
-
+             (defined($source_package) and length($source_package))?("X-$gProject-PR-Source" => $source_package):(),
+             ],message_body_template('mail/process_mark_as_forwarded',
+                                    {date => $header{date},
+                                     messageid => $header{'message-id'},
+                                     data      => $data,
+                                    },
+                                   ),
+            [join("\n",@msg)]),'',[$replyto,@generalbcc,@noticecc],1);
         } else {
            &htmllog("Reply","sent",$replyto,"You have taken responsibility.");
             &sendmessage(create_mime_message(
@@ -442,32 +408,22 @@ END
               Subject       => "$gBug#$ref: marked as done ($data->{subject})",
               "Message-ID"  => "<handler.$ref.$nn.ackdone\@$gEmailDomain>",
               "In-Reply-To" => $header{'message-id'},
-              References    => "$header{'message-id'} $data->{msgid}",
+              References    => join(' ',grep {defined $_} $header{'message-id'},$data->{msgid}),
               Precedence    => 'bulk',
               "X-$gProject-PR-Message"  => "closed $ref",
               "X-$gProject-PR-Package"  => $data->{package},
               "X-$gProject-PR-Keywords" => $data->{keywords},
              # Only have a X-$gProject-PR-Source when we know the source package
-             length($source_package)?("X-$gProject-PR-Source" => $source_package):(),
-             ],<<END ,[$x,join("\n",@msg)]),'',[$replyto,@generalbcc,@noticecc],1);
-Your message dated $header{'date'}
-with message-id $header{'message-id'}
-and subject line $subject
-has caused the attached $gBug report to be marked as done.
-
-This means that you claim that the problem has been dealt with.
-If this is not the case it is now your responsibility to reopen the
-$gBug report if necessary, and/or fix the problem forthwith.
-
-(NB: If you are a system administrator and have no idea what I am
-talking about this indicates a serious mail system misconfiguration
-somewhere.  Please contact me immediately.)
-
-$gMaintainer
-(administrator, $gProject $gBugs database)
-
-END
-            &htmllog("Notification","sent",$data->{originator}, 
+             (defined($source_package) and length($source_package))?("X-$gProject-PR-Source" => $source_package):(),
+             ],message_body_template('mail/process_mark_as_done',
+                                    {date => $header{date},
+                                     messageid => $header{'message-id'},
+                                     subject   => $header{subject},
+                                     data      => $data,
+                                    },
+                                   ),
+            [$orig_report,join("\n",@msg)]),'',[$replyto,@generalbcc,@noticecc],1);
+            &htmllog("Notification","sent",$data->{originator},
                "$gBug acknowledged by developer.");
             &sendmessage(create_mime_message(
             ["X-Loop"      => "$gMaintainerEmail",
@@ -476,30 +432,22 @@ END
               Subject       => "$gBug#$ref closed by $markedby ($header{'subject'})",
               "Message-ID"  => "<handler.$ref.$nn.notifdone\@$gEmailDomain>",
               "In-Reply-To" => "$data->{msgid}",
-              References    => "$header{'message-id'} $data->{msgid}",
+              References    => join(' ',grep {defined $_} $header{'message-id'},$data->{msgid}),
               "X-$gProject-PR-Message"  => "they-closed $ref",
               "X-$gProject-PR-Package"  => "$data->{package}",
               "X-$gProject-PR-Keywords" => "$data->{keywords}",
              # Only have a X-$gProject-PR-Source when we know the source package
-             length($source_package)?("X-$gProject-PR-Source" => $source_package):(),
+             (defined($source_package) and length($source_package))?("X-$gProject-PR-Source" => $source_package):(),
               "Reply-To"                => "$ref\@$gEmailDomain",
               "Content-Type"            => 'text/plain; charset="utf-8"',
-             ],<<END ,[join("\n",@msg)]),'',undef,1);
-This is an automatic notification regarding your $gBug report
-#$ref: $data->{subject},
-which was filed against the $data->{package} package.
-
-It has been closed by $markedby.
-
-Their explanation is attached below.  If this explanation is
-unsatisfactory and you have not received a better one in a separate
-message then please contact $markedby by replying
-to this email.
-
-$gMaintainer
-(administrator, $gProject $gBugs database)
-
-END
+             ],message_body_template('mail/process_your_bug_done',
+                                    {data      => $data,
+                                     markedby  => $markedby,
+                                     messageid => $header{'message-id'},
+                                     subject   => $header{subject},
+                                    },
+                                   ),
+            [join("\n",@msg),$orig_report]),'',undef,1);
         }
        &appendlog;
     }
@@ -509,46 +457,23 @@ END
 if ($ref<0) {
     if ($codeletter eq 'U') {
         &htmllog("Warning","sent",$replyto,"Message not forwarded.");
-        &sendmessage(<<END, '');
-From: $gMaintainerEmail ($gProject $gBug Tracking System)
-To: $replyto
-Subject: Message with no $gBug number cannot be sent to submitter !
-         ($subject)
-Message-ID: <handler.x.$nn.nonumnosub\@$gEmailDomain>
-In-Reply-To: $header{'message-id'}
-References: $header{'message-id'} $data->{msgid}
-Precedence: bulk
-X-$gProject-PR-Message: error
-
-You sent a message to the $gProject $gBug tracking system's $gBug 
-report submitter address $baddress\@$gEmailDomain, without a
-recognisable $gBug number in the Subject.  Your message has been filed
-under junk but otherwise ignored.
-
-If you don't know what I'm talking about then probably either:
-
-(a) you unwittingly sent a message to $baddress\@$gEmailDomain
-because you replied to all recipients of the message a developer sent
-to a $gBug\'s submitter and you modified the Subject.  In this case,
-please do not be alarmed.  To avoid confusion do not do it again, but
-there is no need to apologise or mail anyone asking for an
-explanation.
-
-(b) you are a system administrator, reading this because the $gBug 
-tracking system is responding to a misdirected bounce message.  In this
-case there is a serious mail system misconfiguration somewhere - please
-contact me immediately.
-
-Your message was dated $header{'date'} and had
-message-id $header{'message-id'}
-and subject $subject.
-
-If you need any assistance or explanation please contact me.
-
-$gMaintainer
-(administrator, $gProject $gBugs database)
-
-END
+       &sendmessage(create_mime_message(
+          [From          => "$gMaintainerEmail ($gProject $gBug Tracking System)",
+          To            => $replyto,
+          Subject       => "Message with no $gBug number cannot be sent to submitter! ($subject)",
+          'Message-ID'  => "<handler.x.$nn.nonumnosub\@$gEmailDomain>",
+          'In-Reply-To' => $header{'message-id'},
+          References    => join(' ',grep {defined $_} $header{'message-id'},$data->{msgid}),
+          Precedence    => 'bulk',
+          "X-$gProject-PR-Message" => 'error',
+         ],message_body_template('mail/process_no_bug_number',
+                                 {subject => $subject,
+                                  date    => $header{date},
+                                  markaswhat => 'submitter',
+                                  receivedat => "$baddress\@$gEmailDomain",
+                                  messageid => $header{'message-id'},
+                                 },
+                                )),'');
        &appendlog;
        &finish;
     }
@@ -560,8 +485,13 @@ END
         $data->{package} = $pheader{source};
     } elsif (defined $pheader{package}) {
         $data->{package} = $pheader{package};
-    } else {
+    } elsif (defined $config{default_package}) {
+       $data->{package} = $config{default_package},
+    }
+    else {
        &htmllog("Warning","sent",$replyto,"Message not forwarded.");
+       my $body = message_body_template('mail/process_no_package',
+                                       );
         &sendmessage(create_mime_message(
                        ["X-Loop"      => "$gMaintainerEmail",
                        From          => "$gMaintainerEmail ($gProject $gBug Tracking System)",
@@ -569,36 +499,37 @@ END
                         Subject       => "Message with no Package: tag cannot be processed! ($subject)",
                         "Message-ID"  => "<handler.x.$nn.nonumnosub\@$gEmailDomain>",
                         "In-Reply-To" => $header{'message-id'},
-                        References    => "$header{'message-id'} $data->{msgid}",
+                        References    => join(' ',grep {defined $_} $header{'message-id'},$data->{msgid}),
                         Precedence    => 'bulk',
                         "X-$gProject-PR-Message" => 'error'
-                      ],<<END,[join("\n", @msg)]), '',undef,1);
-
-Your message didn't have a Package: line at the start (in the
-pseudo-header following the real mail header), or didn't have a
-pseudo-header at all.  Your message has been filed under junk but
-otherwise ignored.
-
-This makes it much harder for us to categorise and deal with your
-problem report. Please _resubmit_ your report to $baddress\@$gEmailDomain
-and tell us which package the report is on. For help, check out
-http://$gWebDomain/Reporting$gHTMLSuffix.
-
-Your message was dated $header{'date'} and had
-message-id $header{'message-id'}
-and subject $subject.
-The complete text of it is attached to this message.
-
-If you need any assistance or explanation please contact me.
-
-$gMaintainer
-(administrator, $gProject $gBugs database)
-
-END
+                      ],
+          message_body_template('mail/process_no_package',
+                                {date => $header{date},
+                                 subject => $subject,
+                                 messageid => $header{'message-id'},
+                                 baddress => $baddress,
+                                },
+                               ),[join("\n", @msg)]), '',undef,1);
        &appendlog;
        &finish;
     }
 
+    if (defined $config{default_package}) {
+        &checkmaintainers;
+        # if there are no maintainers for this package, assign it to the default package
+        if (not @maintaddrs) {
+             $data->{package} = $config{default_package};
+             $brokenness.= fill_template('mail/process_default_package_selected',
+                                         {old_package => $pheader{source} || $pheader{package} || 'No package',
+                                          new_package => $data->{package},
+                                         }
+                                        );
+             # force the maintainers to be rechecked
+             $maintainerschecked = 0;
+             &checkmaintainers;
+        }
+    }
+
     $data->{keywords}= '';
     if (defined($pheader{'keywords'})) {
         $data->{keywords}= $pheader{'keywords'};
@@ -619,17 +550,10 @@ END
        $data->{severity}= $pheader{'priority'} unless ($data->{severity});
        $data->{severity} =~ s/^\s*(.+)\s*$/$1/;
 
-       if (!grep($_ eq $data->{severity}, @severities, "$gDefaultSeverity")) {
-            $brokenness.= <<END;
-
-Your message specified a Severity: in the pseudo-header, but
-the severity value $data->{severity} was not recognised.
-The default severity $gDefaultSeverity is being used instead.
-The recognised values are: $gShowSeverities.
-END
-# if we use @gSeverityList array in the above line, perl -c gives:
-# In string, @gSeverityList now must be written as \@gSeverityList at
-#          process line 452, near "$gDefaultSeverity is being used instead.
+       if (!grep($_ eq $data->{severity}, @gSeverityList, "$gDefaultSeverity")) {
+            $brokenness.= fill_template('mail/invalid_severity',
+                                       {severity=>$data->{severity}}
+                                      );
             $data->{severity}= '';
         }
     }
@@ -641,9 +565,9 @@ END
     }
     &filelock("nextnumber.lock");
     open(N,"nextnumber") || &quit("nextnumber: read: $!");
-    $v=<N>; $v =~ s/\n$// || &quit("nextnumber bad format");
-    $ref= $v+0;  $v += 1;  $newref=1;
-    &overwrite('nextnumber', "$v\n");
+    my $nextnumber=<N>; $nextnumber =~ s/\n$// || &quit("nextnumber bad format");
+    $ref= $nextnumber+0;  $nextnumber += 1;  $newref=1;
+    &overwrite('nextnumber', "$nextnumber\n");
     &unfilelock;
     my $hash = get_hashname($ref);
     &overwrite("db-h/$hash/$ref.log",'');
@@ -667,18 +591,17 @@ END
              for my $tag (split /[,\s]+/, $pheader{usertags}) {
                   if ($tag =~ /^[a-zA-Z0-9.+\@-]+/) {
                        my %bugs_with_tag; 
-                       @bugs_with_tag{@{$user_tags{$tag}}} = (1) x @{$user_tags{$tag}};
+                       @bugs_with_tag{@{$user_tags{$tag}||[]}} = (1) x @{$user_tags{$tag}||[]};
                        $bugs_with_tag{$ref} = 1;
                        $user_tags{$tag} = [keys %bugs_with_tag];
                   }
              }
-             write_usertags(\%usertags,$user);
+             write_usertags(\%user_tags,$user);
         }
         else {
-             $brokenness .=<<END;
-Your message tried to set a usertag, but didn't have a valid
-user set ('$user' isn't valid)
-END
+             $brokenness .= fill_template('mail/invalid_user',
+                                          {user => $user}
+                                         );
         }
     }
     &overwrite("db-h/$hash/$ref.report",
@@ -687,28 +610,25 @@ END
 
 &checkmaintainers;
 
-print DEBUG "maintainers >@maintaddrs<\n";
+print DEBUG "maintainers >".join(' ',@maintaddrs)."<\n";
 
-$orgsender= defined($header{'sender'}) ? "Original-Sender: $header{'sender'}\n" : '';
-$newsubject= $subject;  $newsubject =~ s/^$gBug#$ref:*\s*//;
+my $orgsender= defined($header{'sender'}) ? "Original-Sender: $header{'sender'}\n" : '';
+my $newsubject= $subject;  $newsubject =~ s/^$gBug#$ref:*\s*//;
 
-$xcchdr= $header{ 'x-debbugs-cc' };
+my $xcchdr= $header{ 'x-debbugs-cc' } || '';
 if ($xcchdr =~ m/\S/) {
     push(@resentccs,$xcchdr);
-    $resentccexplain.= <<END;
-
-As you requested using X-Debbugs-CC, your message was also forwarded to
-   $xcchdr
-(after having been given a $gBug report number, if it did not have one).
-END
+    $resentccexplain.= fill_template('mail/xdebbugscc',
+                                    {xcchdr => $xcchdr},
+                                   );
 }
 
 if (@maintaddrs && ($codeletter eq 'B' || $codeletter eq 'M')) {
     push(@resentccs,@maintaddrs);
-    $resentccexplain.= <<END." ".join("\n ",@maintaddrs)."\n";
-
-Your message has been sent to the package maintainer(s):
-END
+    $resentccexplain.= fill_template('mail/maintainercc',
+                                    {maintaddrs => \@maintaddrs,
+                                    },
+                                   );
 }
 
 @bccs = @addsrcaddrs;
@@ -735,24 +655,17 @@ if (defined $pheader{source}) {
     writebug($ref, $data);
 }
 
-$veryquiet= $codeletter eq 'Q';
+my $veryquiet= $codeletter eq 'Q';
 if ($codeletter eq 'M' && !@maintaddrs) {
     $veryquiet= 1;
-    $brokenness.= <<END;
-
-You requested that the message be sent to the package maintainer(s)
-but either the $gBug report is not associated with any package (probably
-because of a missing Package pseudo-header field in the original $gBug
-report), or the package(s) specified do not have any maintainer(s).
-
-Your message has *not* been sent to any package maintainers; it has
-merely been filed in the $gBug tracking system.  If you require assistance
-please contact $gMaintainerEmail quoting the $gBug number $ref.
-END
+    $brokenness.= fill_template('mail/invalid_maintainer',
+                               {},
+                              );
 }
 
-$resentccval.= join(', ',@resentccs);
+my $resentccval.= join(', ',@resentccs);
 $resentccval =~ s/\s+\n\s+/ /g; $resentccval =~ s/^\s+/ /; $resentccval =~ s/\s+$//;
+my $resentcc = '';
 if (length($resentccval)) { 
     $resentcc= "Resent-CC: $resentccval\n"; 
 }
@@ -775,7 +688,7 @@ END
     &htmllog($newref ? "Report" : "Information", "forwarded",
              join(', ',"$gSubmitList\@$gListDomain",@resentccs),
              "<code>$gBug#$ref</code>".
-             (length($data->{package})? "; Package <code>".encode_entities($data->{package})."</code>" : '').
+             (length($data->{package})? "; Package <code>".html_escape($data->{package})."</code>" : '').
              ".");
     &sendmessage(<<END,["$gSubmitList\@$gListDomain",@resentccs],[@bccs]);
 Subject: $gBug#$ref: $newsubject
@@ -798,13 +711,13 @@ END
         &htmllog($newref ? "Report" : "Information", "forwarded",
                  $resentccval,
                  "<code>$gBug#$ref</code>".
-                 (length($data->{package}) ? "; Package <code>".encode_entities($data->{package})."</code>" : '').
+                 (length($data->{package}) ? "; Package <code>".html_escape($data->{package})."</code>" : '').
                  ".");
     } else {
         &htmllog($newref ? "Report" : "Information", "stored",
                  "",
                  "<code>$gBug#$ref</code>".
-                 (length($data->{package}) ? "; Package <code>".encode_entities($data->{package})."</code>" : '').
+                 (length($data->{package}) ? "; Package <code>".html_escape($data->{package})."</code>" : '').
                  ".");
     }
     &sendmessage(<<END,[@resentccs],[@bccs]);
@@ -822,240 +735,114 @@ ${source_pr_header}$fwd
 END
 }
 
-$htmlbreak= length($brokenness) ? "<p>\n".encode_entities($brokenness)."\n<p>\n" : '';
+my $htmlbreak= length($brokenness) ? "<p>\n".html_escape($brokenness)."\n<p>\n" : '';
 $htmlbreak =~ s/\n\n/\n<P>\n\n/g;
 if (length($resentccval)) {
-    $htmlbreak = "  Copy sent to <code>".encode_entities($resentccval)."</code>.".
+    $htmlbreak = "  Copy sent to <code>".html_escape($resentccval)."</code>.".
         $htmlbreak;
 }
-unless (exists $header{'x-debbugs-no-ack'}) {
-    if ($newref) {
-        &htmllog("Acknowledgement","sent",$replyto,
-                 ($veryquiet ?
-                  "New $gBug report received and filed, but not forwarded." :
-                  "New $gBug report received and forwarded."). $htmlbreak);
-       if ($veryquiet) {
-            &sendmessage(create_mime_message(
-                      ["X-Loop"      => "$gMaintainerEmail",
-                       From          => "$gMaintainerEmail ($gProject $gBug Tracking System)",
-                       To            => $replyto,
-                        Subject       => "$gBug#$ref: Acknowledgement of QUIET report ($subject)",
-                        "Message-ID"  => "<handler.$ref.$nn.ackquiet\@$gEmailDomain>",
-                        "In-Reply-To" => $header{'message-id'},
-                        References    => $header{'message-id'},
-                        Precedence    => 'bulk',
-                       "X-$gProject-PR-Message"  => "ack-quiet $ref",
-                       "X-$gProject-PR-Package"  => $data->{package},
-                       "X-$gProject-PR-Keywords" => $data->{keywords},
-                       # Only have a X-$gProject-PR-Source when we know the source package
-                       length($source_package)?("X-$gProject-PR-Source" => $source_package):(),
-                       "Reply-To"                => "$ref-quiet\@$gEmailDomain",
-                      ],<<END,[join("\n", @msg)]), '',undef,1);
-Thank you for the problem report you have sent regarding $gProject.
-This is an automatically generated reply, to let you know your message
-has been received.  It has not been forwarded to the package maintainers
-or other interested parties; you should ensure that the developers are
-aware of the problem you have entered into the system - preferably
-quoting the $gBug reference number, #$ref.
-$resentccexplain
-If you wish to submit further information on your problem, please send it
-to $ref-$baddressroot\@$gEmailDomain (and *not*
-to $baddress\@$gEmailDomain).
-
-If you have filed this report in error and wish to close it, please
-send mail to $ref-done\@$gEmailDomain with an explanation
-why the bug report should be closed.
-
-Please do not reply to the address at the top of this message,
-unless you wish to report a problem with the $gBug-tracking system.
-$brokenness
-$gMaintainer
-(administrator, $gProject $gBugs database)
-END
-       }
-       elsif ($codeletter eq 'M') { # Maintonly
-            &sendmessage(create_mime_message(
-                      ["X-Loop"      => "$gMaintainerEmail",
-                       From          => "$gMaintainerEmail ($gProject $gBug Tracking System)",
-                       To            => $replyto,
-                        Subject       => "$gBug#$ref: Acknowledgement of maintainer-only report ($subject)",
-                        "Message-ID"  => "<handler.$ref.$nn.ackmaint\@$gEmailDomain>",
-                        "In-Reply-To" => $header{'message-id'},
-                        References    => $header{'message-id'},
-                        Precedence    => 'bulk',
-                       "X-$gProject-PR-Message" => "ack-maintonly $ref",
-                       "X-$gProject-PR-Package"  => $data->{package},
-                       "X-$gProject-PR-Keywords" => $data->{keywords},
-                       # Only have a X-$gProject-PR-Source when we know the source package
-                       length($source_package)?("X-$gProject-PR-Source" => $source_package):(),
-                       "Reply-To"               => "$ref-maintonly\@$gEmailDomain",
-                      ],<<END,[]), '',undef,1);
-Thank you for the problem report you have sent regarding $gProject.
-This is an automatically generated reply, to let you know your message has
-been received.  It is being forwarded to the package maintainers (but not
-other interested parties, as you requested) for their attention; they will
-reply in due course.
-$resentccexplain
-If you wish to submit further information on your problem, please send
-it to $ref-$baddressroot\@$gEmailDomain (and *not*
-to $baddress\@$gEmailDomain).
-
-If you have filed this report in error and wish to close it, please
-send mail to $ref-done\@$gEmailDomain with an explanation
-why the bug report should be closed.
-
-Please do not reply to the address at the top of this message,
-unless you wish to report a problem with the $gBug-tracking system.
-$brokenness
-$gMaintainer
-(administrator, $gProject $gBugs database)
-END
-                  }
-       else {
-            &sendmessage(create_mime_message(
-                      ["X-Loop"      => "$gMaintainerEmail",
-                       From          => "$gMaintainerEmail ($gProject $gBug Tracking System)",
-                       To            => $replyto,
-                        Subject       => "$gBug#$ref: Acknowledgement ($subject)",
-                        "Message-ID"  => "<handler.$ref.$nn.ack\@$gEmailDomain>",
-                        "In-Reply-To" => $header{'message-id'},
-                        References    => $header{'message-id'},
-                        Precedence    => 'bulk',
-                       "X-$gProject-PR-Message"  => "ack $ref",
-                       "X-$gProject-PR-Package"  => $data->{package},
-                       "X-$gProject-PR-Keywords" => $data->{keywords},
-                       # Only have a X-$gProject-PR-Source when we know the source package
-                       length($source_package)?("X-$gProject-PR-Source" => $source_package):(),
-                       "Reply-To"                => "$ref\@$gEmailDomain",
-                      ],<<END,[]), '',undef,1);
-Thank you for the problem report you have sent regarding $gProject.
-This is an automatically generated reply, to let you know your message has
-been received.  It is being forwarded to the package maintainers and other
-interested parties for their attention; they will reply in due course.
-$resentccexplain
-If you wish to submit further information on your problem, please send
-it to $ref\@$gEmailDomain (and *not* to
-$baddress\@$gEmailDomain).
-
-If you have filed this report in error and wish to close it, please
-send mail to $ref-done\@$gEmailDomain with an explanation
-why the bug report should be closed.
-
-Please do not reply to the address at the top of this message,
-unless you wish to report a problem with the $gBug-tracking system.
-$brokenness
-$gMaintainer
-(administrator, $gProject $gBugs database)
-END
-                  }
-    } elsif ($codeletter ne 'U' and
-             $header{'precedence'} !~ /\b(?:bulk|junk|list)\b/) {
-        &htmllog("Acknowledgement","sent",$replyto,
-                 ($veryquiet ? "Extra info received and filed, but not forwarded." :
-                  $codeletter eq 'M' ? "Extra info received and forwarded to maintainer." :
-                  "Extra info received and forwarded to list."). $htmlbreak);
-       if ($veryquiet) {
-            &sendmessage(create_mime_message(
+
+# Should we send an ack out?
+if (not exists $header{'x-debbugs-no-ack'} and
+    ($newref or
+     ($codeletter ne 'U' and
+      (not defined $header{precedence} or
+       $header{'precedence'} !~ /\b(?:bulk|junk|list)\b/
+      )
+     )
+    )
+   ){
+
+     # figure out forward explanation
+     my $forwardexplain = '';
+     my $thanks = '';
+     my $extra_vars;
+     # will contain info and -info in moreinfo messages
+     my $info = '';
+     my $infod = '';
+     # temporary headers
+     my %t_h;
+     if ($newref) {
+         &htmllog("Acknowledgement","sent",$replyto,
+                  ($veryquiet ?
+                   "New $gBug report received and filed, but not forwarded." :
+                   "New $gBug report received and forwarded."). $htmlbreak);
+         $thanks = fill_template('mail/process_ack_thanks_new');
+     }
+     else {
+         &htmllog("Acknowledgement","sent",$replyto,
+                  ($veryquiet ? "Extra info received and filed, but not forwarded." :
+                   $codeletter eq 'M' ? "Extra info received and forwarded to maintainer." :
+                   "Extra info received and forwarded to list."). $htmlbreak);
+         $thanks = fill_template('mail/process_ack_thanks_additional');
+         $info = 'info';
+         $infod = '-info';
+     }
+     if ($veryquiet) {
+         $forwardexplain = fill_template('mail/forward_veryquiet',
+                                        );
+         # these are the headers that quiet messages override
+         $t_h{messageid}  = "<handler.$ref.$nn.ack${info}quiet\@$gEmailDomain>";
+         $t_h{pr_message} = "ack${infod}-quiet $ref";
+         $t_h{reply_to}   = "$ref-quiet\@$gEmailDomain";
+         $extra_vars->{refreplyto} = "$ref-quiet\@$gEmailDomain";
+         $t_h{subject}    = length($info)?
+              "$gBug#$ref: Info received and FILED only ($subject)":
+              "$gBug#$ref: Acknowledgement of QUIET report ($subject)";
+     }
+     elsif ($codeletter eq 'M') {
+         $forwardexplain = fill_template('mail/forward_maintonly',
+                                        );
+         # these are the headers that maintonly messages override
+         $t_h{messageid}  = "<handler.$ref.$nn.ack{$info}maintonly\@$gEmailDomain>";
+         $t_h{pr_message} = "ack${infod}-maintonly $ref";
+         $t_h{reply_to}   = "$ref-maintonly\@$gEmailDomain";
+         $extra_vars->{refreplyto} = "$ref-maintonly\@$gEmailDomain";
+         $t_h{subject}    = length($info)?
+              "$gBug#$ref: Info received for maintainer only ($subject)":
+              "$gBug#$ref: Acknowledgement of maintainer-only report ($subject)";
+     }
+     else {
+         $forwardexplain = fill_template('mail/forward_normal',
+                                        );
+         $t_h{messageid}  = "<handler.$ref.$nn.ack${info}\@$gEmailDomain>";
+         $t_h{pr_message} = "ack${infod} $ref";
+         $t_h{reply_to}   = "$ref\@$gEmailDomain";
+         $extra_vars->{refreplyto} = "$ref\@$gEmailDomain";
+         $t_h{subject}    = (defined $info and length($info))?
+              "$gBug#$ref: Info received ($subject)" :
+              "$gBug#$ref: Acknowledgement ($subject)";
+     }
+     my $body = message_body_template('mail/process_ack',
+                                     {forwardexplain  => $forwardexplain,
+                                      resentccexplain => $resentccexplain,
+                                      thanks          => $thanks,
+                                      %{$extra_vars}
+                                     }
+                                    );
+     &sendmessage(create_mime_message(
                       ["X-Loop"      => "$gMaintainerEmail",
                        From          => "$gMaintainerEmail ($gProject $gBug Tracking System)",
                        To            => $replyto,
-                        Subject       => "$gBug#$ref: Info received and FILED only (was $subject)",
-                        "Message-ID"  => "<handler.$ref.$nn.ackinfoquiet\@$gEmailDomain>",
-                        "In-Reply-To" => $header{'message-id'},
+                       Subject       => $t_h{subject},
+                       "Message-ID"  => $t_h{messageid},
+                       "In-Reply-To" => $header{'message-id'},
                         References    => $header{'message-id'},
                         Precedence    => 'bulk',
-                       "X-$gProject-PR-Message" => "ack-info-quiet $ref",
-                       "X-$gProject-PR-Package"  => $data->{package},
-                       "X-$gProject-PR-Keywords" => $data->{keywords},
-                       # Only have a X-$gProject-PR-Source when we know the source package
-                       length($source_package)?("X-$gProject-PR-Source" => $source_package):(),
-                       "Reply-To"               => "$ref-maintonly\@$gEmailDomain",
-                      ],<<END,[]), '',undef,1);
-Thank you for the additional information you have supplied regarding
-this problem report.  It has NOT been forwarded to the package
-maintainers, but will accompany the original report in the $gBug
-tracking system.  Please ensure that you yourself have sent a copy of
-the additional information to any relevant developers or mailing lists.
-$resentccexplain
-If you wish to continue to submit further information on this problem,
-please send it to $ref-$baddressroot\@$gEmailDomain, as before.
-
-Please do not reply to the address at the top of this message,
-unless you wish to report a problem with the $gBug-tracking system.
-$brokenness
-$gMaintainer
-(administrator, $gProject $gBugs database)
-END
-                }
-       elsif ($codeletter eq 'M') {
-            &sendmessage(create_mime_message(
-                      ["X-Loop"      => "$gMaintainerEmail",
-                       From          => "$gMaintainerEmail ($gProject $gBug Tracking System)",
-                       To            => $replyto,
-                        Subject       => "$gBug#$ref: Info received for maintainer only (was $subject)",
-                        "Message-ID"  => "<handler.$ref.$nn.ackinfomaint\@$gEmailDomain>",
-                        "In-Reply-To" => $header{'message-id'},
-                        References    => "$header{'message-id'} $data->{msgid}",
-                        Precedence    => 'bulk',
-                       "X-$gProject-PR-Message" => "ack-info-maintonly $ref",
-                       "X-$gProject-PR-Package"  => $data->{package},
-                       "X-$gProject-PR-Keywords" => $data->{keywords},
-                       "Reply-To"               => "$ref-maintonly\@$gEmailDomain",
-                      ],<<END,[]), '',undef,1);
-Thank you for the additional information you have supplied regarding
-this problem report.  It has been forwarded to the package maintainer(s)
-(but not to other interested parties) to accompany the original report.
-$resentccexplain
-If you wish to continue to submit further information on this problem,
-please send it to $ref-$baddressroot\@$gEmailDomain, as before.
-
-Please do not reply to the address at the top of this message,
-unless you wish to report a problem with the $gBug-tracking system.
-$brokenness
-$gMaintainer
-(administrator, $gProject $gBugs database)
-END
-                  }
-       else {
-            &sendmessage(create_mime_message(
-                      ["X-Loop"      => "$gMaintainerEmail",
-                       From          => "$gMaintainerEmail ($gProject $gBug Tracking System)",
-                       To            => $replyto,
-                        Subject       => "$gBug#$ref: Info received ($subject)",
-                        "Message-ID"  => "<handler.$ref.$nn.ackinfo\@$gEmailDomain>",
-                        "In-Reply-To" => $header{'message-id'},
-                        References    => "$header{'message-id'} $data->{msgid}",
-                        Precedence    => 'bulk',
-                       "X-$gProject-PR-Message"  => "ack-info $ref",
+                       "X-$gProject-PR-Message"  => $t_h{pr_message} || "ack $ref",
                        "X-$gProject-PR-Package"  => $data->{package},
                        "X-$gProject-PR-Keywords" => $data->{keywords},
                        # Only have a X-$gProject-PR-Source when we know the source package
-                       length($source_package)?("X-$gProject-PR-Source" => $source_package):(),
-                       "Reply-To"                => "$ref\@$gEmailDomain",
-                      ],<<END,[]), '',undef,1);
-Thank you for the additional information you have supplied regarding
-this problem report.  It has been forwarded to the package maintainer(s)
-and to other interested parties to accompany the original report.
-$resentccexplain
-If you wish to continue to submit further information on this problem,
-please send it to $ref\@$gEmailDomain, as before.
-
-Please do not reply to the address at the top of this message,
-unless you wish to report a problem with the $gBug-tracking system.
-$brokenness
-$gMaintainer
-(administrator, $gProject $gBugs database)
-END
-
-                  }
-   }
+                       (defined($source_package) and length($source_package))?("X-$gProject-PR-Source" => $source_package):(),
+                       "Reply-To"                => $t_h{reply_to} || "$ref\@$gEmailDomain",
+                      ],$body,[]), '',undef,1);
 }
 
 &appendlog;
 &finish;
 
 sub overwrite {
-    local ($f,$v) = @_;
+    my ($f,$v) = @_;
     open(NEW,">$f.new") || &quit("$f.new: create: $!");
     print(NEW "$v") || &quit("$f.new: write: $!");
     close(NEW) || &quit("$f.new: close: $!");
@@ -1069,28 +856,31 @@ sub appendlog {
         print DEBUG "failed open log err $!<\n";
         &quit("opening db-h/$hash/$ref.log (li): $!");
     }
-    print(AP "\7\n",@{escapelog(@log)},"\n\3\n") || &quit("writing db-h/$hash/$ref.log (li): $!");
+    print(AP "\7\n",escape_log(@log),"\n\3\n") || &quit("writing db-h/$hash/$ref.log (li): $!");
     close(AP) || &quit("closing db-h/$hash/$ref.log (li): $!");
 }
 
 sub finish {
+    my ($exit) = @_;
+    $exit ||= 0;
     utime(time,time,"db");
-    local ($u);
-    while ($u= $cleanups[$#cleanups]) { &$u; }
+    # cleanups are run in an end block now.
+    #my ($u);
+    #while ($u= $cleanups[$#cleanups]) { &$u; }
     unlink("incoming/P$nn") || &quit("unlinking incoming/P$nn: $!");
-    exit $_[0];
+    exit $exit;
 }
 
 &quit("wot no exit");
 
 sub htmllog {
-    local ($whatobj,$whatverb,$where,$desc) = @_;
+    my ($whatobj,$whatverb,$where,$desc) = @_;
     my $hash = get_hashname($ref);
     open(AP,">>db-h/$hash/$ref.log") || &quit("opening db-h/$hash/$ref.log (lh): $!");
     print(AP
           "\6\n".
           "<strong>$whatobj $whatverb</strong>".
-          ($where eq '' ? "" : " to <code>".encode_entities($where)."</code>").
+          ($where eq '' ? "" : " to <code>".html_escape($where)."</code>").
           ":<br>\n". $desc.
           "\n\3\n") || &quit("writing db-h/$hash/$ref.log (lh): $!");
     close(AP) || &quit("closing db-h/$hash/$ref.log (lh): $!");
@@ -1153,7 +943,7 @@ sub sendmessage {
     #save email to the log
     open(AP,">>db-h/$hash/$ref.log") || &quit("opening db-h/$hash/$ref.log (lo): $!");
     print(AP "\2\n",join("\4",@$recips),"\n\5\n",
-          @{escapelog(stripbccs($msg))},"\n\3\n") ||
+          escape_log(stripbccs($msg)),"\n\3\n") ||
         &quit("writing db-h/$hash/$ref.log (lo): $!");
     close(AP) || &quit("closing db-h/$hash/$ref.log (lo): $!");
 
@@ -1168,10 +958,59 @@ sub sendmessage {
                      recipients     => $recips);
 }
 
-my $maintainerschecked = 0;
+=head2 message_body_template
+
+     message_body_template('mail/ack',{ref=>'foo'});
+
+Creates a message body using a template
+
+=cut
+
+sub message_body_template{
+     my ($template,$extra_var) = @_;
+     $extra_var ||={};
+     my $body = fill_template($template,$extra_var);
+     return fill_template('mail/message_body',
+                         {%{$extra_var},
+                          body => $body,
+                         },
+                        );
+}
+
+=head2 fill_template
+
+     fill_template('mail/foo',{foo=>'bar'});
+
+Calls fill_in_template with a default set of variables and any extras
+added in.
+
+=cut
+
+sub fill_template{
+     my ($template,$extra_var) = @_;
+     $extra_var ||={};
+     my $variables = {config => \%config,
+                     defined($ref)?(ref    => $ref):(),
+                     defined($data)?(data  => $data):(),
+                     %{$extra_var},
+                    };
+     my $hole_var = {'&bugurl' =>
+                    sub{"$_[0]: ".
+                             'http://'.$config{cgi_domain}.'/'.
+                                  Debbugs::CGI::bug_url($_[0]);
+                   }
+                   };
+     return fill_in_template(template => $template,
+                            variables => $variables,
+                            hole_var  => $hole_var,
+                           );
+}
+
+
 sub checkmaintainers {
     return if $maintainerschecked++;
     return if !length($data->{package});
+    my %maintainerof;
     open(MAINT,"$gMaintainerFile") || die &quit("maintainers open: $!");
     while (<MAINT>) {
        m/^\n$/ && next;
@@ -1192,6 +1031,7 @@ sub checkmaintainers {
         $maintainerof{$a}= $2;
     }
     close(MAINT);
+    my %pkgsrc;
     open(SOURCES,"$gPackageSource") || &quit("pkgsrc open: $!");
     while (<SOURCES>) {
         next unless m/^(\S+)\s+\S+\s+(\S.*\S)\s*$/;
@@ -1200,8 +1040,8 @@ sub checkmaintainers {
        $pkgsrc{$a} = $b;
     }
     close(SOURCES);
-    $anymaintfound=0; $anymaintnotfound=0;
-    for $p (split(m/[ \t?,():]+/,$data->{package})) {
+    my $anymaintfound=0; my $anymaintnotfound=0;
+    for my $p (split(m/[ \t?,():]+/,$data->{package})) {
         $p =~ y/A-Z/a-z/;
        $p =~ /([a-z0-9.+-]+)/;
        $p = $1;
@@ -1215,7 +1055,7 @@ sub checkmaintainers {
        }
         if (defined($maintainerof{$p})) {
            print DEBUG "maintainer add >$p|$maintainerof{$p}<\n";
-            $addmaint= $maintainerof{$p};
+            my $addmaint= $maintainerof{$p};
             push(@maintaddrs,$addmaint) unless
                 $addmaint eq $replyto || grep($_ eq $addmaint, @maintaddrs);
             $anymaintfound++;
@@ -1227,9 +1067,9 @@ sub checkmaintainers {
         }
     }
 
-    if (length $data->{owner}) {
+    if (defined $data->{owner} and length $data->{owner}) {
         print DEBUG "owner add >$data->{package}|$data->{owner}<\n";
-        $addmaint = $data->{owner};
+        my $addmaint = $data->{owner};
         push(@maintaddrs, $addmaint) unless
             $addmaint eq $replyto or grep($_ eq $addmaint, @maintaddrs);
     }