]> git.donarmstrong.com Git - debbugs.git/commitdiff
ditch using .in naming of scripts
authorDon Armstrong <don@donarmstrong.com>
Sat, 24 May 2008 12:54:57 +0000 (14:54 +0200)
committerDon Armstrong <don@donarmstrong.com>
Sat, 24 May 2008 12:54:57 +0000 (14:54 +0200)
35 files changed:
Makefile
scripts/age-1 [new file with mode: 0755]
scripts/age-1.in [deleted file]
scripts/config [new file with mode: 0644]
scripts/config.in [deleted file]
scripts/db2html [new file with mode: 0755]
scripts/db2html.in [deleted file]
scripts/errorlib [new file with mode: 0755]
scripts/errorlib.in [deleted file]
scripts/expire [new file with mode: 0755]
scripts/expire.in [deleted file]
scripts/gen-indices [new file with mode: 0755]
scripts/gen-indices.in [deleted file]
scripts/html-control [new file with mode: 0755]
scripts/html-control.in [deleted file]
scripts/html-install [new file with mode: 0755]
scripts/html-install.in [deleted file]
scripts/mailsummary [new file with mode: 0755]
scripts/mailsummary.in [deleted file]
scripts/process [new file with mode: 0755]
scripts/process.in [deleted file]
scripts/processall [new file with mode: 0755]
scripts/processall.in [deleted file]
scripts/rebuild [new file with mode: 0755]
scripts/rebuild.in [deleted file]
scripts/receive [new file with mode: 0755]
scripts/receive.in [deleted file]
scripts/service [new file with mode: 0755]
scripts/service.in [deleted file]
scripts/spamscan [new file with mode: 0755]
scripts/spamscan.in [deleted file]
scripts/summary [new file with mode: 0755]
scripts/summary.in [deleted file]
scripts/text [new file with mode: 0644]
scripts/text.in [deleted file]

index 0fdfa0c06755b617617f8c90c02981a400cc87d5..d1185b61bcf9298e4b0c73f89128c52cd2e8a4cf 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -12,7 +12,7 @@ man_dir               := $(DESTDIR)/usr/share/man
 man8_dir       := $(man_dir)/man8
 examples_dir   := $(doc_dir)/examples
 
-scripts_in     := $(filter-out scripts/config.in scripts/errorlib.in scripts/text.in, $(wildcard scripts/*.in))
+scripts_in     := $(filter-out scripts/config scripts/errorlib scripts/text, $(wildcard scripts/*))
 htmls_in       := $(wildcard html/*.html.in)
 cgis           := $(wildcard cgi/*.cgi cgi/*.pl)
 
@@ -48,13 +48,13 @@ $(var_dir)/spool/db-h $(scripts_dir) $(perl_dir) $(examples_dir) $(man8_dir); \
 
 
        # install the scripts
-       $(foreach script,$(scripts_in), $(install_exec) $(script) $(scripts_dir)/$(patsubst scripts/%.in,%,$(script));)
-       $(install_data) scripts/errorlib.in $(scripts_dir)/errorlib
+       $(foreach script,$(scripts_in), $(install_exec) $(script) $(scripts_dir)/$(script);)
+       $(install_data) scripts/errorlib $(scripts_dir)/errorlib
 
        # install examples
-       $(install_data) scripts/config.in $(examples_dir)/config
+       $(install_data) scripts/config $(examples_dir)/config
        $(install_data) scripts/config.debian $(examples_dir)/config.debian
-       $(install_data) scripts/text.in $(examples_dir)/text
+       $(install_data) scripts/text $(examples_dir)/text
        $(install_data) debian/crontab misc/nextnumber misc/Maintainers \
          misc/Maintainers.override misc/pseudo-packages.description \
          misc/sources $(examples_dir)
@@ -67,7 +67,7 @@ $(var_dir)/spool/db-h $(scripts_dir) $(perl_dir) $(examples_dir) $(man8_dir); \
 
        # install the CGIs
        for cgi in $(cgis); do $(install_exec) $$cgi $(var_dir)/www/cgi; done
-       $(install_exec) cgi/bugs-fetch2.pl.in $(var_dir)/www/cgi/bugs-fetch2.pl
+       $(install_exec) cgi/bugs-fetch2.pl $(var_dir)/www/cgi/bugs-fetch2.pl
 
 #      # install Perl modules
 #      for perl in $(perls); do $(install_data) $$perl $(perl_dir); done
diff --git a/scripts/age-1 b/scripts/age-1
new file mode 100755 (executable)
index 0000000..cc2e72d
--- /dev/null
@@ -0,0 +1,8 @@
+#!/bin/sh
+# $Id: age-1.in,v 1.3 2002/01/06 10:46:24 ajt Exp $
+set -e
+cd /var/lib/debbugs/spool/db-h
+test -f ./-3.log && rm ./-3.log
+test -f ./-2.log && mv ./-2.log ./-3.log
+test -f ./-1.log && mv ./-1.log ./-2.log
+#rm -f ../stamp.html
diff --git a/scripts/age-1.in b/scripts/age-1.in
deleted file mode 100755 (executable)
index cc2e72d..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/sh
-# $Id: age-1.in,v 1.3 2002/01/06 10:46:24 ajt Exp $
-set -e
-cd /var/lib/debbugs/spool/db-h
-test -f ./-3.log && rm ./-3.log
-test -f ./-2.log && mv ./-2.log ./-3.log
-test -f ./-1.log && mv ./-1.log ./-2.log
-#rm -f ../stamp.html
diff --git a/scripts/config b/scripts/config
new file mode 100644 (file)
index 0000000..4767f6a
--- /dev/null
@@ -0,0 +1,83 @@
+# -*- mode: cperl -*-
+# This is the template debbugs configuration file.
+# You *must* edit it in order for debbugs to work.
+# $Id: config.in,v 1.23 2005/07/17 19:07:01 cjwatson Exp $
+
+# Domains
+$gEmailDomain = "bugs.something";                      # e.g. bugs.debian.org
+$gListDomain = "lists.something";                      # e.g. lists.debian.org
+$gWebHost = "localhost";                               # e.g. www.debian.org
+$gWebHostBugDir = "Bugs";                              # e.g. Bugs
+# For now, don't change this one manually!
+$gWebDomain = "$gWebHost/$gWebHostBugDir";
+$gHTMLSuffix = ".html";
+$gCGIDomain = "$gWebDomain/cgi";                       # e.g. cgi.debian.org
+$gMirrors = "";                                                # comma separated list
+$gPackagePages = "packages.debian.org";                 # e.g. packages.debian.org
+$gSubscriptionDomain = "packages.something";           # e.g. packages.qa.debian.org
+
+# Project identification
+$gProject = "Something";                               # e.g. Debian
+$gProjectTitle = "Something DebBugs Test";             # e.g. Debian GNU/Linux
+# Person(s) responsible for this installation
+$gMaintainer = "Local DebBugs Owner";                  # e.g. Ian Jackson
+$gMaintainerWebpage = "http://localhost/~owner";       # e.g. http://www.debian.org/~iwj
+$gMaintainerEmail = "root\@something";                 # e.g. owner@bugs.debian.org
+$gUnknownMaintainerEmail = "$gMaintainerEmail";                # e.g. unknown-package@qa.debian.org
+
+# BTS mailing lists, at $gListDomain
+# if you don't want lists, set them all to $gMaintainerEmail
+# if you don't want that mail at all, filter it out somehow :)
+$gSubmitList = "bug-submit-list";              # e.g. debian-bugs-dist
+$gMaintList = "bug-maint-list";                        # e.g. debian-bugs-dist
+$gQuietList = "bug-quiet-list";                        # e.g. debian-bugs-dist
+$gForwardList = "bug-forward-list";            # e.g. debian-bugs-forwarded
+$gDoneList = "bug-done-list";                  # e.g. debian-bugs-closed
+$gRequestList = "bug-request-list";            # e.g. debian-bugs-dist
+$gSubmitterList = "bug-submitter-list";                # e.g. debian-bugs-dist
+$gControlList = "bug-control-list";            # e.g. debian-bugs-dist
+$gSummaryList = "bug-summary-list";            # e.g. debian-bugs-reports
+$gMirrorList = "bug-mirrors-list";             # sends to all mirrors
+
+# Various configurable options
+$gMailer = "exim";                             # valid: exim, qmail and sendmail
+$gBug = "bug";                                 # how to spell `bug'
+$gBugs = "bugs";                               # how to spell `bugs'
+$gRemoveAge = 28;                              # days after closed bugs are cleaned out,
+                                               # 0 disables
+$gSaveOldBugs = 1;                             # whether to archive such bugs
+$gDefaultSeverity = "normal";
+$gShowSeverities = "critical, grave, normal, minor, wishlist";
+@gStrongSeverities = ( 'critical', 'grave' );
+@gSeverityList = ( 'critical', 'grave', 'normal', 'wishlist' );
+%gSeverityDisplay = ( 'critical', "Critical $gBugs",
+                      'grave', "Grave $gBugs",
+                      'normal', "Normal $gBugs",
+                      'wishlist', "Wishlist items" );
+@gTags = ( 'patch', 'wontfix', 'moreinfo', 'unreproducible', 'fixed', 'stable' );
+
+# better don't change this
+$gBounceFroms = "^mailer|^da?emon|^post.*mast|^root|^wpuser|^mmdf|^smt.*|^mrgate|^vmmail|^mail.*system|^uucp|-maiser-|^mal\@|^mail.*agent|^tcpmail|^bitmail|^mailman";
+
+# Directories -- do _not_ change their locations.
+# They are currently hardcoded, variables are here for future expansion.
+$gConfigDir = "/etc/debbugs";                          # directory where this file is
+$gSpoolDir = "/var/lib/debbugs/spool";                 # working directory
+$gIncomingDir = "incoming";                            # unprocessed e-mails
+$gWebDir = "/var/lib/debbugs/www";                     # base location of web pages
+$gDocDir = "/var/lib/debbugs/www/txt";                 # location of text doc files
+
+# Required data files
+$gMaintainerFile = "$gConfigDir/Maintainers";
+$gMaintainerFileOverride = "$gConfigDir/Maintainers.override";
+$gPseudoDescFile = "$gConfigDir/pseudo-packages.description";
+$gPackageSource = "$gConfigDir/indices/sources";
+
+
+# Estraier Configuration
+%gSearchEstraier = (url  => 'http://localhost:1978/node/bts1',
+                   user => 'user',
+                   pass => 'pass',
+                  );
+
+1;
diff --git a/scripts/config.in b/scripts/config.in
deleted file mode 100644 (file)
index 4767f6a..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-# -*- mode: cperl -*-
-# This is the template debbugs configuration file.
-# You *must* edit it in order for debbugs to work.
-# $Id: config.in,v 1.23 2005/07/17 19:07:01 cjwatson Exp $
-
-# Domains
-$gEmailDomain = "bugs.something";                      # e.g. bugs.debian.org
-$gListDomain = "lists.something";                      # e.g. lists.debian.org
-$gWebHost = "localhost";                               # e.g. www.debian.org
-$gWebHostBugDir = "Bugs";                              # e.g. Bugs
-# For now, don't change this one manually!
-$gWebDomain = "$gWebHost/$gWebHostBugDir";
-$gHTMLSuffix = ".html";
-$gCGIDomain = "$gWebDomain/cgi";                       # e.g. cgi.debian.org
-$gMirrors = "";                                                # comma separated list
-$gPackagePages = "packages.debian.org";                 # e.g. packages.debian.org
-$gSubscriptionDomain = "packages.something";           # e.g. packages.qa.debian.org
-
-# Project identification
-$gProject = "Something";                               # e.g. Debian
-$gProjectTitle = "Something DebBugs Test";             # e.g. Debian GNU/Linux
-# Person(s) responsible for this installation
-$gMaintainer = "Local DebBugs Owner";                  # e.g. Ian Jackson
-$gMaintainerWebpage = "http://localhost/~owner";       # e.g. http://www.debian.org/~iwj
-$gMaintainerEmail = "root\@something";                 # e.g. owner@bugs.debian.org
-$gUnknownMaintainerEmail = "$gMaintainerEmail";                # e.g. unknown-package@qa.debian.org
-
-# BTS mailing lists, at $gListDomain
-# if you don't want lists, set them all to $gMaintainerEmail
-# if you don't want that mail at all, filter it out somehow :)
-$gSubmitList = "bug-submit-list";              # e.g. debian-bugs-dist
-$gMaintList = "bug-maint-list";                        # e.g. debian-bugs-dist
-$gQuietList = "bug-quiet-list";                        # e.g. debian-bugs-dist
-$gForwardList = "bug-forward-list";            # e.g. debian-bugs-forwarded
-$gDoneList = "bug-done-list";                  # e.g. debian-bugs-closed
-$gRequestList = "bug-request-list";            # e.g. debian-bugs-dist
-$gSubmitterList = "bug-submitter-list";                # e.g. debian-bugs-dist
-$gControlList = "bug-control-list";            # e.g. debian-bugs-dist
-$gSummaryList = "bug-summary-list";            # e.g. debian-bugs-reports
-$gMirrorList = "bug-mirrors-list";             # sends to all mirrors
-
-# Various configurable options
-$gMailer = "exim";                             # valid: exim, qmail and sendmail
-$gBug = "bug";                                 # how to spell `bug'
-$gBugs = "bugs";                               # how to spell `bugs'
-$gRemoveAge = 28;                              # days after closed bugs are cleaned out,
-                                               # 0 disables
-$gSaveOldBugs = 1;                             # whether to archive such bugs
-$gDefaultSeverity = "normal";
-$gShowSeverities = "critical, grave, normal, minor, wishlist";
-@gStrongSeverities = ( 'critical', 'grave' );
-@gSeverityList = ( 'critical', 'grave', 'normal', 'wishlist' );
-%gSeverityDisplay = ( 'critical', "Critical $gBugs",
-                      'grave', "Grave $gBugs",
-                      'normal', "Normal $gBugs",
-                      'wishlist', "Wishlist items" );
-@gTags = ( 'patch', 'wontfix', 'moreinfo', 'unreproducible', 'fixed', 'stable' );
-
-# better don't change this
-$gBounceFroms = "^mailer|^da?emon|^post.*mast|^root|^wpuser|^mmdf|^smt.*|^mrgate|^vmmail|^mail.*system|^uucp|-maiser-|^mal\@|^mail.*agent|^tcpmail|^bitmail|^mailman";
-
-# Directories -- do _not_ change their locations.
-# They are currently hardcoded, variables are here for future expansion.
-$gConfigDir = "/etc/debbugs";                          # directory where this file is
-$gSpoolDir = "/var/lib/debbugs/spool";                 # working directory
-$gIncomingDir = "incoming";                            # unprocessed e-mails
-$gWebDir = "/var/lib/debbugs/www";                     # base location of web pages
-$gDocDir = "/var/lib/debbugs/www/txt";                 # location of text doc files
-
-# Required data files
-$gMaintainerFile = "$gConfigDir/Maintainers";
-$gMaintainerFileOverride = "$gConfigDir/Maintainers.override";
-$gPseudoDescFile = "$gConfigDir/pseudo-packages.description";
-$gPackageSource = "$gConfigDir/indices/sources";
-
-
-# Estraier Configuration
-%gSearchEstraier = (url  => 'http://localhost:1978/node/bts1',
-                   user => 'user',
-                   pass => 'pass',
-                  );
-
-1;
diff --git a/scripts/db2html b/scripts/db2html
new file mode 100755 (executable)
index 0000000..b45d2b9
--- /dev/null
@@ -0,0 +1,653 @@
+#!/usr/bin/perl
+# $Id: db2html.in,v 1.22 2004/04/19 10:03:53 cjwatson Exp $
+# usage: db2html [-diff] [-stampfile=<stampfile>] [-lastrun=<days>] <wwwbase>
+
+#load the necessary libraries/configuration
+$config_path = '/etc/debbugs';
+$lib_path = '/usr/lib/debbugs';
+
+require("$config_path/config");
+require("$config_path/text");
+require("$lib_path/errorlib");
+$ENV{'PATH'} = $lib_path.':'.$ENV{'PATH'};
+
+use POSIX qw(strftime tzset);
+$ENV{"TZ"} = 'UTC';
+tzset();
+
+#set current working directory
+chdir("$gSpoolDir") || die "chdir spool: $!\n";
+
+#setup variables
+$diff = 0;
+$stampfile = 'stamp.html';
+$tail_html = $gHTMLTail; 
+$expirynote_html = '';
+$expirynote_html = $gHTMLExpireNote if $gRemoveAge;
+$shorthead = ' Ref   * Package    Keywords/Subject                    Submitter';
+$shortindex = ''; 
+$amonths = -1;
+$indexunmatched = '';
+%displayshowpendings = ('pending','outstanding',
+                       'done','resolved',
+                       'forwarded','forwarded to upstream software authors');
+
+#set timestamp for html files
+$dtime = strftime "%a, %e %b %Y %T UTC", localtime;
+$tail_html =~ s/SUBSTITUTE_DTIME/$dtime/;
+
+#check for commandline switches
+while (@ARGV && $ARGV[0] =~ m/^-/) 
+{      if ($ARGV[0] eq '-diff') { $diff=1; }
+    elsif ($ARGV[0] =~ m/^-lastrun\=([0-9.]+)$/) { $lastrun= $1; undef $stampfile; }
+    elsif ($ARGV[0] =~ m/^-full$/) { undef $lastrun; undef $stampfile; }
+    elsif ($ARGV[0] =~ m/^-stampfile\=(\S+)$/) { $stampfile= $1; }
+    else { &quit("bad usage"); }
+    shift;
+}
+
+#check for remaing argument, only one...
+@ARGV==1 or die;
+$wwwbase= shift(@ARGV);
+
+#get starting time
+defined($startdate= time) || &quit("failed to get time: $!");
+
+$|=1;
+
+#if stamp file was given, 
+if (defined($stampfile)) 
+{      if (open(X,"< $stampfile")) 
+       {       $lastrun= -M X;
+        close(X);
+        printf "progress last run %.7f days\n",$lastrun;
+    } else { print "progress stamp file $stampfile: $! - full\n"; }
+}
+
+#only process file if greater than last run...
+if (defined($lastrun) && -M "db-h" > $lastrun) 
+{      $_= $gHTMLStamp;
+    s/SUBSTITUTE_DTIME/$dtime/o;
+    s/\<\!\-\-updateupdate\-\-\>.*\<\!\-\-\/updateupdate\-\-\>/check/;
+    &file('ix/zstamp.html','non',$_."</body></html>\n");
+       print "noremoves";
+#    print "db2html: no changes since last run\n";
+    exit 0;
+}
+
+#parse maintainer file
+open(MM,"$gMaintainerFile") || &quit("open $gMaintainerFile: $!");
+while(<MM>) 
+{      m/^(\S+)\s+(\S.*\S)\s*$/ || &quit("$gMaintainerFile: \`$_'");
+    ($a,$b)=($1,$2);
+    $a =~ y/A-Z/a-z/;
+    $maintainer{$a}= $b;
+}
+close(MM);
+
+#load all database files
+opendir(D,'db-h') || &quit("opendir db-h: $!");
+@dirs = grep(s#^#db-h/#,grep(/^\d+$/,readdir(D)));
+closedir(D);
+foreach my $dir (@dirs) {
+    opendir(D,$dir);
+    push @files, grep(/^-?\d+\.log$/,readdir(D));
+    closedir(D);
+}
+@files = sort { $a <=> $b } @files;
+
+for $pending (qw(pending done forwarded)) 
+{      for $severity (@showseverities) 
+       {       eval "\$index${pending}${severity}= \$iiindex${pending}${severity}= ''; 1;"
+            or &quit("reset \$index${pending}${severity}: $@");
+    }
+}
+
+for $f (@files) 
+{      next unless $f =~ m/^(-?\d+)\.log$/;
+    $ref= $1;
+       #((print STDERR "$ref\n"),
+       #next
+       #)
+       # unless $ref =~ m/^-/ || $ref =~ m/^124/;
+    &filelock("lock/$ref");
+    $preserveonly= defined($lastrun) && -M "db-h/".get_hashname($ref)."/$ref.log" > $lastrun;
+    if ($ref =~ m/^-\d$/) 
+       {       $week= $ref eq '-1' ? 'this week' :
+               $ref eq '-2' ? 'last week' :
+               $ref eq '-3' ? 'two weeks ago' :
+                              ($ref-1)." weeks ago";
+        $linkto= "ju/unmatched$ref";
+        $short= "junk, $week";
+        $descriptivehead=
+            "This includes messages sent to <code>done\@$gEmailDomain</code>\n".
+            "which did not have a $gBug reference number in the Subject line\n".
+            "or which contained an\n".
+            "unknown or out of date $gBug report number (these cause a warning\n".
+            "to be sent to the sender) and details about the messages\n".
+            "sent to <code>request@$gEmailDomain</code> (all of which".
+            "produce replies).\n";
+        $indexlink= "Messages not matched to a specific $gBug report - $week";
+        $data->{subject}= '';
+        $indexentry= '';
+        undef $tpack;
+        undef $tmaint;
+        undef $iiref;
+        $tpackfile= "pnone.html";
+        $indexpart= 'unmatched';
+    } else 
+       {
+       $data=readbug($ref);
+        $_= $data->{package}; y/A-Z/a-z/; $_= $` if m/[^-+._a-z0-9()]/;
+        $tpack= $_;
+        if ($data->{severity} eq '' || $data->{severity} eq 'normal') 
+               {       $showseverity= '';
+            $addseverity= $gDefaultSeverity;
+        } elsif (isstrongseverity($data->{severity})) 
+               {       $showseverity= "<strong>Severity: $data->{severity}</strong>;\n";
+            $addseverity= $data->{severity};
+        } else 
+               {       $showseverity= "Severity: <em>$data->{severity}</em>;\n";
+            $addseverity= $data->{severity};
+        }
+        $days= int(($startdate - $data->{date})/86400); close(S);
+        $indexlink= "#$ref: ".&sani($data->{subject});
+        $indexentry= '';
+        $packfile= length($tpack) ? "pa/l$tpack.html" : "pa/none.html";
+        $indexentry .= "Package: <A href=\"../$packfile\"><strong>".
+                        &sani($data->{package})."</strong></A>;\n"
+            if length($data->{package});
+        $indexentry .= $showseverity;
+        $indexentry .= "Reported by: ".&sani($data->{originator});
+        $indexentry .= ";\nOwned by: ".&sani($data->{owner})
+            if length($data->{owner});
+        $indexentry .= ";\nKeywords: ".&sani($data->{keywords})
+            if length($data->{keywords});
+        $linkto= $ref; $linkto =~ s,^..,$&/$&,;
+        @merged= split(/ /,$data->{mergedwith});
+        if (@merged) 
+               {       $mseparator= ";\nmerged with ";
+            for $m (@merged) 
+                       {       $mfile= $m; $mfile =~ s,^..,$&/$&,;
+                $indexentry .= $mseparator."<A href=\"../$mfile.html\">#$m</A>";
+                $mseparator= ",\n";
+            }
+        }
+        $daysold=$submitted='';
+        if (length($data->{done})) 
+               {       $indexentry .= ";\n<strong>Done:</strong> ".&sani($data->{done});
+            $indexpart= "done$addseverity";
+        } elsif (length($data->{forwarded})) 
+               {       $indexentry .= ";\n<strong>Forwarded</strong> to ".&sani($data->{forwarded});
+            $indexpart= "forwarded$addseverity";
+        } else 
+               {       $cmonths= int($days/30);
+            if ($cmonths != $amonths) 
+                       {       $msg= $cmonths == 0 ? "Submitted in the last month" :
+                       $cmonths == 1 ? "Over one month old" :
+                       $cmonths == 2 ? "Over two months old - attention is required" :
+                       "OVER $cmonths MONTHS OLD - ATTENTION IS REQUIRED";
+                $shortindex .= "</pre><h2>$msg:</h2><pre>\n$shorthead\n";
+                $amonths= $cmonths;
+            }
+            $pad= 6-length(sprintf("%d",$f));
+            $thissient=
+                ($pad>0 ? ' 'x$pad : '').
+                sprintf("<A href=\"../%s.html\">%d</A>",$linkto,$ref).
+                &sani(sprintf(" %-1.1s %-10.10s %-35.35s %-.25s\n",
+                                               $data->{severity},
+                        $data->{package},
+                        (length($data->{keywords}) ? $data->{keywords}.'/' : '').
+                        $data->{subject}, $data->{originator}));
+            $shortindex.= $thissient;
+            $sient{"$ref $data->{package}"}= $thissient;
+            if ($days >= 7) 
+                       {       $font= $days <= 30 ? '' :
+                       $days <= 60 ? 'em' :
+                    'strong';
+                $efont= length($font) ? "</$font>" : '';
+                $font= length($font) ? "<$font>" : '';
+                $daysold= "; $font$days days old$efont";
+            }
+            if ($preserveonly) {
+                $submitted = 'THIS IS A BUG IN THE BUG PROCESSOR';
+            } else {
+                $submitted = strftime "%a, %e %b %Y %T %Z", localtime($data->{date});
+            }
+            $submitted= "; dated $submitted";
+            $indexpart= "pending$addseverity";
+        }
+        $iiref= $ref;
+        $short= $ref; $short =~ s/^\d+/#$&/;
+        $tmaint= defined($maintainer{$tpack}) ? $maintainer{$tpack} : '(unknown)';
+        $qpackage= &sani($_);
+        $descriptivehead= $indexentry.$submitted.";\nMaintainer for $qpackage is\n".
+            '<A href="../ma/l'.&maintencoded($tmaint).'.html">'.&sani($tmaint).'</A>.';
+        $indexentry .= $daysold;
+        $indexentry .= ".";
+    }
+    $indexadd='';
+    $indexadd .= "<!--iid $iiref-->" if defined($iiref);
+    $indexadd .= "<li><A href=\"../$linkto.html\">".$indexlink."</A>";
+    $indexadd .=  "<br>\n".$indexentry if length($indexentry);
+    $indexadd .= "<!--/iid-->" if defined($iiref);
+    $indexadd .= "\n";
+    $estr= "\$index$indexpart = \$indexadd.\$index$indexpart; 1;";
+    eval($estr) || &quit("eval add to \$index$indexpart ($estr) failed: $@");
+       #print STDERR ">$estr|$indexadd<\n";
+    $indexadd= "<!--ii $iiref-->\n" if defined($iiref);
+    eval("\$iiindex$indexpart = \$indexadd.\$iiindex$indexpart; 1;") ||
+        &quit("eval add to \$iiindex$indexpart failed: $@");
+    if (defined($tmaint)) 
+       {       $countpermaint{$tmaint} += length($data->{done}) ? 0 : length($data->{forwarded}) ? 0 : 1;
+        eval("\$permaint${indexpart}{\$tmaint} .= \$indexadd; 1;") ||
+            &quit("eval add to \$permaint${indexpart}{\$tmaint} failed: $@");
+    }
+    if (defined($tpack)) 
+       {       $countperpack{$tpack} += length($data->{done}) ? 0 : length($data->{forwarded}) ? 0 : 1;
+        eval("\$perpack${indexpart}{\$tpack} .= \$indexadd; 1;") ||
+            &quit("eval add to \$perpack${indexpart}{\$tpack} failed: $@");
+    }
+    if ($preserveonly) { &preserve("$linkto.html"); &preserve("$linkto-b.html"); &unfilelock; next; }
+    my $hash = get_hashname($ref);
+    open(L,"db-h/$hash/$ref.log") || &quit("open db-h/$hash/$ref.log: $!");
+    $log='';
+    $boring=''; $xmessage= 0;
+    $normstate= 'kill-init';
+    $suppressnext= 0;
+    while(<L>) {
+        if (m/^\07$/) {
+            $normstate eq 'kill-init' || $normstate eq 'kill-end' ||
+                &quit("$ref ^G in state $normstate");
+            $normstate= 'incoming-recv';
+        } elsif (m/^\01$/) {
+            $normstate eq 'kill-init' || $normstate eq 'kill-end' ||
+                &quit("$ref ^A in state $normstate");
+            $normstate= 'autocheck';
+        } elsif (m/^\02$/) {
+            $normstate eq 'kill-init' || $normstate eq 'kill-end' ||
+                &quit("$ref ^B in state $normstate");
+            $normstate= 'recips';
+        } elsif (m/^\03$/) {
+            $normstate eq 'go' || $normstate eq 'go-nox' || $normstate eq 'html' ||
+                &quit("$ref ^C in state $normstate");
+            $this .= "</pre>\n" if $normstate eq 'go' || $normstate eq 'go-nox';
+            if ($normstate eq 'html') {
+                $xmessage++;
+                $this .= "  <em><A href=\"../$linkto-b.html#m$xmessage\">Full text</A>".
+                         " available.</em>";
+            }
+            if ($suppressnext && $normstate ne 'html') {
+                $ntis= $this; $ntis =~ s:\<pre\>:</A><pre>:i;
+                $boring .= "<hr><A name=\"m$xmessage\">\n$ntis\n";
+            } else {
+                $log = $this. "<hr>\n". $log;
+            }
+            $suppressnext= $normstate eq 'html';
+            $normstate= 'kill-end';
+        } elsif (m/^\05$/) {
+            $normstate eq 'kill-body' || &quit("^E in state $normstate");
+            $this .= "<pre>\n";
+            $normstate= 'go';
+        } elsif (m/^\06$/) {
+            $normstate eq 'kill-init' || $normstate eq 'kill-end' ||
+                &quit("$ref ^F in state $normstate");
+            $normstate= 'html'; $this= '';
+        } elsif ($normstate eq 'incoming-recv') {
+            $pl= $_; $pl =~ s/\n+$//;
+            m/^Received: \(at (\S+)\) by (\S+)\;/ ||
+                &quit("bad line \`$pl' in state incoming-recv");
+            $this = "<h2>Message received at ".&sani("$1\@$2").":</h2><br>\n".
+                    "<pre>\n".
+                    "$_";
+            $normstate= 'go';
+        } elsif ($normstate eq 'html') {
+            $this .= $_;
+        } elsif ($normstate eq 'go') {
+            s/^\030//;
+            $this .= &sani($_);
+        } elsif ($normstate eq 'go-nox') {
+            next if !s/^X//;
+            $this .= &sani($_);
+        } elsif ($normstate eq 'recips') {
+            if (m/^-t$/) {
+                $this = "<h2>Message sent:</h2><br>\n";
+            } else {
+                s/\04/, /g; s/\n$//;
+                $this = "<h2>Message sent to ".&sani($_).":</h2><br>\n";
+            }
+            $normstate= 'kill-body';
+        } elsif ($normstate eq 'autocheck') {
+            next if !m/^X-Debian-Bugs(-\w+)?: This is an autoforward from (\S+)/;
+            $normstate= 'autowait';
+            $this = "<h2>Message received at $2:</h2><br>\n";
+        } elsif ($normstate eq 'autowait') {
+            next if !m/^$/;
+            $normstate= 'go-nox';
+            $this .= "<pre>\n";
+        } else {
+            &quit("$ref state $normstate line \`$_'");
+        }
+    }
+    &quit("$ref state $normstate at end") unless $normstate eq 'kill-end';
+    close(L);
+    if (length($boring)) {
+        &file("$linkto-b.html",'non',
+              "<html><head><title>$gProject $gBug report logs - ".
+              "$short, boring messages</title>\n".
+              "<link rev=\"made\" href=\"mailto:$gMaintainerEmail)\">\n".
+              "</head>$gHTMLStart<h1>$gProject $gBugreport logs -".
+              "\n <A href=\"../$linkto.html\">$short</A>,".
+              " boring messages</h1>\n$boring\n<hr>\n".
+              $tail_html."</body></html>\n");
+    }
+    &file("$linkto.html",'non',
+          "<html><head><title>$gProject $gBug report logs - ".
+          "$short</title>\n".
+          "<link rev=\"made\" href=\"mailto:$gMaintainerEmail\">\n".
+          "</head>$gHTMLStart<h1>$gProject $gBug report logs -  $short<br>\n".
+          &sani($data->{subject})."</h1>".
+          "$descriptivehead\n".
+          "\n<hr>\n".
+          $log.
+          $tail_html."</body></html>\n");
+    &unfilelock;
+}
+
+sub maintsort {
+    $_= $_[0];
+    s/([^<>()]+) \(([^()<>]+)\)/$2 \<$1\>/;
+    
+    s/\s+/ /g;
+    s/^\s*//;
+    $email= s/ *\<[^<>()]+\>$//g ? $& : '';
+    $_= "$1 $_" if s/ (\S+)$//;
+    $_.= $email;
+    $_;
+}
+
+sub maintencoded {
+    return $maintencoded{$_[0]} if defined($maintencoded{$_[0]});
+    local ($input)= @_;
+    local ($todo,$encoded)= ($input);
+    while ($todo =~ m/\W/) {
+        $encoded.=$`.sprintf("-%02x_",unpack("C",$&));
+        $todo= $';
+    }
+    $encoded.= $todo;
+    $encoded =~ s/-2e_/\./g;
+    $encoded =~ s/^([^,]+)-20_-3c_(.*)-40_(.*)-3e_/$1,$2,$3,/;
+    $encoded =~ s/^(.*)-40_(.*)-20_-28_([^,]+)-29_$/,$1,$2,$3/;
+    $encoded =~ s/-20_/_/g;
+    $encoded =~ s/-([^_]+)_-/-$1/g;
+    $maintencoded{$input}= $encoded;
+}
+
+for $tmaint (keys %countpermaint) {
+    $_= $tmaint;
+    $after=$before=$sort2d=$sort2s=$sort1d=$sort1s='';
+    $after= "$&$after" if s/\s*\<[^<>()]+\>\s*$//;
+    $after= "$&$after" if s/\s*\)\s*$//;
+    $after= "$&$after" if s/\s*,.*$//;
+    $before.= $& if s/^.*\(\s*//;
+    $sort2d= $& if s/\S+$//;
+    $sort1d= $_;
+    while (s/^([^()<>]+)\. */$1 /) { };
+    s/\s+$//; y/A-Za-z/a-zA-Z/; $sort1s= $_;
+    $sort2s= $sort2d; $sort2s =~ y/A-Za-z/a-zA-Z/;
+    $maintsort{$tmaint}= $sort2s.' '.$sort1s.' '.$before.$sort1d.$sort2d.$after;
+    $maintdisplay{$tmaint}=
+        &sani($before).'<strong>'.&sani($sort1d.$sort2d).'</strong>'.&sani($after);
+}
+
+sub heading ($$) {
+    my ($pt,$sv) = @_;
+    return $displayshowseverities{$sv}.' - '.$displayshowpendings{$pt};
+}
+
+sub makeindex ($$$) {
+    my ($varprefix,$varsuffix,$tkey) = @_;
+    my ($pending,$severity,$anydone,$text);
+    $anydone= 0;
+    $text= '';
+    for $pending (qw(pending forwarded done)) {
+        for $severity (@showseverities) {
+            $estr= "\$value= \\${varprefix}${pending}${severity}${varsuffix}; 1;";
+#print STDERR $estr;
+            eval $estr
+                or &quit("eval get \$${varprefix}${pending}${severity} failed: $@");
+#print STDERR ">$$value<\n";
+            next unless length($$value);
+            $text.= "<hr>\n<h2>".&heading($pending,$severity).":</h2>\n".
+                    "(List of <A href=\"../si/$pending$severity.html\">all".
+                    " such $gBugs</A> is available.)\n<ul>\n".
+                    $$value.
+                    "</ul>\n";
+            $anydone=1 if $pending eq 'done';
+        }
+    }
+    $text.= $expirynote_html if $anydone;
+    return $text;
+}        
+
+&file("ix/full.html",'def',
+      $gFullIndex.
+      makeindex('$index',"",'').
+      "<hr>\n".
+      $tail_html."</body><html>\n");
+
+&file("ju/junk.html",'non',
+      $gJunkIndex.
+      "<hr>\n<h2>Junk (messages without a specific $gBug report number):</h2>\n".
+      "(\`this week' is everything since last Wednesday.)\n<ul>\n".
+      $indexunmatched.
+      "</ul><hr>\n".
+      $tail_html."</body><html>\n");
+
+$nobugs_html= "No reports are currently in this state.";
+$who_html= $gProject;
+$owner_addr= $gMaintainerEmail;
+$otherindex_html= "For other kinds of index or for other information about
+$gProject and the $gBug system, see the <A HREF=\"../../\">$gBug system top-level
+contents WWW page</A>.
+
+";
+
+for $pending (qw(pending forwarded done)) {
+    for $severity (@showseverities) {
+        eval "\$value= \\\$iiindex${pending}${severity}; 1;"
+            or &quit("eval get \$iiindex${pendingtype}${severity} failed: $@");
+        $value= \$nobugs_html if !length($$value);
+        $headstring= &heading($pending,$severity);
+        &file("si/$pending$severity.html",'ref',
+              "<html><head><title>$who_html $gBug reports: $headstring</title>\n".
+              "<link rev=\"made\" href=\"mailto:".&sani($owner_addr)."\">\n".
+              "</head>$gHTMLStart<h1>$who_html $gBug reports: $headstring</h1>\n".
+              $otherindex_html.
+              ($pending eq 'done' ? "<P>\n$expirynote_html" : '').
+              "<hr>\n<ul>\n".
+              $$value.
+              "</ul>\n<hr>\n".
+              $tail_html."</body></html>\n");
+    }
+}
+
+sub individualindexes ($\@&\%&&$$$$$&&) {
+    my ($filename,$keysref,$getfilenameref,$countref,$getdisplayref,
+        $getsimpledisplayref,$what,$caveat,$whatplural,$abbrev,$ihead,
+        $getxinforef,$getxindexref) = @_;
+    my ($itext,$i,$tkey,$sani,$count,$tfilename,$refto,$backnext,$xitext,$bugbugs);
+    $itext='';
+    for ($i=0; $i<=$#$keysref; $i++) {
+        $tkey= $$keysref[$i];
+        $tfilename= &$getfilenameref($tkey);
+        $sani= &$getsimpledisplayref($tkey);
+        $count= $$countref{$tkey};
+        $count= $count >= 1 ? "$count" : "no";
+        $bugbugs= $count == 1 ? "$gBug" : "$gBugs";
+        $xitext= &$getxindexref($tkey);
+        $xitext= length($xitext) ? "$count $bugbugs; $xitext"
+                                 : "$count outstanding $bugbugs";
+        $itext .= "<li><A href=\"../$tfilename\">".&$getdisplayref($tkey)."</A>"."\n".
+                  "  ($xitext)\n";
+        $backnext= '';
+        if ($i>0) {
+            $refto= $$keysref[$i-1];
+            $xitext= &$getxindexref($refto);
+            $xitext= " ($xitext)" if length($xitext);
+            $backnext .= "<br>\nPrevious $what in list, <A href=\"../".
+                         &$getfilenameref($refto)."\">".&$getdisplayref($refto)."</A>".
+                         "$xitext\n";
+        }
+        if ($i<$#$keysref) {
+            $refto= $$keysref[$i+1];
+            $xitext= &$getxindexref($refto);
+            $xitext= " ($xitext)" if length($xitext);
+            $backnext .= "<br>\nNext $what in list, <A href=\"../".
+                         &$getfilenameref($refto)."\">".&$getdisplayref($refto)."</A>".
+                         "$xitext\n";
+        }
+        &file($tfilename,'ref',
+              "<html><head><title>$gProject $gBug reports: $what $sani</title>\n".
+              "<link rev=\"made\" href=\"mailto:$gMaintainerEmail\">\n".
+              "</head>$gHTMLStart<h1>$gProject $gBug reports: $what $sani</h1>\n".
+              &$getxinforef($tkey).
+              $caveat.
+              "See the <A href=\"../$filename\">listing of $whatplural</A>.\n".
+              $backnext.
+              &makeindex("\$per${abbrev}","{\$tkey}",$tkey).
+              "<hr>\n".
+              $tail_html."</body></html>\n");
+    }
+    &file($filename,'non',
+          $ihead.
+          "<hr><ul>\n".
+          $itext.
+          "</ul><hr>\n".
+          $tail_html."</body></html>\n");
+}
+
+@maintainers= sort { $maintsort{$a} cmp $maintsort{$b}; } keys %countpermaint;
+individualindexes('ix/maintainers.html',
+                  @maintainers,
+                  sub { 'ma/l'.&maintencoded($_[0]).'.html'; },
+                  %countpermaint,
+                  sub { $maintdisplay{$_[0]}; },
+                  sub { &sani($_[0]); },
+                  'maintainer',
+                  "Note that there may be other reports filed under different
+                                 variations on the maintainer\'s name and email address.<P>",
+                  'maintainers',
+                  'maint',
+                  $gMaintIndex,
+                  sub { return ''; },
+                  sub { return ''; });
+
+@packages= sort keys %countperpack;
+individualindexes('ix/packages.html',
+                  @packages,
+                  sub { length($_[0]) ? "pa/l$_[0].html" : 'pa/none.html'; },
+                  %countperpack,
+                  sub { length($_[0]) ? $_[0] : 'not specified'; },
+                  sub { &sani(length($_[0]) ? $_[0] : 'not specified'); },
+                  'package',
+                  "Note that with multi-binary packages there may be other
+                                 reports filed under the different binary package names.<P>",
+                  'packages',
+                  'pack',
+                  $gPackageIndex,
+                  sub {
+                      return unless defined($maintainer{$_[0]});
+                      $tmaint= $maintainer{$_[0]};
+                      return "Maintainer for $_[0] is <A href=\"../ma/l".
+                             &maintencoded($tmaint).
+                             ".html\">".&sani($tmaint)."</A>.\n<p>\n";
+                  },
+                  sub {
+                      return unless defined($maintainer{$_[0]});
+                      $tmaint= $maintainer{$_[0]};
+                      return "<A href=\"../ma/l".
+                             &maintencoded($tmaint).
+                             ".html\">".&sani($tmaint)."</A>";
+                  });
+
+&file('ix/summary.html','non',
+      $gSummaryIndex.
+      "<hr><pre>\n".
+      $shortindex.
+      "</pre><hr>\n".
+      $tail_html."</body></html>\n");
+
+$bypackageindex='';
+for $k (map {$_->[0] }
+       sort { $a->[2] cmp $b->[2]  ||  $a->[1] <=> $b->[1] }
+       map { [$_, split(' ',$_,2)] } keys %sient)
+    { $bypackageindex.= $sient{$k}; }
+&file('ix/psummary.html','non',
+      $gPackageLog.
+      "<hr><pre>\n$shorthead\n".
+      $bypackageindex.
+      "</pre><hr>\n".
+      $tail_html."</body></html>\n");
+
+open(P,"$gPseudoDescFile") ||
+    &quit("$gPseudoDescFile: $!");
+$ppd=''; while(<P>) { s/\s*\n$//; $ppd.= &sani($_)."\n"; } close(P);
+&file('ix/pseudopackages.html','non',
+      $gPseudoIndex.
+      "<hr><pre>\n$ppd".
+      "</pre><hr>\n".
+      $tail_html."</body></html>\n");
+
+$_= $gHTMLStamp; s/SUBSTITUTE_DTIME/$dtime/o;
+
+&file('ix/zstamp.html','non',$_."</body></html>\n");
+
+sub notimestamp ($) {
+    $_= $_[0];
+    s/\<\!\-\-timestamp\-\-\>\n.*\n\<\!\-\-\/timestamp\-\-\>\n//;
+    return $_;
+}
+
+sub file {
+    local ($name,$ii,$file)= @_;
+    if ($diff) {
+        $cmppath= "$wwwbase/$name".($ii eq 'ref' ? '.ref' : '');
+        if (open(ORIG,"$cmppath")) {
+            undef $/; $orig= <ORIG>; $/= "\n";
+            close(ORIG);
+            if (&notimestamp($orig) eq &notimestamp($file)) {
+               print "preserve $name\n";
+               return;
+           }
+            defined($c= open(P,"-|")) or &quit("pipe/fork for diff: $!");
+            if (!$c) {
+                open(Q,"|diff -e $cmppath -") or die "pipe/fork II for diff: $!\n";
+                print Q $file or die "write orig to diff: $!\n";
+                close(Q); $?==0 || $?==256 or die "diff gave $?\n";
+                exit($?>>8);
+            }
+            undef $/; $difftxt= <P>; $/= "\n";
+            close(P); $?==0 || $?==256 or die "diff fork gave $?\n";
+            if ($?==0) {
+                print "preserve $name\n";
+                return;
+            }
+            $v= (split(/\n/,$difftxt));
+            print "diff $v $ii $name\n${difftxt}thatdiff $name\n"
+                or &quit("stdout (diff): $!");
+            return;
+        }
+    } 
+    $v= (split(/\n/,$file));
+    print "file $v $ii $name\n${file}thatfile $name\n" or &quit("stdout: $!");
+}
+
+sub preserve {
+    print "preserve $_[0]\n";
+}
+
+print "end\n";
+
+while ($u= $cleanups[$#cleanups]) { &$u; }
+exit 0;
diff --git a/scripts/db2html.in b/scripts/db2html.in
deleted file mode 100755 (executable)
index b45d2b9..0000000
+++ /dev/null
@@ -1,653 +0,0 @@
-#!/usr/bin/perl
-# $Id: db2html.in,v 1.22 2004/04/19 10:03:53 cjwatson Exp $
-# usage: db2html [-diff] [-stampfile=<stampfile>] [-lastrun=<days>] <wwwbase>
-
-#load the necessary libraries/configuration
-$config_path = '/etc/debbugs';
-$lib_path = '/usr/lib/debbugs';
-
-require("$config_path/config");
-require("$config_path/text");
-require("$lib_path/errorlib");
-$ENV{'PATH'} = $lib_path.':'.$ENV{'PATH'};
-
-use POSIX qw(strftime tzset);
-$ENV{"TZ"} = 'UTC';
-tzset();
-
-#set current working directory
-chdir("$gSpoolDir") || die "chdir spool: $!\n";
-
-#setup variables
-$diff = 0;
-$stampfile = 'stamp.html';
-$tail_html = $gHTMLTail; 
-$expirynote_html = '';
-$expirynote_html = $gHTMLExpireNote if $gRemoveAge;
-$shorthead = ' Ref   * Package    Keywords/Subject                    Submitter';
-$shortindex = ''; 
-$amonths = -1;
-$indexunmatched = '';
-%displayshowpendings = ('pending','outstanding',
-                       'done','resolved',
-                       'forwarded','forwarded to upstream software authors');
-
-#set timestamp for html files
-$dtime = strftime "%a, %e %b %Y %T UTC", localtime;
-$tail_html =~ s/SUBSTITUTE_DTIME/$dtime/;
-
-#check for commandline switches
-while (@ARGV && $ARGV[0] =~ m/^-/) 
-{      if ($ARGV[0] eq '-diff') { $diff=1; }
-    elsif ($ARGV[0] =~ m/^-lastrun\=([0-9.]+)$/) { $lastrun= $1; undef $stampfile; }
-    elsif ($ARGV[0] =~ m/^-full$/) { undef $lastrun; undef $stampfile; }
-    elsif ($ARGV[0] =~ m/^-stampfile\=(\S+)$/) { $stampfile= $1; }
-    else { &quit("bad usage"); }
-    shift;
-}
-
-#check for remaing argument, only one...
-@ARGV==1 or die;
-$wwwbase= shift(@ARGV);
-
-#get starting time
-defined($startdate= time) || &quit("failed to get time: $!");
-
-$|=1;
-
-#if stamp file was given, 
-if (defined($stampfile)) 
-{      if (open(X,"< $stampfile")) 
-       {       $lastrun= -M X;
-        close(X);
-        printf "progress last run %.7f days\n",$lastrun;
-    } else { print "progress stamp file $stampfile: $! - full\n"; }
-}
-
-#only process file if greater than last run...
-if (defined($lastrun) && -M "db-h" > $lastrun) 
-{      $_= $gHTMLStamp;
-    s/SUBSTITUTE_DTIME/$dtime/o;
-    s/\<\!\-\-updateupdate\-\-\>.*\<\!\-\-\/updateupdate\-\-\>/check/;
-    &file('ix/zstamp.html','non',$_."</body></html>\n");
-       print "noremoves";
-#    print "db2html: no changes since last run\n";
-    exit 0;
-}
-
-#parse maintainer file
-open(MM,"$gMaintainerFile") || &quit("open $gMaintainerFile: $!");
-while(<MM>) 
-{      m/^(\S+)\s+(\S.*\S)\s*$/ || &quit("$gMaintainerFile: \`$_'");
-    ($a,$b)=($1,$2);
-    $a =~ y/A-Z/a-z/;
-    $maintainer{$a}= $b;
-}
-close(MM);
-
-#load all database files
-opendir(D,'db-h') || &quit("opendir db-h: $!");
-@dirs = grep(s#^#db-h/#,grep(/^\d+$/,readdir(D)));
-closedir(D);
-foreach my $dir (@dirs) {
-    opendir(D,$dir);
-    push @files, grep(/^-?\d+\.log$/,readdir(D));
-    closedir(D);
-}
-@files = sort { $a <=> $b } @files;
-
-for $pending (qw(pending done forwarded)) 
-{      for $severity (@showseverities) 
-       {       eval "\$index${pending}${severity}= \$iiindex${pending}${severity}= ''; 1;"
-            or &quit("reset \$index${pending}${severity}: $@");
-    }
-}
-
-for $f (@files) 
-{      next unless $f =~ m/^(-?\d+)\.log$/;
-    $ref= $1;
-       #((print STDERR "$ref\n"),
-       #next
-       #)
-       # unless $ref =~ m/^-/ || $ref =~ m/^124/;
-    &filelock("lock/$ref");
-    $preserveonly= defined($lastrun) && -M "db-h/".get_hashname($ref)."/$ref.log" > $lastrun;
-    if ($ref =~ m/^-\d$/) 
-       {       $week= $ref eq '-1' ? 'this week' :
-               $ref eq '-2' ? 'last week' :
-               $ref eq '-3' ? 'two weeks ago' :
-                              ($ref-1)." weeks ago";
-        $linkto= "ju/unmatched$ref";
-        $short= "junk, $week";
-        $descriptivehead=
-            "This includes messages sent to <code>done\@$gEmailDomain</code>\n".
-            "which did not have a $gBug reference number in the Subject line\n".
-            "or which contained an\n".
-            "unknown or out of date $gBug report number (these cause a warning\n".
-            "to be sent to the sender) and details about the messages\n".
-            "sent to <code>request@$gEmailDomain</code> (all of which".
-            "produce replies).\n";
-        $indexlink= "Messages not matched to a specific $gBug report - $week";
-        $data->{subject}= '';
-        $indexentry= '';
-        undef $tpack;
-        undef $tmaint;
-        undef $iiref;
-        $tpackfile= "pnone.html";
-        $indexpart= 'unmatched';
-    } else 
-       {
-       $data=readbug($ref);
-        $_= $data->{package}; y/A-Z/a-z/; $_= $` if m/[^-+._a-z0-9()]/;
-        $tpack= $_;
-        if ($data->{severity} eq '' || $data->{severity} eq 'normal') 
-               {       $showseverity= '';
-            $addseverity= $gDefaultSeverity;
-        } elsif (isstrongseverity($data->{severity})) 
-               {       $showseverity= "<strong>Severity: $data->{severity}</strong>;\n";
-            $addseverity= $data->{severity};
-        } else 
-               {       $showseverity= "Severity: <em>$data->{severity}</em>;\n";
-            $addseverity= $data->{severity};
-        }
-        $days= int(($startdate - $data->{date})/86400); close(S);
-        $indexlink= "#$ref: ".&sani($data->{subject});
-        $indexentry= '';
-        $packfile= length($tpack) ? "pa/l$tpack.html" : "pa/none.html";
-        $indexentry .= "Package: <A href=\"../$packfile\"><strong>".
-                        &sani($data->{package})."</strong></A>;\n"
-            if length($data->{package});
-        $indexentry .= $showseverity;
-        $indexentry .= "Reported by: ".&sani($data->{originator});
-        $indexentry .= ";\nOwned by: ".&sani($data->{owner})
-            if length($data->{owner});
-        $indexentry .= ";\nKeywords: ".&sani($data->{keywords})
-            if length($data->{keywords});
-        $linkto= $ref; $linkto =~ s,^..,$&/$&,;
-        @merged= split(/ /,$data->{mergedwith});
-        if (@merged) 
-               {       $mseparator= ";\nmerged with ";
-            for $m (@merged) 
-                       {       $mfile= $m; $mfile =~ s,^..,$&/$&,;
-                $indexentry .= $mseparator."<A href=\"../$mfile.html\">#$m</A>";
-                $mseparator= ",\n";
-            }
-        }
-        $daysold=$submitted='';
-        if (length($data->{done})) 
-               {       $indexentry .= ";\n<strong>Done:</strong> ".&sani($data->{done});
-            $indexpart= "done$addseverity";
-        } elsif (length($data->{forwarded})) 
-               {       $indexentry .= ";\n<strong>Forwarded</strong> to ".&sani($data->{forwarded});
-            $indexpart= "forwarded$addseverity";
-        } else 
-               {       $cmonths= int($days/30);
-            if ($cmonths != $amonths) 
-                       {       $msg= $cmonths == 0 ? "Submitted in the last month" :
-                       $cmonths == 1 ? "Over one month old" :
-                       $cmonths == 2 ? "Over two months old - attention is required" :
-                       "OVER $cmonths MONTHS OLD - ATTENTION IS REQUIRED";
-                $shortindex .= "</pre><h2>$msg:</h2><pre>\n$shorthead\n";
-                $amonths= $cmonths;
-            }
-            $pad= 6-length(sprintf("%d",$f));
-            $thissient=
-                ($pad>0 ? ' 'x$pad : '').
-                sprintf("<A href=\"../%s.html\">%d</A>",$linkto,$ref).
-                &sani(sprintf(" %-1.1s %-10.10s %-35.35s %-.25s\n",
-                                               $data->{severity},
-                        $data->{package},
-                        (length($data->{keywords}) ? $data->{keywords}.'/' : '').
-                        $data->{subject}, $data->{originator}));
-            $shortindex.= $thissient;
-            $sient{"$ref $data->{package}"}= $thissient;
-            if ($days >= 7) 
-                       {       $font= $days <= 30 ? '' :
-                       $days <= 60 ? 'em' :
-                    'strong';
-                $efont= length($font) ? "</$font>" : '';
-                $font= length($font) ? "<$font>" : '';
-                $daysold= "; $font$days days old$efont";
-            }
-            if ($preserveonly) {
-                $submitted = 'THIS IS A BUG IN THE BUG PROCESSOR';
-            } else {
-                $submitted = strftime "%a, %e %b %Y %T %Z", localtime($data->{date});
-            }
-            $submitted= "; dated $submitted";
-            $indexpart= "pending$addseverity";
-        }
-        $iiref= $ref;
-        $short= $ref; $short =~ s/^\d+/#$&/;
-        $tmaint= defined($maintainer{$tpack}) ? $maintainer{$tpack} : '(unknown)';
-        $qpackage= &sani($_);
-        $descriptivehead= $indexentry.$submitted.";\nMaintainer for $qpackage is\n".
-            '<A href="../ma/l'.&maintencoded($tmaint).'.html">'.&sani($tmaint).'</A>.';
-        $indexentry .= $daysold;
-        $indexentry .= ".";
-    }
-    $indexadd='';
-    $indexadd .= "<!--iid $iiref-->" if defined($iiref);
-    $indexadd .= "<li><A href=\"../$linkto.html\">".$indexlink."</A>";
-    $indexadd .=  "<br>\n".$indexentry if length($indexentry);
-    $indexadd .= "<!--/iid-->" if defined($iiref);
-    $indexadd .= "\n";
-    $estr= "\$index$indexpart = \$indexadd.\$index$indexpart; 1;";
-    eval($estr) || &quit("eval add to \$index$indexpart ($estr) failed: $@");
-       #print STDERR ">$estr|$indexadd<\n";
-    $indexadd= "<!--ii $iiref-->\n" if defined($iiref);
-    eval("\$iiindex$indexpart = \$indexadd.\$iiindex$indexpart; 1;") ||
-        &quit("eval add to \$iiindex$indexpart failed: $@");
-    if (defined($tmaint)) 
-       {       $countpermaint{$tmaint} += length($data->{done}) ? 0 : length($data->{forwarded}) ? 0 : 1;
-        eval("\$permaint${indexpart}{\$tmaint} .= \$indexadd; 1;") ||
-            &quit("eval add to \$permaint${indexpart}{\$tmaint} failed: $@");
-    }
-    if (defined($tpack)) 
-       {       $countperpack{$tpack} += length($data->{done}) ? 0 : length($data->{forwarded}) ? 0 : 1;
-        eval("\$perpack${indexpart}{\$tpack} .= \$indexadd; 1;") ||
-            &quit("eval add to \$perpack${indexpart}{\$tpack} failed: $@");
-    }
-    if ($preserveonly) { &preserve("$linkto.html"); &preserve("$linkto-b.html"); &unfilelock; next; }
-    my $hash = get_hashname($ref);
-    open(L,"db-h/$hash/$ref.log") || &quit("open db-h/$hash/$ref.log: $!");
-    $log='';
-    $boring=''; $xmessage= 0;
-    $normstate= 'kill-init';
-    $suppressnext= 0;
-    while(<L>) {
-        if (m/^\07$/) {
-            $normstate eq 'kill-init' || $normstate eq 'kill-end' ||
-                &quit("$ref ^G in state $normstate");
-            $normstate= 'incoming-recv';
-        } elsif (m/^\01$/) {
-            $normstate eq 'kill-init' || $normstate eq 'kill-end' ||
-                &quit("$ref ^A in state $normstate");
-            $normstate= 'autocheck';
-        } elsif (m/^\02$/) {
-            $normstate eq 'kill-init' || $normstate eq 'kill-end' ||
-                &quit("$ref ^B in state $normstate");
-            $normstate= 'recips';
-        } elsif (m/^\03$/) {
-            $normstate eq 'go' || $normstate eq 'go-nox' || $normstate eq 'html' ||
-                &quit("$ref ^C in state $normstate");
-            $this .= "</pre>\n" if $normstate eq 'go' || $normstate eq 'go-nox';
-            if ($normstate eq 'html') {
-                $xmessage++;
-                $this .= "  <em><A href=\"../$linkto-b.html#m$xmessage\">Full text</A>".
-                         " available.</em>";
-            }
-            if ($suppressnext && $normstate ne 'html') {
-                $ntis= $this; $ntis =~ s:\<pre\>:</A><pre>:i;
-                $boring .= "<hr><A name=\"m$xmessage\">\n$ntis\n";
-            } else {
-                $log = $this. "<hr>\n". $log;
-            }
-            $suppressnext= $normstate eq 'html';
-            $normstate= 'kill-end';
-        } elsif (m/^\05$/) {
-            $normstate eq 'kill-body' || &quit("^E in state $normstate");
-            $this .= "<pre>\n";
-            $normstate= 'go';
-        } elsif (m/^\06$/) {
-            $normstate eq 'kill-init' || $normstate eq 'kill-end' ||
-                &quit("$ref ^F in state $normstate");
-            $normstate= 'html'; $this= '';
-        } elsif ($normstate eq 'incoming-recv') {
-            $pl= $_; $pl =~ s/\n+$//;
-            m/^Received: \(at (\S+)\) by (\S+)\;/ ||
-                &quit("bad line \`$pl' in state incoming-recv");
-            $this = "<h2>Message received at ".&sani("$1\@$2").":</h2><br>\n".
-                    "<pre>\n".
-                    "$_";
-            $normstate= 'go';
-        } elsif ($normstate eq 'html') {
-            $this .= $_;
-        } elsif ($normstate eq 'go') {
-            s/^\030//;
-            $this .= &sani($_);
-        } elsif ($normstate eq 'go-nox') {
-            next if !s/^X//;
-            $this .= &sani($_);
-        } elsif ($normstate eq 'recips') {
-            if (m/^-t$/) {
-                $this = "<h2>Message sent:</h2><br>\n";
-            } else {
-                s/\04/, /g; s/\n$//;
-                $this = "<h2>Message sent to ".&sani($_).":</h2><br>\n";
-            }
-            $normstate= 'kill-body';
-        } elsif ($normstate eq 'autocheck') {
-            next if !m/^X-Debian-Bugs(-\w+)?: This is an autoforward from (\S+)/;
-            $normstate= 'autowait';
-            $this = "<h2>Message received at $2:</h2><br>\n";
-        } elsif ($normstate eq 'autowait') {
-            next if !m/^$/;
-            $normstate= 'go-nox';
-            $this .= "<pre>\n";
-        } else {
-            &quit("$ref state $normstate line \`$_'");
-        }
-    }
-    &quit("$ref state $normstate at end") unless $normstate eq 'kill-end';
-    close(L);
-    if (length($boring)) {
-        &file("$linkto-b.html",'non',
-              "<html><head><title>$gProject $gBug report logs - ".
-              "$short, boring messages</title>\n".
-              "<link rev=\"made\" href=\"mailto:$gMaintainerEmail)\">\n".
-              "</head>$gHTMLStart<h1>$gProject $gBugreport logs -".
-              "\n <A href=\"../$linkto.html\">$short</A>,".
-              " boring messages</h1>\n$boring\n<hr>\n".
-              $tail_html."</body></html>\n");
-    }
-    &file("$linkto.html",'non',
-          "<html><head><title>$gProject $gBug report logs - ".
-          "$short</title>\n".
-          "<link rev=\"made\" href=\"mailto:$gMaintainerEmail\">\n".
-          "</head>$gHTMLStart<h1>$gProject $gBug report logs -  $short<br>\n".
-          &sani($data->{subject})."</h1>".
-          "$descriptivehead\n".
-          "\n<hr>\n".
-          $log.
-          $tail_html."</body></html>\n");
-    &unfilelock;
-}
-
-sub maintsort {
-    $_= $_[0];
-    s/([^<>()]+) \(([^()<>]+)\)/$2 \<$1\>/;
-    
-    s/\s+/ /g;
-    s/^\s*//;
-    $email= s/ *\<[^<>()]+\>$//g ? $& : '';
-    $_= "$1 $_" if s/ (\S+)$//;
-    $_.= $email;
-    $_;
-}
-
-sub maintencoded {
-    return $maintencoded{$_[0]} if defined($maintencoded{$_[0]});
-    local ($input)= @_;
-    local ($todo,$encoded)= ($input);
-    while ($todo =~ m/\W/) {
-        $encoded.=$`.sprintf("-%02x_",unpack("C",$&));
-        $todo= $';
-    }
-    $encoded.= $todo;
-    $encoded =~ s/-2e_/\./g;
-    $encoded =~ s/^([^,]+)-20_-3c_(.*)-40_(.*)-3e_/$1,$2,$3,/;
-    $encoded =~ s/^(.*)-40_(.*)-20_-28_([^,]+)-29_$/,$1,$2,$3/;
-    $encoded =~ s/-20_/_/g;
-    $encoded =~ s/-([^_]+)_-/-$1/g;
-    $maintencoded{$input}= $encoded;
-}
-
-for $tmaint (keys %countpermaint) {
-    $_= $tmaint;
-    $after=$before=$sort2d=$sort2s=$sort1d=$sort1s='';
-    $after= "$&$after" if s/\s*\<[^<>()]+\>\s*$//;
-    $after= "$&$after" if s/\s*\)\s*$//;
-    $after= "$&$after" if s/\s*,.*$//;
-    $before.= $& if s/^.*\(\s*//;
-    $sort2d= $& if s/\S+$//;
-    $sort1d= $_;
-    while (s/^([^()<>]+)\. */$1 /) { };
-    s/\s+$//; y/A-Za-z/a-zA-Z/; $sort1s= $_;
-    $sort2s= $sort2d; $sort2s =~ y/A-Za-z/a-zA-Z/;
-    $maintsort{$tmaint}= $sort2s.' '.$sort1s.' '.$before.$sort1d.$sort2d.$after;
-    $maintdisplay{$tmaint}=
-        &sani($before).'<strong>'.&sani($sort1d.$sort2d).'</strong>'.&sani($after);
-}
-
-sub heading ($$) {
-    my ($pt,$sv) = @_;
-    return $displayshowseverities{$sv}.' - '.$displayshowpendings{$pt};
-}
-
-sub makeindex ($$$) {
-    my ($varprefix,$varsuffix,$tkey) = @_;
-    my ($pending,$severity,$anydone,$text);
-    $anydone= 0;
-    $text= '';
-    for $pending (qw(pending forwarded done)) {
-        for $severity (@showseverities) {
-            $estr= "\$value= \\${varprefix}${pending}${severity}${varsuffix}; 1;";
-#print STDERR $estr;
-            eval $estr
-                or &quit("eval get \$${varprefix}${pending}${severity} failed: $@");
-#print STDERR ">$$value<\n";
-            next unless length($$value);
-            $text.= "<hr>\n<h2>".&heading($pending,$severity).":</h2>\n".
-                    "(List of <A href=\"../si/$pending$severity.html\">all".
-                    " such $gBugs</A> is available.)\n<ul>\n".
-                    $$value.
-                    "</ul>\n";
-            $anydone=1 if $pending eq 'done';
-        }
-    }
-    $text.= $expirynote_html if $anydone;
-    return $text;
-}        
-
-&file("ix/full.html",'def',
-      $gFullIndex.
-      makeindex('$index',"",'').
-      "<hr>\n".
-      $tail_html."</body><html>\n");
-
-&file("ju/junk.html",'non',
-      $gJunkIndex.
-      "<hr>\n<h2>Junk (messages without a specific $gBug report number):</h2>\n".
-      "(\`this week' is everything since last Wednesday.)\n<ul>\n".
-      $indexunmatched.
-      "</ul><hr>\n".
-      $tail_html."</body><html>\n");
-
-$nobugs_html= "No reports are currently in this state.";
-$who_html= $gProject;
-$owner_addr= $gMaintainerEmail;
-$otherindex_html= "For other kinds of index or for other information about
-$gProject and the $gBug system, see the <A HREF=\"../../\">$gBug system top-level
-contents WWW page</A>.
-
-";
-
-for $pending (qw(pending forwarded done)) {
-    for $severity (@showseverities) {
-        eval "\$value= \\\$iiindex${pending}${severity}; 1;"
-            or &quit("eval get \$iiindex${pendingtype}${severity} failed: $@");
-        $value= \$nobugs_html if !length($$value);
-        $headstring= &heading($pending,$severity);
-        &file("si/$pending$severity.html",'ref',
-              "<html><head><title>$who_html $gBug reports: $headstring</title>\n".
-              "<link rev=\"made\" href=\"mailto:".&sani($owner_addr)."\">\n".
-              "</head>$gHTMLStart<h1>$who_html $gBug reports: $headstring</h1>\n".
-              $otherindex_html.
-              ($pending eq 'done' ? "<P>\n$expirynote_html" : '').
-              "<hr>\n<ul>\n".
-              $$value.
-              "</ul>\n<hr>\n".
-              $tail_html."</body></html>\n");
-    }
-}
-
-sub individualindexes ($\@&\%&&$$$$$&&) {
-    my ($filename,$keysref,$getfilenameref,$countref,$getdisplayref,
-        $getsimpledisplayref,$what,$caveat,$whatplural,$abbrev,$ihead,
-        $getxinforef,$getxindexref) = @_;
-    my ($itext,$i,$tkey,$sani,$count,$tfilename,$refto,$backnext,$xitext,$bugbugs);
-    $itext='';
-    for ($i=0; $i<=$#$keysref; $i++) {
-        $tkey= $$keysref[$i];
-        $tfilename= &$getfilenameref($tkey);
-        $sani= &$getsimpledisplayref($tkey);
-        $count= $$countref{$tkey};
-        $count= $count >= 1 ? "$count" : "no";
-        $bugbugs= $count == 1 ? "$gBug" : "$gBugs";
-        $xitext= &$getxindexref($tkey);
-        $xitext= length($xitext) ? "$count $bugbugs; $xitext"
-                                 : "$count outstanding $bugbugs";
-        $itext .= "<li><A href=\"../$tfilename\">".&$getdisplayref($tkey)."</A>"."\n".
-                  "  ($xitext)\n";
-        $backnext= '';
-        if ($i>0) {
-            $refto= $$keysref[$i-1];
-            $xitext= &$getxindexref($refto);
-            $xitext= " ($xitext)" if length($xitext);
-            $backnext .= "<br>\nPrevious $what in list, <A href=\"../".
-                         &$getfilenameref($refto)."\">".&$getdisplayref($refto)."</A>".
-                         "$xitext\n";
-        }
-        if ($i<$#$keysref) {
-            $refto= $$keysref[$i+1];
-            $xitext= &$getxindexref($refto);
-            $xitext= " ($xitext)" if length($xitext);
-            $backnext .= "<br>\nNext $what in list, <A href=\"../".
-                         &$getfilenameref($refto)."\">".&$getdisplayref($refto)."</A>".
-                         "$xitext\n";
-        }
-        &file($tfilename,'ref',
-              "<html><head><title>$gProject $gBug reports: $what $sani</title>\n".
-              "<link rev=\"made\" href=\"mailto:$gMaintainerEmail\">\n".
-              "</head>$gHTMLStart<h1>$gProject $gBug reports: $what $sani</h1>\n".
-              &$getxinforef($tkey).
-              $caveat.
-              "See the <A href=\"../$filename\">listing of $whatplural</A>.\n".
-              $backnext.
-              &makeindex("\$per${abbrev}","{\$tkey}",$tkey).
-              "<hr>\n".
-              $tail_html."</body></html>\n");
-    }
-    &file($filename,'non',
-          $ihead.
-          "<hr><ul>\n".
-          $itext.
-          "</ul><hr>\n".
-          $tail_html."</body></html>\n");
-}
-
-@maintainers= sort { $maintsort{$a} cmp $maintsort{$b}; } keys %countpermaint;
-individualindexes('ix/maintainers.html',
-                  @maintainers,
-                  sub { 'ma/l'.&maintencoded($_[0]).'.html'; },
-                  %countpermaint,
-                  sub { $maintdisplay{$_[0]}; },
-                  sub { &sani($_[0]); },
-                  'maintainer',
-                  "Note that there may be other reports filed under different
-                                 variations on the maintainer\'s name and email address.<P>",
-                  'maintainers',
-                  'maint',
-                  $gMaintIndex,
-                  sub { return ''; },
-                  sub { return ''; });
-
-@packages= sort keys %countperpack;
-individualindexes('ix/packages.html',
-                  @packages,
-                  sub { length($_[0]) ? "pa/l$_[0].html" : 'pa/none.html'; },
-                  %countperpack,
-                  sub { length($_[0]) ? $_[0] : 'not specified'; },
-                  sub { &sani(length($_[0]) ? $_[0] : 'not specified'); },
-                  'package',
-                  "Note that with multi-binary packages there may be other
-                                 reports filed under the different binary package names.<P>",
-                  'packages',
-                  'pack',
-                  $gPackageIndex,
-                  sub {
-                      return unless defined($maintainer{$_[0]});
-                      $tmaint= $maintainer{$_[0]};
-                      return "Maintainer for $_[0] is <A href=\"../ma/l".
-                             &maintencoded($tmaint).
-                             ".html\">".&sani($tmaint)."</A>.\n<p>\n";
-                  },
-                  sub {
-                      return unless defined($maintainer{$_[0]});
-                      $tmaint= $maintainer{$_[0]};
-                      return "<A href=\"../ma/l".
-                             &maintencoded($tmaint).
-                             ".html\">".&sani($tmaint)."</A>";
-                  });
-
-&file('ix/summary.html','non',
-      $gSummaryIndex.
-      "<hr><pre>\n".
-      $shortindex.
-      "</pre><hr>\n".
-      $tail_html."</body></html>\n");
-
-$bypackageindex='';
-for $k (map {$_->[0] }
-       sort { $a->[2] cmp $b->[2]  ||  $a->[1] <=> $b->[1] }
-       map { [$_, split(' ',$_,2)] } keys %sient)
-    { $bypackageindex.= $sient{$k}; }
-&file('ix/psummary.html','non',
-      $gPackageLog.
-      "<hr><pre>\n$shorthead\n".
-      $bypackageindex.
-      "</pre><hr>\n".
-      $tail_html."</body></html>\n");
-
-open(P,"$gPseudoDescFile") ||
-    &quit("$gPseudoDescFile: $!");
-$ppd=''; while(<P>) { s/\s*\n$//; $ppd.= &sani($_)."\n"; } close(P);
-&file('ix/pseudopackages.html','non',
-      $gPseudoIndex.
-      "<hr><pre>\n$ppd".
-      "</pre><hr>\n".
-      $tail_html."</body></html>\n");
-
-$_= $gHTMLStamp; s/SUBSTITUTE_DTIME/$dtime/o;
-
-&file('ix/zstamp.html','non',$_."</body></html>\n");
-
-sub notimestamp ($) {
-    $_= $_[0];
-    s/\<\!\-\-timestamp\-\-\>\n.*\n\<\!\-\-\/timestamp\-\-\>\n//;
-    return $_;
-}
-
-sub file {
-    local ($name,$ii,$file)= @_;
-    if ($diff) {
-        $cmppath= "$wwwbase/$name".($ii eq 'ref' ? '.ref' : '');
-        if (open(ORIG,"$cmppath")) {
-            undef $/; $orig= <ORIG>; $/= "\n";
-            close(ORIG);
-            if (&notimestamp($orig) eq &notimestamp($file)) {
-               print "preserve $name\n";
-               return;
-           }
-            defined($c= open(P,"-|")) or &quit("pipe/fork for diff: $!");
-            if (!$c) {
-                open(Q,"|diff -e $cmppath -") or die "pipe/fork II for diff: $!\n";
-                print Q $file or die "write orig to diff: $!\n";
-                close(Q); $?==0 || $?==256 or die "diff gave $?\n";
-                exit($?>>8);
-            }
-            undef $/; $difftxt= <P>; $/= "\n";
-            close(P); $?==0 || $?==256 or die "diff fork gave $?\n";
-            if ($?==0) {
-                print "preserve $name\n";
-                return;
-            }
-            $v= (split(/\n/,$difftxt));
-            print "diff $v $ii $name\n${difftxt}thatdiff $name\n"
-                or &quit("stdout (diff): $!");
-            return;
-        }
-    } 
-    $v= (split(/\n/,$file));
-    print "file $v $ii $name\n${file}thatfile $name\n" or &quit("stdout: $!");
-}
-
-sub preserve {
-    print "preserve $_[0]\n";
-}
-
-print "end\n";
-
-while ($u= $cleanups[$#cleanups]) { &$u; }
-exit 0;
diff --git a/scripts/errorlib b/scripts/errorlib
new file mode 100755 (executable)
index 0000000..a2e9016
--- /dev/null
@@ -0,0 +1,41 @@
+# -*- perl -*-
+
+use Mail::Address;
+use Debbugs::MIME qw(decode_rfc1522 encode_rfc1522 getmailbody);
+use Debbugs::Packages qw(:all);
+use Debbugs::Common qw(:all);
+use Debbugs::Status qw(:all);
+use Carp;
+
+sub unlockreadbugmerge {
+    local ($rv) = @_;
+    &unfilelock if $rv >= 2;
+    &unfilelock if $rv >= 1;
+}
+
+%saniarray= ('<','lt', '>','gt', '&','amp', '"','quot');
+
+sub sani {
+    my ($in) = @_;
+    carp "You should be using HTML::Entities instead.";
+    $in =~ s/([<>&"])/$saniarray{$1}/g;
+    return $in;
+}
+
+sub get_addresses {
+       return
+           map { $_->address() }
+           map { Mail::Address->parse($_) } @_;
+}
+
+@severities= grep { not exists $gObsoleteSeverities{$_} } @gSeverityList;
+@showseverities= @severities;
+grep ($_= $_ eq '' ? $gDefaultSeverity : $_, @showseverities);
+%displayshowseverities= %gSeverityDisplay;
+
+# compatibility
+if (defined $gFowardList and not defined $gForwardList) {
+    $gForwardList = $gFowardList;
+}
+
+1;
diff --git a/scripts/errorlib.in b/scripts/errorlib.in
deleted file mode 100755 (executable)
index a2e9016..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-# -*- perl -*-
-
-use Mail::Address;
-use Debbugs::MIME qw(decode_rfc1522 encode_rfc1522 getmailbody);
-use Debbugs::Packages qw(:all);
-use Debbugs::Common qw(:all);
-use Debbugs::Status qw(:all);
-use Carp;
-
-sub unlockreadbugmerge {
-    local ($rv) = @_;
-    &unfilelock if $rv >= 2;
-    &unfilelock if $rv >= 1;
-}
-
-%saniarray= ('<','lt', '>','gt', '&','amp', '"','quot');
-
-sub sani {
-    my ($in) = @_;
-    carp "You should be using HTML::Entities instead.";
-    $in =~ s/([<>&"])/$saniarray{$1}/g;
-    return $in;
-}
-
-sub get_addresses {
-       return
-           map { $_->address() }
-           map { Mail::Address->parse($_) } @_;
-}
-
-@severities= grep { not exists $gObsoleteSeverities{$_} } @gSeverityList;
-@showseverities= @severities;
-grep ($_= $_ eq '' ? $gDefaultSeverity : $_, @showseverities);
-%displayshowseverities= %gSeverityDisplay;
-
-# compatibility
-if (defined $gFowardList and not defined $gForwardList) {
-    $gForwardList = $gFowardList;
-}
-
-1;
diff --git a/scripts/expire b/scripts/expire
new file mode 100755 (executable)
index 0000000..d5149e9
--- /dev/null
@@ -0,0 +1,129 @@
+#!/usr/bin/perl
+# This script is part of debbugs, and is released
+# under the terms of the GPL version 2, or any later
+# version at your option.
+# See the file README and COPYING for more information.
+#
+# [Other people may have contributed to this file; their copyrights
+# should go here too.]
+# Copyright 2004 by Collin Watson <cjwatson@debian.org>
+# Copyright 2007 by Don Armstrong <don@donarmstrong.com>
+
+use Getopt::Long;
+use Pod::Usage;
+
+use warnings;
+use strict;
+
+=head1 NAME
+
+expire - Expires archiveable bugs by copying to archive or deleting
+
+=head1 SYNOPSIS
+
+ expire [options]
+
+ Options:
+  --debug, -d debugging level (Default 0)
+  --help, -h display this help
+  --man, -m display manual
+
+=head1 OPTIONS
+
+=over
+
+=item B<--debug, -d>
+
+Debug verbosity. (Default 0)
+
+=item B<--help, -h>
+
+Display brief useage information.
+
+=item B<--man, -m>
+
+Display this manual.
+
+=back
+
+=head1 EXAMPLES
+
+
+=cut
+
+my %options = (debug           => 0,
+              help            => 0,
+              man             => 0,
+              quick           => 0,
+              index_path      => undef,
+              );
+
+GetOptions(\%options,'debug|d+','help|h|?','man|m') or pod2usage(2);
+pod2usage(1) if $options{help};
+pod2usage(-verbose=>2) if $options{man};
+
+
+my $verbose = $options{debug};
+
+use Debbugs::Control qw(bug_archive);
+use Debbugs::Status qw(bug_archiveable);
+
+use Debbugs::Config qw(:config);
+use Debbugs::Common qw(:lock);
+
+# No $gRemoveAge means "never expire".
+exit 0 unless $config{remove_age};
+
+chdir($config{spool_dir}) || die "chdir $config{spool_dir} failed: $!\n";
+
+#get list of bugs (ie, status files)
+opendir(DIR,"db-h") or die "Unable to open dir db-h: $!";
+my @dirs = sort { $a cmp $b } grep(s,^,db-h/,, grep(m/^\d+$/,readdir(DIR)));
+close(DIR);
+my @list;
+foreach my $dir (@dirs) {
+    opendir(DIR,$dir);
+    push @list, sort { $a <=> $b } grep(s/\.summary$//,grep(m/^\d+\.summary$/,readdir(DIR)));
+    close(DIR);
+}
+
+my $bug;
+my $errors=0;
+our $exit_now = 0;
+#process each bug (ie, status file)
+my @bugs_to_archive = ();
+for my $bug (@list) {
+     # Weeeee.
+     print "Examining $bug\n" if $verbose;
+     next unless bug_archiveable(bug=>$bug);
+     push @bugs_to_archive,$bug;
+}
+
+$SIG{INT} = sub {$exit_now=1;};
+# At this point we want to block control
+if (not lockpid($config{spool_dir}.'/lock/expire.pid')) {
+     exit 1;
+}
+# We'll also double check that the bug can be archived
+for my $bug (@bugs_to_archive) {
+     last if $exit_now;
+     print "Reexamining $bug\n" if $verbose;
+     next unless bug_archiveable(bug=>$bug);
+     last if $exit_now;
+     print "Bug $bug can be archived: " if $verbose;
+     eval {
+         bug_archive(bug=>$bug,
+                    );
+         print "archived.\n" if $verbose;
+     };
+     if ($@) {
+         $errors=1;
+         print "failed.\n" if $verbose;
+         print STDERR "Unable to archive bug# $bug which I thought I could archive:\n$@\n";
+     }
+     last if $exit_now;
+}
+unlink($config{spool_dir}.'/lock/expire.pid');
+
+
+exit $errors;
diff --git a/scripts/expire.in b/scripts/expire.in
deleted file mode 100755 (executable)
index d5149e9..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-#!/usr/bin/perl
-# This script is part of debbugs, and is released
-# under the terms of the GPL version 2, or any later
-# version at your option.
-# See the file README and COPYING for more information.
-#
-# [Other people may have contributed to this file; their copyrights
-# should go here too.]
-# Copyright 2004 by Collin Watson <cjwatson@debian.org>
-# Copyright 2007 by Don Armstrong <don@donarmstrong.com>
-
-use Getopt::Long;
-use Pod::Usage;
-
-use warnings;
-use strict;
-
-=head1 NAME
-
-expire - Expires archiveable bugs by copying to archive or deleting
-
-=head1 SYNOPSIS
-
- expire [options]
-
- Options:
-  --debug, -d debugging level (Default 0)
-  --help, -h display this help
-  --man, -m display manual
-
-=head1 OPTIONS
-
-=over
-
-=item B<--debug, -d>
-
-Debug verbosity. (Default 0)
-
-=item B<--help, -h>
-
-Display brief useage information.
-
-=item B<--man, -m>
-
-Display this manual.
-
-=back
-
-=head1 EXAMPLES
-
-
-=cut
-
-my %options = (debug           => 0,
-              help            => 0,
-              man             => 0,
-              quick           => 0,
-              index_path      => undef,
-              );
-
-GetOptions(\%options,'debug|d+','help|h|?','man|m') or pod2usage(2);
-pod2usage(1) if $options{help};
-pod2usage(-verbose=>2) if $options{man};
-
-
-my $verbose = $options{debug};
-
-use Debbugs::Control qw(bug_archive);
-use Debbugs::Status qw(bug_archiveable);
-
-use Debbugs::Config qw(:config);
-use Debbugs::Common qw(:lock);
-
-# No $gRemoveAge means "never expire".
-exit 0 unless $config{remove_age};
-
-chdir($config{spool_dir}) || die "chdir $config{spool_dir} failed: $!\n";
-
-#get list of bugs (ie, status files)
-opendir(DIR,"db-h") or die "Unable to open dir db-h: $!";
-my @dirs = sort { $a cmp $b } grep(s,^,db-h/,, grep(m/^\d+$/,readdir(DIR)));
-close(DIR);
-my @list;
-foreach my $dir (@dirs) {
-    opendir(DIR,$dir);
-    push @list, sort { $a <=> $b } grep(s/\.summary$//,grep(m/^\d+\.summary$/,readdir(DIR)));
-    close(DIR);
-}
-
-my $bug;
-my $errors=0;
-our $exit_now = 0;
-#process each bug (ie, status file)
-my @bugs_to_archive = ();
-for my $bug (@list) {
-     # Weeeee.
-     print "Examining $bug\n" if $verbose;
-     next unless bug_archiveable(bug=>$bug);
-     push @bugs_to_archive,$bug;
-}
-
-$SIG{INT} = sub {$exit_now=1;};
-# At this point we want to block control
-if (not lockpid($config{spool_dir}.'/lock/expire.pid')) {
-     exit 1;
-}
-# We'll also double check that the bug can be archived
-for my $bug (@bugs_to_archive) {
-     last if $exit_now;
-     print "Reexamining $bug\n" if $verbose;
-     next unless bug_archiveable(bug=>$bug);
-     last if $exit_now;
-     print "Bug $bug can be archived: " if $verbose;
-     eval {
-         bug_archive(bug=>$bug,
-                    );
-         print "archived.\n" if $verbose;
-     };
-     if ($@) {
-         $errors=1;
-         print "failed.\n" if $verbose;
-         print STDERR "Unable to archive bug# $bug which I thought I could archive:\n$@\n";
-     }
-     last if $exit_now;
-}
-unlink($config{spool_dir}.'/lock/expire.pid');
-
-
-exit $errors;
diff --git a/scripts/gen-indices b/scripts/gen-indices
new file mode 100755 (executable)
index 0000000..11775e4
--- /dev/null
@@ -0,0 +1,224 @@
+#!/usr/bin/perl
+
+# Generates by-*.idx files for the CGI scripts
+# Copyright (c) 2005/08/03 Anthony Towns
+# GPL v2
+
+use DB_File;
+use MLDBM qw(DB_FILE Storable);
+use Fcntl qw/O_RDWR O_CREAT O_TRUNC/;
+use File::Copy;
+
+use Getopt::Long;
+use Pod::Usage;
+
+use warnings;
+use strict;
+
+use File::stat;
+use List::Util qw(min);
+
+=head1 NAME
+
+gen-indices - Generates index files for the cgi scripts
+
+=head1 SYNOPSIS
+
+ gen-indices [options]
+
+ Options:
+  --index-path path to index location
+  --quick update changed bugs
+  --debug, -d debugging level (Default 0)
+  --help, -h display this help
+  --man, -m display manual
+
+=head1 OPTIONS
+
+=over
+
+=itme B<--quick>
+
+Only update changed bugs
+
+=item B<--debug, -d>
+
+Debug verbosity. (Default 0)
+
+=item B<--help, -h>
+
+Display brief useage information.
+
+=item B<--man, -m>
+
+Display this manual.
+
+=back
+
+=head1 EXAMPLES
+
+
+=cut
+
+# Use portable Storable images
+$MLDBM::DumpMeth=q(portable);
+
+
+my %options = (debug           => 0,
+              help            => 0,
+              man             => 0,
+              quick           => 0,
+              index_path      => undef,
+              );
+
+GetOptions(\%options,'quick!','index_path|index-path=s','debug|d+','help|h|?','man|m') or pod2usage(2);
+pod2usage(1) if $options{help};
+pod2usage(-verbose=>2) if $options{man};
+
+use Debbugs::Config qw(:config);
+use Debbugs::Common qw(getparsedaddrs getbugcomponent lockpid);
+use Debbugs::Status qw(readbug);
+
+chdir($config{spool_dir}) or die "chdir $config{spool_dir} failed: $!";
+
+my $verbose = $options{debug};
+my $indexdest = $options{index_path} || $config{spool_dir};
+
+my $initialdir = "db-h";
+my $suffix = "";
+
+if (defined $ARGV[0] and $ARGV[0] eq "archive") {
+    $initialdir = "archive";
+    $suffix = "-arc";
+}
+
+if (not lockpid($config{spool_dir}.'/lock/gen-indices')) {
+     if ($options{quick}) {
+         # If this is a quick run, just exit
+         print STDERR "Another gen-indices is running; stopping\n" if $verbose;
+         exit 0;
+     }
+     print STDERR "Another gen-indices is running; stopping\n";
+     exit 1;
+}
+
+# NB: The reverse index is special; it's used to clean up during updates to bugs
+my @indexes = ('package', 'tag', 'severity','owner','submitter-email','status','reverse');
+my $indexes;
+my %slow_index = ();
+my %fast_index = ();
+if (not $options{quick}) {
+     # We'll trade memory for speed here if we're not doing a quick rebuild
+     for my $indexes (@indexes) {
+         $fast_index{$indexes} = {};
+     }
+     $indexes = \%fast_index;
+}
+else {
+     $indexes = \%slow_index;
+}
+my $time = undef;
+my $start_time = time;
+for my $i (@indexes) {
+       $slow_index{$i} = {};
+       if ($options{quick}) {
+            if (-e "$indexdest/by-$i${suffix}.idx") {
+                 system('cp','-a',"$indexdest/by-$i${suffix}.idx","$indexdest/by-$i${suffix}.idx.new") == 0
+                      or die "Error creating the new index";
+                 my $stat = stat("$indexdest/by-$i${suffix}.idx") or die "Unable to stat $indexdest/by-$i${suffix}.idx";
+                 $time = defined $time ? min($time,$stat->mtime) : $stat->mtime;
+            }
+            tie %{$slow_index{$i}}, MLDBM => "$indexdest/by-$i$suffix.idx.new",
+                 O_RDWR|O_CREAT, 0666
+                      or die "$0: can't create by-$i$suffix-idx.new: $!";
+       }
+       else {
+            tie %{$slow_index{$i}}, MLDBM => "$indexdest/by-$i$suffix.idx.new",
+                 O_RDWR|O_CREAT|O_TRUNC, 0666
+                      or die "$0: can't create by-$i$suffix-idx.new: $!";
+
+       }
+       $time = 0 if not defined $time;
+}
+
+sub addbugtoindex {
+     my ($index, $bug, @values) = @_;
+
+     if (exists $indexes->{reverse}{"$index $bug"}) {
+         # We do this insanity to work around a "feature" in MLDBM
+         for my $key (@{$indexes->{reverse}{"$index $bug"}}) {
+              my $temp = $indexes->{$index}{$key};
+              delete $temp->{$bug};
+              $indexes->{$index}{$key} = $temp;
+              $indexes->{$index}{"count $key"}--;
+         }
+         delete $indexes->{reverse}{"$index $bug"};
+     }
+     for my $key (@values) {
+         $indexes->{$index}->{"count $key"}++;
+         # We do this insanity to work around a "feature" in MLDBM
+         my $temp = $indexes->{$index}->{$key};
+         $temp->{$bug} = 1;
+         $indexes->{$index}->{$key} = $temp;
+     }
+     $indexes->{reverse}{"$index $bug"} = [@values];
+}
+
+sub emailfromrfc822 {
+       my $email = shift;
+       $email =~ s/\s*\(.*\)\s*//;
+       $email = $1 if ($email =~ m/<(.*)>/);
+       return $email;
+}
+
+my $cnt = 0;
+
+my @dirs = ($initialdir);
+while (my $dir = shift @dirs) {
+       printf "Doing dir %s ...\n", $dir if $verbose;
+
+       opendir(DIR, "$dir/.") or die "opendir $dir: $!";
+       my @subdirs = readdir(DIR);
+       closedir(DIR);
+
+       my @list = map { m/^(\d+)\.summary$/?($1):() } @subdirs;
+       push @dirs, map { m/^(\d+)$/ && -d "$dir/$1"?("$dir/$1"):() } @subdirs;
+
+       for my $bug (@list) {
+               print "Up to $cnt bugs...\n" if (++$cnt % 100 == 0 && $verbose);
+               my $stat = stat(getbugcomponent($bug,'summary',$initialdir));
+               if (not defined $stat) {
+                    print STDERR "Unable to stat $bug $!\n";
+                    next;
+               }
+               next if $stat->mtime < $time;
+               my $fdata = readbug($bug, $initialdir);
+               addbugtoindex("package", $bug, split /[\s,]+/, $fdata->{"package"});
+               addbugtoindex("tag", $bug, split /[\s,]+/, $fdata->{"keywords"});
+               addbugtoindex('submitter-email', $bug,
+                             map {lc($_->address)} getparsedaddrs($fdata->{originator}));
+               addbugtoindex("severity", $bug, $fdata->{"severity"});
+               addbugtoindex("owner", $bug,
+                             map {lc($_->address)} getparsedaddrs($fdata->{"owner"}));
+       }
+}
+
+if (not $options{quick}) {
+     # put the fast index into the slow index
+     for my $key1 (keys %fast_index) {
+         for my $key2 (keys %{$fast_index{$key1}}) {
+              $slow_index{$key1}{$key2} = $fast_index{$key1}{$key2};
+         }
+         print "Dealt with index $key1\n" if $verbose;
+     }
+}
+
+
+for my $i (@indexes) {
+       untie %{$slow_index{$i}};
+       move("$indexdest/by-$i$suffix.idx.new", "$indexdest/by-$i$suffix.idx");
+       # We do this, because old versions of touch don't support -d '@epoch'
+       system('touch','-d',"1/1/1970 UTC + ${start_time}secs","$indexdest/by-$i$suffix.idx");
+}
+
+unlink($config{spool_dir}.'/lock/gen-indices')
diff --git a/scripts/gen-indices.in b/scripts/gen-indices.in
deleted file mode 100755 (executable)
index 11775e4..0000000
+++ /dev/null
@@ -1,224 +0,0 @@
-#!/usr/bin/perl
-
-# Generates by-*.idx files for the CGI scripts
-# Copyright (c) 2005/08/03 Anthony Towns
-# GPL v2
-
-use DB_File;
-use MLDBM qw(DB_FILE Storable);
-use Fcntl qw/O_RDWR O_CREAT O_TRUNC/;
-use File::Copy;
-
-use Getopt::Long;
-use Pod::Usage;
-
-use warnings;
-use strict;
-
-use File::stat;
-use List::Util qw(min);
-
-=head1 NAME
-
-gen-indices - Generates index files for the cgi scripts
-
-=head1 SYNOPSIS
-
- gen-indices [options]
-
- Options:
-  --index-path path to index location
-  --quick update changed bugs
-  --debug, -d debugging level (Default 0)
-  --help, -h display this help
-  --man, -m display manual
-
-=head1 OPTIONS
-
-=over
-
-=itme B<--quick>
-
-Only update changed bugs
-
-=item B<--debug, -d>
-
-Debug verbosity. (Default 0)
-
-=item B<--help, -h>
-
-Display brief useage information.
-
-=item B<--man, -m>
-
-Display this manual.
-
-=back
-
-=head1 EXAMPLES
-
-
-=cut
-
-# Use portable Storable images
-$MLDBM::DumpMeth=q(portable);
-
-
-my %options = (debug           => 0,
-              help            => 0,
-              man             => 0,
-              quick           => 0,
-              index_path      => undef,
-              );
-
-GetOptions(\%options,'quick!','index_path|index-path=s','debug|d+','help|h|?','man|m') or pod2usage(2);
-pod2usage(1) if $options{help};
-pod2usage(-verbose=>2) if $options{man};
-
-use Debbugs::Config qw(:config);
-use Debbugs::Common qw(getparsedaddrs getbugcomponent lockpid);
-use Debbugs::Status qw(readbug);
-
-chdir($config{spool_dir}) or die "chdir $config{spool_dir} failed: $!";
-
-my $verbose = $options{debug};
-my $indexdest = $options{index_path} || $config{spool_dir};
-
-my $initialdir = "db-h";
-my $suffix = "";
-
-if (defined $ARGV[0] and $ARGV[0] eq "archive") {
-    $initialdir = "archive";
-    $suffix = "-arc";
-}
-
-if (not lockpid($config{spool_dir}.'/lock/gen-indices')) {
-     if ($options{quick}) {
-         # If this is a quick run, just exit
-         print STDERR "Another gen-indices is running; stopping\n" if $verbose;
-         exit 0;
-     }
-     print STDERR "Another gen-indices is running; stopping\n";
-     exit 1;
-}
-
-# NB: The reverse index is special; it's used to clean up during updates to bugs
-my @indexes = ('package', 'tag', 'severity','owner','submitter-email','status','reverse');
-my $indexes;
-my %slow_index = ();
-my %fast_index = ();
-if (not $options{quick}) {
-     # We'll trade memory for speed here if we're not doing a quick rebuild
-     for my $indexes (@indexes) {
-         $fast_index{$indexes} = {};
-     }
-     $indexes = \%fast_index;
-}
-else {
-     $indexes = \%slow_index;
-}
-my $time = undef;
-my $start_time = time;
-for my $i (@indexes) {
-       $slow_index{$i} = {};
-       if ($options{quick}) {
-            if (-e "$indexdest/by-$i${suffix}.idx") {
-                 system('cp','-a',"$indexdest/by-$i${suffix}.idx","$indexdest/by-$i${suffix}.idx.new") == 0
-                      or die "Error creating the new index";
-                 my $stat = stat("$indexdest/by-$i${suffix}.idx") or die "Unable to stat $indexdest/by-$i${suffix}.idx";
-                 $time = defined $time ? min($time,$stat->mtime) : $stat->mtime;
-            }
-            tie %{$slow_index{$i}}, MLDBM => "$indexdest/by-$i$suffix.idx.new",
-                 O_RDWR|O_CREAT, 0666
-                      or die "$0: can't create by-$i$suffix-idx.new: $!";
-       }
-       else {
-            tie %{$slow_index{$i}}, MLDBM => "$indexdest/by-$i$suffix.idx.new",
-                 O_RDWR|O_CREAT|O_TRUNC, 0666
-                      or die "$0: can't create by-$i$suffix-idx.new: $!";
-
-       }
-       $time = 0 if not defined $time;
-}
-
-sub addbugtoindex {
-     my ($index, $bug, @values) = @_;
-
-     if (exists $indexes->{reverse}{"$index $bug"}) {
-         # We do this insanity to work around a "feature" in MLDBM
-         for my $key (@{$indexes->{reverse}{"$index $bug"}}) {
-              my $temp = $indexes->{$index}{$key};
-              delete $temp->{$bug};
-              $indexes->{$index}{$key} = $temp;
-              $indexes->{$index}{"count $key"}--;
-         }
-         delete $indexes->{reverse}{"$index $bug"};
-     }
-     for my $key (@values) {
-         $indexes->{$index}->{"count $key"}++;
-         # We do this insanity to work around a "feature" in MLDBM
-         my $temp = $indexes->{$index}->{$key};
-         $temp->{$bug} = 1;
-         $indexes->{$index}->{$key} = $temp;
-     }
-     $indexes->{reverse}{"$index $bug"} = [@values];
-}
-
-sub emailfromrfc822 {
-       my $email = shift;
-       $email =~ s/\s*\(.*\)\s*//;
-       $email = $1 if ($email =~ m/<(.*)>/);
-       return $email;
-}
-
-my $cnt = 0;
-
-my @dirs = ($initialdir);
-while (my $dir = shift @dirs) {
-       printf "Doing dir %s ...\n", $dir if $verbose;
-
-       opendir(DIR, "$dir/.") or die "opendir $dir: $!";
-       my @subdirs = readdir(DIR);
-       closedir(DIR);
-
-       my @list = map { m/^(\d+)\.summary$/?($1):() } @subdirs;
-       push @dirs, map { m/^(\d+)$/ && -d "$dir/$1"?("$dir/$1"):() } @subdirs;
-
-       for my $bug (@list) {
-               print "Up to $cnt bugs...\n" if (++$cnt % 100 == 0 && $verbose);
-               my $stat = stat(getbugcomponent($bug,'summary',$initialdir));
-               if (not defined $stat) {
-                    print STDERR "Unable to stat $bug $!\n";
-                    next;
-               }
-               next if $stat->mtime < $time;
-               my $fdata = readbug($bug, $initialdir);
-               addbugtoindex("package", $bug, split /[\s,]+/, $fdata->{"package"});
-               addbugtoindex("tag", $bug, split /[\s,]+/, $fdata->{"keywords"});
-               addbugtoindex('submitter-email', $bug,
-                             map {lc($_->address)} getparsedaddrs($fdata->{originator}));
-               addbugtoindex("severity", $bug, $fdata->{"severity"});
-               addbugtoindex("owner", $bug,
-                             map {lc($_->address)} getparsedaddrs($fdata->{"owner"}));
-       }
-}
-
-if (not $options{quick}) {
-     # put the fast index into the slow index
-     for my $key1 (keys %fast_index) {
-         for my $key2 (keys %{$fast_index{$key1}}) {
-              $slow_index{$key1}{$key2} = $fast_index{$key1}{$key2};
-         }
-         print "Dealt with index $key1\n" if $verbose;
-     }
-}
-
-
-for my $i (@indexes) {
-       untie %{$slow_index{$i}};
-       move("$indexdest/by-$i$suffix.idx.new", "$indexdest/by-$i$suffix.idx");
-       # We do this, because old versions of touch don't support -d '@epoch'
-       system('touch','-d',"1/1/1970 UTC + ${start_time}secs","$indexdest/by-$i$suffix.idx");
-}
-
-unlink($config{spool_dir}.'/lock/gen-indices')
diff --git a/scripts/html-control b/scripts/html-control
new file mode 100755 (executable)
index 0000000..5dd8e0d
--- /dev/null
@@ -0,0 +1,101 @@
+#!/usr/bin/perl
+# $Id: html-control.in,v 1.12 2004/10/26 14:00:05 cjwatson Exp $
+
+use POSIX qw(strftime tzset ENOENT);
+$ENV{"TZ"} = 'UTC';
+tzset();
+
+$config_path = '/etc/debbugs';
+$lib_path = '/usr/lib/debbugs';
+
+require("$config_path/config");
+require("$lib_path/errorlib");
+$ENV{'PATH'} = $lib_path.':'.$ENV{'PATH'};
+
+chdir("$gSpoolDir") || die "chdir spool: $!\n";
+#push(@INC,"$lib_path");
+
+&filelock("html.fcntl-lock");
+
+unlink("html-data.gz") || $!==&ENOENT or &quit("remove html-data.gz: $!");
+
+sub nonawful ($) {
+    rename("stamp.html.run","stamp.html") or warn "warning: put back stamp.html: $!";
+    &quit($_[0]);
+}
+
+if (open(US,'updateseqs') && -f 'stamp.html') {
+    chop($lastmain=<US>);
+    chop($lastsub=<US>);
+    close(US);
+
+    $lastsub++;
+    $args= "-diff -stampfile=stamp.html.run";
+    rename("stamp.html","stamp.html.run") or &quit("rename stamp.html: $!");
+} else {
+    $lastsub=0;
+    $lastmain = strftime "%Y%m%d%H%M%S", localtime;
+    $args= '-full';
+    unlink('stamp.html') || $!==&ENOENT or &quit("excise stale stamp.html: $!");
+}
+
+open(X,">stamp.html.new") or &quit("stamp.html.new: $!");
+close(X) or &quit("close stamp.html.new: $!");
+
+open(US,'>updateseqs.new') || &quit("create updateseqs.new: $!");
+print(US "$lastmain\n$lastsub\n") || &quit("write updateseqs.new: $!");
+close(US) || &quit("close updateseqs.new: $!");
+rename('updateseqs.new','updateseqs') or nonawful("install updateseqs: $!");
+
+sub runshell ($&) {
+    my ($cmd,$errhref) = @_;
+    print "xx $cmd\n";
+    system $cmd;
+    !$? && !length($stderr) or &$errhref("$cmd failed - gave $? / $stderr");
+}
+
+$sequences="$lastmain $lastsub";
+$seqmid= $sequences; $seqmid =~ y/ /-/;
+open(MM,">html-data.mail") or nonawful("open html-data.mail: $!");
+if ( length( $gListDomain ) > 0 && length( $gMirrorList ) > 0 ) {
+print(MM <<END
+From: $gMaintainerEmail ($gProject $gBug Tracking System)
+To: $gMirrorList\@$gListDomain
+Subject: $gProject $gBugs autoupdate 259012
+Message-ID: <handle.htmlup.$seqmid\@$gEmailDomain>
+X-$gProject-PR: update $sequences
+
+END
+      ) or nonawful("write html-data.mail header: $!");
+} else {
+print(MM <<END
+From: $gMaintainerEmail ($gProject $gBug Tracking System)
+To: $gMaintainerEmail
+Subject: $gProject $gBugs autoupdate 259012
+Message-ID: <handle.htmlup.$seqmid\@$gEmailDomain>
+X-$gProject-PR: update $sequences
+
+END
+      ) or nonawful("write html-data.mail header: $!");
+}
+close(MM) or nonawful("close html-data.mail: $!");
+
+runshell("$lib_path/db2html $args 2>&1 >html-data $gWebDir/db",
+         sub { &nonawful; });
+runshell("$lib_path/html-install $gWebDir/db <html-data 2>&1",sub { &quit; });
+#runshell("gzip -9 html-data 2>&1",sub { &quit; });
+#runshell("btoa 2>&1 <html-data.gz >>html-data.mail",sub { &quit; });
+#runshell('2>&1 '.join(' ',('/usr/lib/sendmail','-f'."$gMaintainerEmail")).' -oem -oi -t <html-data.mail',
+#         sub { &quit; });
+
+rename("stamp.html.new","stamp.html") or &quit("install new stamp.html: $!");
+
+unlink("html-data") or warn "remove html-data: $!";
+#unlink("html-data.gz") or warn "remove html-data.gz: $!";
+#unlink("html-data.mail") or warn "remove html-data.mail: $!";
+unlink("stamp.html.run") || $!==&ENOENT or warn "remove stamp.html.run: $!";
+
+print "sequences $lastmain $lastsub\n";
+
+&unfilelock();
+exit(0);
diff --git a/scripts/html-control.in b/scripts/html-control.in
deleted file mode 100755 (executable)
index 5dd8e0d..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-#!/usr/bin/perl
-# $Id: html-control.in,v 1.12 2004/10/26 14:00:05 cjwatson Exp $
-
-use POSIX qw(strftime tzset ENOENT);
-$ENV{"TZ"} = 'UTC';
-tzset();
-
-$config_path = '/etc/debbugs';
-$lib_path = '/usr/lib/debbugs';
-
-require("$config_path/config");
-require("$lib_path/errorlib");
-$ENV{'PATH'} = $lib_path.':'.$ENV{'PATH'};
-
-chdir("$gSpoolDir") || die "chdir spool: $!\n";
-#push(@INC,"$lib_path");
-
-&filelock("html.fcntl-lock");
-
-unlink("html-data.gz") || $!==&ENOENT or &quit("remove html-data.gz: $!");
-
-sub nonawful ($) {
-    rename("stamp.html.run","stamp.html") or warn "warning: put back stamp.html: $!";
-    &quit($_[0]);
-}
-
-if (open(US,'updateseqs') && -f 'stamp.html') {
-    chop($lastmain=<US>);
-    chop($lastsub=<US>);
-    close(US);
-
-    $lastsub++;
-    $args= "-diff -stampfile=stamp.html.run";
-    rename("stamp.html","stamp.html.run") or &quit("rename stamp.html: $!");
-} else {
-    $lastsub=0;
-    $lastmain = strftime "%Y%m%d%H%M%S", localtime;
-    $args= '-full';
-    unlink('stamp.html') || $!==&ENOENT or &quit("excise stale stamp.html: $!");
-}
-
-open(X,">stamp.html.new") or &quit("stamp.html.new: $!");
-close(X) or &quit("close stamp.html.new: $!");
-
-open(US,'>updateseqs.new') || &quit("create updateseqs.new: $!");
-print(US "$lastmain\n$lastsub\n") || &quit("write updateseqs.new: $!");
-close(US) || &quit("close updateseqs.new: $!");
-rename('updateseqs.new','updateseqs') or nonawful("install updateseqs: $!");
-
-sub runshell ($&) {
-    my ($cmd,$errhref) = @_;
-    print "xx $cmd\n";
-    system $cmd;
-    !$? && !length($stderr) or &$errhref("$cmd failed - gave $? / $stderr");
-}
-
-$sequences="$lastmain $lastsub";
-$seqmid= $sequences; $seqmid =~ y/ /-/;
-open(MM,">html-data.mail") or nonawful("open html-data.mail: $!");
-if ( length( $gListDomain ) > 0 && length( $gMirrorList ) > 0 ) {
-print(MM <<END
-From: $gMaintainerEmail ($gProject $gBug Tracking System)
-To: $gMirrorList\@$gListDomain
-Subject: $gProject $gBugs autoupdate 259012
-Message-ID: <handle.htmlup.$seqmid\@$gEmailDomain>
-X-$gProject-PR: update $sequences
-
-END
-      ) or nonawful("write html-data.mail header: $!");
-} else {
-print(MM <<END
-From: $gMaintainerEmail ($gProject $gBug Tracking System)
-To: $gMaintainerEmail
-Subject: $gProject $gBugs autoupdate 259012
-Message-ID: <handle.htmlup.$seqmid\@$gEmailDomain>
-X-$gProject-PR: update $sequences
-
-END
-      ) or nonawful("write html-data.mail header: $!");
-}
-close(MM) or nonawful("close html-data.mail: $!");
-
-runshell("$lib_path/db2html $args 2>&1 >html-data $gWebDir/db",
-         sub { &nonawful; });
-runshell("$lib_path/html-install $gWebDir/db <html-data 2>&1",sub { &quit; });
-#runshell("gzip -9 html-data 2>&1",sub { &quit; });
-#runshell("btoa 2>&1 <html-data.gz >>html-data.mail",sub { &quit; });
-#runshell('2>&1 '.join(' ',('/usr/lib/sendmail','-f'."$gMaintainerEmail")).' -oem -oi -t <html-data.mail',
-#         sub { &quit; });
-
-rename("stamp.html.new","stamp.html") or &quit("install new stamp.html: $!");
-
-unlink("html-data") or warn "remove html-data: $!";
-#unlink("html-data.gz") or warn "remove html-data.gz: $!";
-#unlink("html-data.mail") or warn "remove html-data.mail: $!";
-unlink("stamp.html.run") || $!==&ENOENT or warn "remove stamp.html.run: $!";
-
-print "sequences $lastmain $lastsub\n";
-
-&unfilelock();
-exit(0);
diff --git a/scripts/html-install b/scripts/html-install
new file mode 100755 (executable)
index 0000000..bb6b04d
--- /dev/null
@@ -0,0 +1,120 @@
+#!/usr/bin/perl
+# $Id: html-install.in,v 1.4 2002/11/17 22:45:16 cjwatson Exp $
+# Takes 1 argument - directory tree to install into
+# Tree _must_ be synch'd with one used by db2html to generate file
+
+use POSIX;
+$config_path = '/etc/debbugs';
+
+require("$config_path/config");
+$ENV{'PATH'} = $lib_path.':'.$ENV{'PATH'};
+
+$dirtree= shift(@ARGV);
+defined($dirtree) or die 'usage';
+chdir $dirtree or die $!;
+
+$filenamere= '[0-9a-z]{2}/[0-9a-z][-+_:,.0-9a-zA-Z]*';
+
+opendir(D,".") or die " opendir: $!";
+while ($dir=readdir(D)) {
+    next if $dir =~ m/^\.\.?$/;
+    if (-f $dir) {
+        $remove{$dir}= 1;
+    } else {
+        opendir(E,"$dir") or die " opendir $dir: $!";
+        while ($_=readdir(E)) {
+            next if $_ =~ m/^\.\.?$/;
+            $remove{"$dir/$_"}= 1;
+        }
+        closedir(E) or die " closedir $dir: $!";
+        $rmdir{$dir}= 1;
+    }
+}
+closedir(D) or die " closedir: $!";
+
+while(<>) {
+    chomp;
+    if (m/^end$/) {
+       print "end, removing\n";
+        for $k (keys %remove) { unlink($k) || $!==&ENOENT or die "$k: $!"; }
+        for $k (keys %rmdir) { rmdir($k) || $!==&ENOTEMPTY || $!==EEXIST or die "$k: $!"; }
+        exit 0;
+    } elsif (s/^progress //) {
+        y/-+:._!#=,0-9a-zA-Z //cd;
+        print " progress $_\n";
+    } elsif (m/^preserve ($filenamere)$/o) {
+        delete $remove{$1};
+        delete $remove{"$1.ref"};
+        print " preserve $1\n";
+    } elsif (m/^(file|diff) (\d+) (ref|def|non) ($filenamere)$/o) {
+        $filediff= $1; $linestodo= $2; $ii= $3; $file= $4;
+        print " $filediff $ii $file\n";
+        delete $remove{$file};
+        delete $remove{"$file.ref"} if $ii eq 'ref';
+        $file =~ m,^(..)/, or die $file;
+        mkdir($1,0777) || $!==EEXIST or die $!;
+        $tranfile= $file;
+        $tranfile.= '.ref' if $ii eq 'ref';
+        open(DT,"> recv.tmp") or die $!;
+        if ($filediff eq 'diff') { print DT "r $tranfile\n" or die $!; }
+        $indata= 0;
+        while ($linestodo--) {
+            $z=<STDIN>;
+            if ($filediff eq 'diff') {
+                if ($indata) { $indata=0 if $incmd && m/^\.$/; }
+                elsif ($z =~ m/^[0-9,]+[ac]/) { $indata= 1; }
+                elsif ($z !~ m/^[0-9,]+[ds]/) { die "SECURITY $file >$z<"; }
+            }
+            print DT $z or die $!;
+        }
+        if ($filediff eq 'diff') { print DT "w new.tmp\nq\n" or die $!; }
+        close(DT) or die $!;
+        ($z=<STDIN>) eq "that$filediff $file\n" or die die "not confirmed >$z<";
+        if ($filediff eq 'diff') {
+            $q= `ed -s <recv.tmp 2>&1`;
+            length($q) || $? and die "ed $q $?";
+            rename("new.tmp","$tranfile") or die "$tranfile $!";
+            unlink("recv.tmp") or die $!;
+        } else {
+            rename("recv.tmp","$tranfile") or die "$tranfile $!";
+        }
+        if ($ii eq 'ref') {
+            open(I,"$tranfile") or die $!;
+            open(O,"> ref.tmp") or die $!;
+            while (<I>) {
+                if (m/^\<\!\-\-ii (\d+)\-\-\>$/) {
+                    defined($iival{$1}) or die "$tranfile $1";
+                    print O $iival{$1} or die $!;
+                } else {
+                    print O or die $!;
+                }
+            }
+            close(I) or die $!;
+            close(O) or die $!;
+            rename("ref.tmp","$file") or die $!;
+        } elsif ($ii eq 'def') {
+            open(I,"$file") or die $!;
+            undef $cdef; $ctext= '';
+            while (<I>) {
+                if (s/^\<\!\-\-iid (\d+)\-\-\>//) {
+                    defined($cdef) and die $file;
+                    $cdef= $1;
+                    $ctext= $_;
+                } elsif (s/\<\!\-\-\/iid\-\-\>\n$//) {
+                    defined($cdef) or die $file;
+                    $iival{$cdef}= $ctext.$_."\n";
+                    $ctext=''; undef $cdef;
+                } else {
+                    $ctext.= $_ if defined($cdef);
+                }
+            }
+        }
+    } elsif (m/^noremoves$/) {
+       print "noremoves\n";
+        exit 0;
+    } else {
+        die " huh ? $_";
+    }
+}
+
+die "eof $!";
diff --git a/scripts/html-install.in b/scripts/html-install.in
deleted file mode 100755 (executable)
index bb6b04d..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-#!/usr/bin/perl
-# $Id: html-install.in,v 1.4 2002/11/17 22:45:16 cjwatson Exp $
-# Takes 1 argument - directory tree to install into
-# Tree _must_ be synch'd with one used by db2html to generate file
-
-use POSIX;
-$config_path = '/etc/debbugs';
-
-require("$config_path/config");
-$ENV{'PATH'} = $lib_path.':'.$ENV{'PATH'};
-
-$dirtree= shift(@ARGV);
-defined($dirtree) or die 'usage';
-chdir $dirtree or die $!;
-
-$filenamere= '[0-9a-z]{2}/[0-9a-z][-+_:,.0-9a-zA-Z]*';
-
-opendir(D,".") or die " opendir: $!";
-while ($dir=readdir(D)) {
-    next if $dir =~ m/^\.\.?$/;
-    if (-f $dir) {
-        $remove{$dir}= 1;
-    } else {
-        opendir(E,"$dir") or die " opendir $dir: $!";
-        while ($_=readdir(E)) {
-            next if $_ =~ m/^\.\.?$/;
-            $remove{"$dir/$_"}= 1;
-        }
-        closedir(E) or die " closedir $dir: $!";
-        $rmdir{$dir}= 1;
-    }
-}
-closedir(D) or die " closedir: $!";
-
-while(<>) {
-    chomp;
-    if (m/^end$/) {
-       print "end, removing\n";
-        for $k (keys %remove) { unlink($k) || $!==&ENOENT or die "$k: $!"; }
-        for $k (keys %rmdir) { rmdir($k) || $!==&ENOTEMPTY || $!==EEXIST or die "$k: $!"; }
-        exit 0;
-    } elsif (s/^progress //) {
-        y/-+:._!#=,0-9a-zA-Z //cd;
-        print " progress $_\n";
-    } elsif (m/^preserve ($filenamere)$/o) {
-        delete $remove{$1};
-        delete $remove{"$1.ref"};
-        print " preserve $1\n";
-    } elsif (m/^(file|diff) (\d+) (ref|def|non) ($filenamere)$/o) {
-        $filediff= $1; $linestodo= $2; $ii= $3; $file= $4;
-        print " $filediff $ii $file\n";
-        delete $remove{$file};
-        delete $remove{"$file.ref"} if $ii eq 'ref';
-        $file =~ m,^(..)/, or die $file;
-        mkdir($1,0777) || $!==EEXIST or die $!;
-        $tranfile= $file;
-        $tranfile.= '.ref' if $ii eq 'ref';
-        open(DT,"> recv.tmp") or die $!;
-        if ($filediff eq 'diff') { print DT "r $tranfile\n" or die $!; }
-        $indata= 0;
-        while ($linestodo--) {
-            $z=<STDIN>;
-            if ($filediff eq 'diff') {
-                if ($indata) { $indata=0 if $incmd && m/^\.$/; }
-                elsif ($z =~ m/^[0-9,]+[ac]/) { $indata= 1; }
-                elsif ($z !~ m/^[0-9,]+[ds]/) { die "SECURITY $file >$z<"; }
-            }
-            print DT $z or die $!;
-        }
-        if ($filediff eq 'diff') { print DT "w new.tmp\nq\n" or die $!; }
-        close(DT) or die $!;
-        ($z=<STDIN>) eq "that$filediff $file\n" or die die "not confirmed >$z<";
-        if ($filediff eq 'diff') {
-            $q= `ed -s <recv.tmp 2>&1`;
-            length($q) || $? and die "ed $q $?";
-            rename("new.tmp","$tranfile") or die "$tranfile $!";
-            unlink("recv.tmp") or die $!;
-        } else {
-            rename("recv.tmp","$tranfile") or die "$tranfile $!";
-        }
-        if ($ii eq 'ref') {
-            open(I,"$tranfile") or die $!;
-            open(O,"> ref.tmp") or die $!;
-            while (<I>) {
-                if (m/^\<\!\-\-ii (\d+)\-\-\>$/) {
-                    defined($iival{$1}) or die "$tranfile $1";
-                    print O $iival{$1} or die $!;
-                } else {
-                    print O or die $!;
-                }
-            }
-            close(I) or die $!;
-            close(O) or die $!;
-            rename("ref.tmp","$file") or die $!;
-        } elsif ($ii eq 'def') {
-            open(I,"$file") or die $!;
-            undef $cdef; $ctext= '';
-            while (<I>) {
-                if (s/^\<\!\-\-iid (\d+)\-\-\>//) {
-                    defined($cdef) and die $file;
-                    $cdef= $1;
-                    $ctext= $_;
-                } elsif (s/\<\!\-\-\/iid\-\-\>\n$//) {
-                    defined($cdef) or die $file;
-                    $iival{$cdef}= $ctext.$_."\n";
-                    $ctext=''; undef $cdef;
-                } else {
-                    $ctext.= $_ if defined($cdef);
-                }
-            }
-        }
-    } elsif (m/^noremoves$/) {
-       print "noremoves\n";
-        exit 0;
-    } else {
-        die " huh ? $_";
-    }
-}
-
-die "eof $!";
diff --git a/scripts/mailsummary b/scripts/mailsummary
new file mode 100755 (executable)
index 0000000..1ed2e0b
--- /dev/null
@@ -0,0 +1,83 @@
+#!/usr/bin/perl
+# $Id: mailsummary.in,v 1.11 2003/04/28 23:51:15 cjwatson Exp $
+
+$config_path = '/etc/debbugs';
+$lib_path = '/usr/lib/debbugs';
+
+require("$config_path/config");
+require("$lib_path/errorlib");
+$ENV{'PATH'} = $lib_path.':'.$ENV{'PATH'};
+
+chdir("$gSpoolDir") || die "chdir spool: $!\n";
+
+#open(DEBUG,">&4");
+
+if ($ARGV[0] eq 'undone') {
+    $vdef= "(no outstanding $gBug reports on file, or problem running script)\n";
+    $subject= "Unanswered problem reports by date";
+    $intro=
+"The following problem reports have not yet been marked as `taken up\' by a
+message to done\@$gEmailDomain or or `forwarded\' by a
+message to forwarded\@$gEmailDomain."
+    ;
+} elsif ($ARGV[0] eq 'bymaint') {
+    $vdef= "(no outstanding $gBug reports on file, or problem running script)\n";
+    $subject= "Unanswered problem reports by maintainer and package";
+    $intro=
+"The following problem reports have not yet been marked as `taken up\' by a
+message to done\@$gEmailDomain or or `forwarded\' by a
+message to forwarded\@$gEmailDomain.
+The maintainer listed against each package is derived from the Maintainer
+field of the package found in the development tree; there is an override file
+that can be amended to get the right results if you have taken over a package
+and do not expect to issue a new version soon.
+
+Variant versions of the Maintainer field for the same actual package
+maintainer will be listed separately.
+
+Maintainers with few outstanding $gBugs appear first, to avoid those with few
+$gBugs being lost deep in the message.
+"
+    ;
+} elsif ($ARGV[0] eq 'veryold') {
+    $vdef= '';
+    $subject= "Overdue problem reports by age";
+    $intro=
+"The following problem reports are very old but have not yet been marked
+as `taken up\' by a message to done\@$gEmailDomain as forwarded
+to a developer by CCing a message to forwarded\@$gEmailDomain.
+Please help ensure that these $gBugs are dealt with quickly, even if you
+are not the package maintainer in question.  (NB a full list of outstanding
+$gBug reports is posted periodically - this is a partial list only!)
+"
+} else {
+    die "urgk, wrong argument @ARGV";
+}
+
+$v=`$lib_path/summary $ARGV[0]`; $? && die "undone failed $?: $!\n";
+
+$v= $vdef if $v eq '';
+exit 0 if $v eq '';
+
+open(D, '| '.join(' ',('/usr/lib/sendmail','-f'.$gMaintainerEmail)).' -odq -oem -oi -t') ||
+    die "start sendmail: $!";
+
+print D <<END || die "complete sendmail";
+From: $gMaintainerEmail ($gProject $gBug Tracking System)
+To: $gSummaryList\@$gListDomain
+Subject: $subject
+
+$intro
+$v
+Every Tuesday, the listing by package maintainer is posted.
+Every Friday, the listing by age of the report is posted.
+
+Please see the documentation for more information about how to
+use the $gBug tracking system.  It is available on the WWW at
+<A HREF=\"http://$gWebDomain/txt/\">$gWebDomain/txt</A>
+END
+
+close(D);
+$? && die "sendmail failed $?: $!\n";
+
+print length($v)," bytes of summary posted.\n";
diff --git a/scripts/mailsummary.in b/scripts/mailsummary.in
deleted file mode 100755 (executable)
index 1ed2e0b..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-#!/usr/bin/perl
-# $Id: mailsummary.in,v 1.11 2003/04/28 23:51:15 cjwatson Exp $
-
-$config_path = '/etc/debbugs';
-$lib_path = '/usr/lib/debbugs';
-
-require("$config_path/config");
-require("$lib_path/errorlib");
-$ENV{'PATH'} = $lib_path.':'.$ENV{'PATH'};
-
-chdir("$gSpoolDir") || die "chdir spool: $!\n";
-
-#open(DEBUG,">&4");
-
-if ($ARGV[0] eq 'undone') {
-    $vdef= "(no outstanding $gBug reports on file, or problem running script)\n";
-    $subject= "Unanswered problem reports by date";
-    $intro=
-"The following problem reports have not yet been marked as `taken up\' by a
-message to done\@$gEmailDomain or or `forwarded\' by a
-message to forwarded\@$gEmailDomain."
-    ;
-} elsif ($ARGV[0] eq 'bymaint') {
-    $vdef= "(no outstanding $gBug reports on file, or problem running script)\n";
-    $subject= "Unanswered problem reports by maintainer and package";
-    $intro=
-"The following problem reports have not yet been marked as `taken up\' by a
-message to done\@$gEmailDomain or or `forwarded\' by a
-message to forwarded\@$gEmailDomain.
-The maintainer listed against each package is derived from the Maintainer
-field of the package found in the development tree; there is an override file
-that can be amended to get the right results if you have taken over a package
-and do not expect to issue a new version soon.
-
-Variant versions of the Maintainer field for the same actual package
-maintainer will be listed separately.
-
-Maintainers with few outstanding $gBugs appear first, to avoid those with few
-$gBugs being lost deep in the message.
-"
-    ;
-} elsif ($ARGV[0] eq 'veryold') {
-    $vdef= '';
-    $subject= "Overdue problem reports by age";
-    $intro=
-"The following problem reports are very old but have not yet been marked
-as `taken up\' by a message to done\@$gEmailDomain as forwarded
-to a developer by CCing a message to forwarded\@$gEmailDomain.
-Please help ensure that these $gBugs are dealt with quickly, even if you
-are not the package maintainer in question.  (NB a full list of outstanding
-$gBug reports is posted periodically - this is a partial list only!)
-"
-} else {
-    die "urgk, wrong argument @ARGV";
-}
-
-$v=`$lib_path/summary $ARGV[0]`; $? && die "undone failed $?: $!\n";
-
-$v= $vdef if $v eq '';
-exit 0 if $v eq '';
-
-open(D, '| '.join(' ',('/usr/lib/sendmail','-f'.$gMaintainerEmail)).' -odq -oem -oi -t') ||
-    die "start sendmail: $!";
-
-print D <<END || die "complete sendmail";
-From: $gMaintainerEmail ($gProject $gBug Tracking System)
-To: $gSummaryList\@$gListDomain
-Subject: $subject
-
-$intro
-$v
-Every Tuesday, the listing by package maintainer is posted.
-Every Friday, the listing by age of the report is posted.
-
-Please see the documentation for more information about how to
-use the $gBug tracking system.  It is available on the WWW at
-<A HREF=\"http://$gWebDomain/txt/\">$gWebDomain/txt</A>
-END
-
-close(D);
-$? && die "sendmail failed $?: $!\n";
-
-print length($v)," bytes of summary posted.\n";
diff --git a/scripts/process b/scripts/process
new file mode 100755 (executable)
index 0000000..e17127b
--- /dev/null
@@ -0,0 +1,1169 @@
+#!/usr/bin/perl
+# $Id: process.in,v 1.109 2006/02/09 22:02:04 don Exp $
+#
+# Usage: process nn
+# Temps:  incoming/Pnn
+
+use warnings;
+use strict;
+
+use POSIX qw(strftime);
+
+use IO::File;
+
+use MIME::Parser;
+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 Debbugs::CGI qw(html_escape bug_url);
+
+use Debbugs::Log qw(:misc);
+
+use Debbugs::Text qw(:templates);
+
+use Debbugs::Status qw(:versions);
+use Debbugs::Config qw(:globals :config);
+
+chdir( "$gSpoolDir" ) || die "chdir spool: $!\n";
+
+#open(DEBUG,"> /tmp/debbugs.debug");
+umask(002);
+open DEBUG, ">/dev/null";
+
+my $intdate = time or quit("failed to get time: $!");
+
+$_=shift;
+m/^([BMQFDUL])(\d*)\.\d+$/ or quit("bad argument: $_");
+my $codeletter= $1;
+my $tryref= length($2) ? $2 : -1;
+my $nn= $_;
+
+if (!rename("incoming/G$nn","incoming/P$nn")) 
+{
+    $_=$!.'';  m/no such file or directory/i && exit 0;
+    &quit("renaming to lock: $!");
+}
+
+my $baddress= 'submit' if $codeletter eq 'B';
+$baddress= 'maintonly' if $codeletter eq 'M';
+$baddress= 'quiet' if $codeletter eq 'Q';
+$baddress= 'forwarded' if $codeletter eq 'F';
+$baddress= 'done' if $codeletter eq 'D';
+$baddress= 'submitter' if $codeletter eq 'U';
+bug_list_forward($nn) if $codeletter eq 'L';
+$baddress || &quit("bad codeletter $codeletter");
+my $baddressroot= $baddress;
+$baddress= "$tryref-$baddress" if $tryref>=0;
+
+open(M,"incoming/P$nn");
+my @log=<M>;
+close(M);
+
+my @msg = @log;
+chomp @msg;
+
+print DEBUG "###\n",join("##\n",@msg),"\n###\n";
+
+my $tdate = strftime "%a, %d %h %Y %T +0000", gmtime;
+my $fwd= <<END;
+Received: via spool by $baddress\@$gEmailDomain id=$nn
+          (code $codeletter ref $tryref); $tdate
+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");
+my $entity = eval { $parser->parse_data(join('',@log)) };
+
+my $i;
+if ($entity and $entity->head->tags) {
+    @headerlines = @{$entity->head->header};
+    chomp @headerlines;
+
+    my $entity_body = getmailbody($entity);
+    @bodylines = map {s/\r?\n$//; $_;}
+        $entity_body ? $entity_body->as_lines() : ();
+
+    # set $i to beginning of encoded body data, so we can dump it out
+    # verbatim later
+    $i = 0;
+    ++$i while $msg[$i] =~ /./;
+} else {
+    # Legacy pre-MIME code, kept around in case MIME::Parser fails.
+    for ($i = 0; $i <= $#msg; $i++) {
+       $_ = $msg[$i];
+       last unless length($_);
+       while ($msg[$i+1] =~ m/^\s/) {
+           $i++;
+           $_ .= "\n".$msg[$i];
+       }
+       push @headerlines, $_;
+    }
+
+    @bodylines = @msg[$i..$#msg];
+}
+
+my %header;
+
+for my $hdr (@headerlines) {
+    $hdr = decode_rfc1522($hdr);
+    $_ = $hdr;
+    s/\n\s/ /g;
+    &finish if m/^x-loop: (\S+)$/i && $1 eq "$gMaintainerEmail";
+    my $ins = !m/^subject:/i && !m/^reply-to:/i && !m/^return-path:/i
+           && !m/^From / && !m/^X-Debbugs-/i;
+    $fwd .= $hdr."\n" if $ins;
+    # print DEBUG ">$_<\n";
+    if (s/^(\S+):\s*//) {
+       my $v = lc $1;
+       print DEBUG ">$v=$_<\n";
+       $header{$v} = $_;
+    } else {
+       print DEBUG "!>$_<\n";
+    }
+}
+$header{'message-id'} = '' if not defined $header{'message-id'};
+
+# remove blank lines
+shift @bodylines while @bodylines and $bodylines[0] !~ /\S/;
+
+# Strip off RFC2440-style PGP clearsigning.
+if (@bodylines and $bodylines[0] =~ /^-----BEGIN PGP SIGNED/) {
+    shift @bodylines while @bodylines and length $bodylines[0];
+    shift @bodylines while @bodylines and $bodylines[0] !~ /\S/;
+    for my $findsig (0 .. $#bodylines) {
+       if ($bodylines[$findsig] =~ /^-----BEGIN PGP SIGNATURE/) {
+           $#bodylines = $findsig - 1;
+           last;
+       }
+    }
+    map { s/^- // } @bodylines;
+}
+
+#psuedoheaders
+my %pheader;
+# extract pseudo-headers
+for my $phline (@bodylines)
+{
+    last if $phline !~ m/^([\w-]+):\s*(\S.*)/;
+    my ($fn, $fv) = ($1, $2);
+    $fv =~ s/\s*$//;
+    print DEBUG ">$fn|$fv|\n";
+    $fn = lc $fn;
+    # Don't lc owner or forwarded
+    $fv = lc $fv unless $fn =~ /^(?:owner|forwarded|usertags|version|source-version)$/;
+    $pheader{$fn} = $fv;
+    print DEBUG ">$fn~$fv<\n";
+}
+
+# Allow pseudo headers to set x-debbugs- stuff [#179340]
+for my $key (grep /X-Debbugs-.*/i, keys %pheader) {
+     $header{$key} = $pheader{$key} if not exists $header{$key};
+}
+
+$fwd .= join("\n",@msg[$i..$#msg]);
+
+print DEBUG "***\n$fwd\n***\n";
+
+if (defined $header{'resent-from'} && !defined $header{'from'}) {
+    $header{'from'} = $header{'resent-from'};
+}
+defined($header{'from'}) || &quit("no From header");
+
+my $replyto = $header{'reply-to'};
+$replyto = '' unless defined $replyto;
+$replyto =~ s/^ +//;
+$replyto =~ s/ +$//;
+unless (length $replyto) {
+    $replyto = $header{'from'};
+}
+
+my $subject = '(no subject)';
+if (!defined($header{'subject'})) 
+{
+       $brokenness.= fill_template('mail/process_broken_subject');
+
+} else { 
+    $subject= $header{'subject'}; 
+}
+
+my $ref=-1;
+$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>.");
+        &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;
+    }
+} else { 
+    &filelock('lock/-1'); 
+}
+
+# Attempt to determine which source package this is
+my $source_pr_header = '';
+my $source_package = '';
+if (defined $pheader{source}) {
+     $source_package = $pheader{source};
+}
+elsif (defined $data->{package} or defined $pheader{package}) {
+     my $pkg_src = getpkgsrc();
+     $source_package = $pkg_src->{defined $data->{package}?$data->{package}:$pheader{package}};
+}
+$source_pr_header = "X-$gProject-PR-Source: $source_package\n"
+     if defined $source_package and length $source_package;
+
+# Done and Forwarded Bugs
+if ($codeletter eq 'D' || $codeletter eq 'F') 
+{
+    if ($replyto =~ m/$gBounceFroms/o ||
+        $header{'from'} =~ m/$gBounceFroms/o)
+    {
+        print STDERR "bounce detected !  Mwaap! Mwaap!";
+        exit 1;
+    }
+    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 defined $data->{forwarded} and length($data->{forwarded});
+        $receivedat= "forwarded\@$gEmailDomain";
+        $markaswhat= 'forwarded';
+        $set_forwarded= $header{'to'};
+       # Dissallow forwarded being set to this bug tracking system
+       if (defined $set_forwarded and $set_forwarded =~ /\Q$gEmailDomain\E/) {
+            undef $set_forwarded;
+       }
+       if ( length( $gListDomain ) > 0 && length( $gForwardList ) > 0 ) {
+           push @generalcc, "$gForwardList\@$gListDomain";
+           $generalcc= "$gForwardList\@$gListDomain";
+       } else { 
+           $generalcc=''; 
+        }
+    } else { # Done
+        if (defined $data->{done} and length($data->{done}) and
+                not defined $pheader{'source-version'} and
+                not defined $pheader{'version'}) {
+            &appendlog;
+            &finish;
+        }
+        $receivedat= "done\@$gEmailDomain";
+        $markaswhat= 'done';
+        $set_done= $header{'from'};
+       if ( length( $gListDomain ) > 0 && length( $gDoneList ) > 0 ) {
+            $generalcc= "$gDoneList\@$gListDomain";
+           push @generalcc, "$gDoneList\@$gListDomain";
+       } else { 
+           $generalcc=''; 
+       }
+    }
+    if (defined $gStrongList and isstrongseverity($data->{severity})) {
+        $generalcc = join ', ', $generalcc, "$gStrongList\@$gListDomain";
+       push @generalcc,"$gStrongList\@$gListDomain";
+    }
+    if ($ref<0) {
+       &htmllog("Warning","sent",$replyto,"Message ignored.");
+       &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;
+    }
+
+    &checkmaintainers;
+
+    my @noticecc = grep($_ ne $replyto,@maintaddrs);
+    my $noticeccval.= join(', ', grep($_ ne $replyto,@maintaddrs));
+    $noticeccval =~ s/\s+\n\s+/ /g; 
+    $noticeccval =~ s/^\s+/ /; $noticeccval =~ s/\s+$//;
+
+    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 ".join(' ',@process);
+       }
+        $data->{done}= $set_done if defined($set_done);
+        $data->{forwarded}= $set_forwarded if defined($set_forwarded);
+        if ($codeletter eq 'D') {
+            $data->{keywords} = join ' ', grep $_ ne 'pending',
+                                     split ' ', $data->{keywords};
+            if (defined $pheader{'source-version'}) {
+                if ($pheader{'source-version'} !~ m/^$config{package_version_re}$/) {
+                     $brokenness .= fill_template('mail/invalid_version',
+                                                  {version => $pheader{'source-version'}},
+                                                 );
+                }
+                else {
+                     addfixedversions($data, $pheader{source}, $pheader{'source-version'}, '');
+                }
+           } elsif (defined $pheader{version}) {
+                if ($pheader{version} !~ m/^$config{package_version_re}$/) {
+                     $brokenness .= fill_template('mail/invalid_version',
+                                                  {version => $pheader{version}},
+                                                 );
+                }
+                else {
+                     addfixedversions($data, $pheader{package}, $pheader{version}, '');
+                }
+           }
+        }
+
+       # Add bug mailing list to $generalbcc as appropriate
+       # This array is used to specify bcc in the cases where we're using create_mime_message.
+       my @generalbcc = (@generalcc,@addsrcaddrs,"bugs=$ref\@$gListDomain");
+       my $generalbcc = join(', ', $generalcc, @addsrcaddrs,"bugs=$ref\@$gListDomain");
+       $generalbcc =~ s/\s+\n\s+/ /g;
+       $generalbcc =~ s/^\s+/ /; $generalbcc =~ s/\s+$//;
+       if (length $generalbcc) {$generalbcc = "Bcc: $generalbcc\n"};
+
+       writebug($ref, $data);
+
+       my $hash = get_hashname($ref);
+        open(O,"db-h/$hash/$ref.report") || &quit("read original report: $!");
+        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(
+            ["X-Loop"      => "$gMaintainerEmail",
+             From          => "$gMaintainerEmail ($gProject $gBug Tracking System)",
+              To            => "$replyto",
+              Subject       => "$gBug#$ref: marked as forwarded ($data->{subject})",
+              "Message-ID"  => "<header.$ref.$nn.ackfwdd\@$gEmailDomain>",
+              "In-Reply-To" => $header{'message-id'},
+              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
+             (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(
+            ["X-Loop"      => "$gMaintainerEmail",
+             From          => "$gMaintainerEmail ($gProject $gBug Tracking System)",
+              To            => $replyto,
+              Subject       => "$gBug#$ref: marked as done ($data->{subject})",
+              "Message-ID"  => "<handler.$ref.$nn.ackdone\@$gEmailDomain>",
+              "In-Reply-To" => $header{'message-id'},
+              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
+             (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",
+             From          => "$gMaintainerEmail ($gProject $gBug Tracking System)",
+              To            => "$data->{originator}",
+              Subject       => "$gBug#$ref closed by $markedby ($header{'subject'})",
+              "Message-ID"  => "<handler.$ref.$nn.notifdone\@$gEmailDomain>",
+              "In-Reply-To" => "$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
+             (defined($source_package) and length($source_package))?("X-$gProject-PR-Source" => $source_package):(),
+              "Reply-To"                => "$ref\@$gEmailDomain",
+              "Content-Type"            => 'text/plain; charset="utf-8"',
+             ],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;
+    }
+    &finish;
+}
+
+if ($ref<0) { # new bug report
+    if ($codeletter eq 'U') { # -submitter
+        &htmllog("Warning","sent",$replyto,"Message not forwarded.");
+       &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;
+    }
+
+    $data->{found_versions} = [];
+    $data->{fixed_versions} = [];
+
+    if (defined $pheader{source}) {
+        $data->{package} = $pheader{source};
+    } elsif (defined $pheader{package}) {
+        $data->{package} = $pheader{package};
+    } 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)",
+                        To            => $replyto,
+                        Subject       => "Message with no Package: tag cannot be processed! ($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_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'};
+    } elsif (defined($pheader{'tags'})) {
+        $data->{keywords}= $pheader{'tags'};
+    }
+    if (length($data->{keywords})) {
+        my @kws;
+        my %gkws = map { ($_, 1) } @gTags;
+        foreach my $kw (sort split(/[,\s]+/, lc($data->{keywords}))) {
+            push @kws, $kw if (defined $gkws{$kw});
+        }
+        $data->{keywords} = join(" ", @kws);
+    }
+    $data->{severity}= '';
+    if (defined($pheader{'severity'}) || defined($pheader{'priority'})) {
+       $data->{severity}= $pheader{'severity'};
+       $data->{severity}= $pheader{'priority'} unless ($data->{severity});
+       $data->{severity} =~ s/^\s*(.+)\s*$/$1/;
+
+       if (!grep($_ eq $data->{severity}, @gSeverityList, "$gDefaultSeverity")) {
+            $brokenness.= fill_template('mail/invalid_severity',
+                                       {severity=>$data->{severity}}
+                                      );
+            $data->{severity}= '';
+        }
+    }
+    if (defined($pheader{owner})) {
+        $data->{owner}= $pheader{owner};
+    }
+    if (defined($pheader{forwarded})) {
+       $data->{'forwarded-to'} = $pheader{forwarded};
+    }
+    &filelock("nextnumber.lock");
+    open(N,"nextnumber") || &quit("nextnumber: read: $!");
+    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",'');
+    $data->{originator} = $replyto;
+    $data->{date} = $intdate;
+    $data->{subject} = $subject;
+    $data->{msgid} = $header{'message-id'};
+    writebug($ref, $data);
+    # Deal with usertags
+    if (exists $pheader{usertags}) {
+        my $user = $replyto;
+        $user = $pheader{user} if exists $pheader{user};
+        $user =~ s/,.*//;
+        $user =~ s/^.*<(.*)>.*$/$1/;
+        $user =~ s/[(].*[)]//;
+        $user =~ s/^\s*(\S+)\s+.*$/$1/;
+        if ($user ne '' and Debbugs::User::is_valid_user($user)) {
+             $pheader{usertags} =~ s/(?:^\s+|\s+$)//g;
+             my %user_tags;
+             read_usertags(\%user_tags,$user);
+             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{$ref} = 1;
+                       $user_tags{$tag} = [keys %bugs_with_tag];
+                  }
+             }
+             write_usertags(\%user_tags,$user);
+        }
+        else {
+             $brokenness .= fill_template('mail/invalid_user',
+                                          {user => $user}
+                                         );
+        }
+    }
+    &overwrite("db-h/$hash/$ref.report",
+               join("\n",@msg)."\n");
+}
+
+&checkmaintainers;
+
+print DEBUG "maintainers >".join(' ',@maintaddrs)."<\n";
+
+my $orgsender= defined($header{'sender'}) ? "Original-Sender: $header{'sender'}\n" : '';
+my $newsubject= $subject;  $newsubject =~ s/^$gBug#$ref:*\s*//;
+
+my $xcchdr= $header{ 'x-debbugs-cc' } || '';
+if ($xcchdr =~ m/\S/) {
+    push(@resentccs,$xcchdr);
+    $resentccexplain.= fill_template('mail/xdebbugscc',
+                                    {xcchdr => $xcchdr},
+                                   );
+}
+
+if (@maintaddrs && ($codeletter eq 'B' || $codeletter eq 'M')) {
+    push(@resentccs,@maintaddrs);
+    $resentccexplain.= fill_template('mail/maintainercc',
+                                    {maintaddrs => \@maintaddrs,
+                                    },
+                                   );
+}
+
+@bccs = @addsrcaddrs;
+if (defined $gStrongList and isstrongseverity($data->{severity})) {
+    push @bccs, "$gStrongList\@$gListDomain";
+}
+
+# Send mail to the per bug list subscription too
+push @bccs, "bugs=$ref\@$gListDomain";
+
+if (defined $pheader{source}) {
+    # Prefix source versions with the name of the source package. They
+    # appear that way in version trees so that we can deal with binary
+    # packages moving from one source package to another.
+    if (defined $pheader{'source-version'}) {
+        if ($pheader{'source-version'} !~ m/^$config{package_version_re}$/) {
+             $brokenness .= fill_template('mail/invalid_version',
+                                          {version => $pheader{'source-version'}},
+                                         );
+        }
+        else {
+             addfoundversions($data, $pheader{source}, $pheader{'source-version'}, '');
+        }
+    } elsif (defined $pheader{version}) {
+        if ($pheader{version} !~ m/^$config{package_version_re}$/) {
+             $brokenness .= fill_template('mail/invalid_version',
+                                          {version => $pheader{version}},
+                                         );
+        }
+        else {
+             addfoundversions($data, $pheader{source}, $pheader{version}, '');
+        }
+    }
+    writebug($ref, $data);
+} elsif (defined $pheader{package}) {
+    # TODO: could handle Source-Version: by looking up the source package?
+     if (defined $pheader{version}) {
+         if ($pheader{version} !~ m/^$config{package_version_re}$/) {
+              $brokenness .= fill_template('mail/invalid_version',
+                                           {version => $pheader{version}},
+                                          );
+         }
+         else {
+              addfoundversions($data, $pheader{package}, $pheader{version}, 'binary');
+         }
+     }
+     writebug($ref, $data);
+}
+
+my $veryquiet= $codeletter eq 'Q';
+if ($codeletter eq 'M' && !@maintaddrs) {
+    $veryquiet= 1;
+    $brokenness.= fill_template('mail/invalid_maintainer',
+                               {},
+                              );
+}
+
+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"; 
+}
+
+if ($codeletter eq 'U') { # sent to -submitter
+    &htmllog("Message", "sent on", $data->{originator}, "$gBug#$ref.");
+    &sendmessage(<<END,[$data->{originator},@resentccs],[@bccs]);
+Subject: $gBug#$ref: $newsubject
+Reply-To: $replyto, $ref-quiet\@$gEmailDomain
+${orgsender}Resent-To: $data->{originator}
+${resentcc}Resent-Date: $tdate
+Resent-Message-ID: <handler.$ref.$nn\@$gEmailDomain>
+Resent-Sender: $gMaintainerEmail
+X-$gProject-PR-Message: report $ref
+X-$gProject-PR-Package: $data->{package}
+X-$gProject-PR-Keywords: $data->{keywords}
+${source_pr_header}$fwd
+END
+} elsif ($codeletter eq 'B') { # Sent to submit
+    my $report_followup = $newref ? 'report' : 'followup';
+    &htmllog($newref ? "Report" : "Information", "forwarded",
+             join(', ',"$gSubmitList\@$gListDomain",@resentccs),
+             "<code>$gBug#$ref</code>".
+             (length($data->{package})? "; Package <code>".html_escape($data->{package})."</code>" : '').
+             ".");
+    &sendmessage(<<END,["$gSubmitList\@$gListDomain",@resentccs],[@bccs]);
+Subject: $gBug#$ref: $newsubject
+Reply-To: $replyto, $ref\@$gEmailDomain
+Resent-From: $header{'from'}
+${orgsender}Resent-To: $gSubmitList\@$gListDomain
+${resentcc}Resent-Date: $tdate
+Resent-Message-ID: <handler.$ref.$nn\@$gEmailDomain>
+Resent-Sender: $gMaintainerEmail
+X-$gProject-PR-Message: $report_followup $ref
+X-$gProject-PR-Package: $data->{package}
+X-$gProject-PR-Keywords: $data->{keywords}
+${source_pr_header}$fwd
+END
+} elsif (@resentccs or @bccs) { # Quiet or Maintainer
+    # D and F done far earlier; B just done - so this must be M or Q
+    # We preserve whichever it was in the Reply-To (possibly adding
+    # the $gBug#).
+    my $report_followup = $newref ? 'report' : 'followup';
+    if (@resentccs) {
+        &htmllog($newref ? "Report" : "Information", "forwarded",
+                 $resentccval,
+                 "<code>$gBug#$ref</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>".html_escape($data->{package})."</code>" : '').
+                 ".");
+    }
+    &sendmessage(<<END,[@resentccs],[@bccs]);
+Subject: $gBug#$ref: $newsubject
+Reply-To: $replyto, $ref-$baddressroot\@$gEmailDomain
+Resent-From: $header{'from'}
+${orgsender}Resent-To: $resentccval
+Resent-Date: $tdate
+Resent-Message-ID: <handler.$ref.$nn\@$gEmailDomain>
+Resent-Sender: $gMaintainerEmail
+X-$gProject-PR-Message: $report_followup $ref
+X-$gProject-PR-Package: $data->{package}
+X-$gProject-PR-Keywords: $data->{keywords}
+${source_pr_header}$fwd
+END
+}
+
+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>".html_escape($resentccval)."</code>.".
+        $htmlbreak;
+}
+
+# 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       => $t_h{subject},
+                       "Message-ID"  => $t_h{messageid},
+                       "In-Reply-To" => $header{'message-id'},
+                        References    => $header{'message-id'},
+                        Precedence    => 'bulk',
+                       "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
+                       (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 {
+    my ($f,$v) = @_;
+    open(NEW,">$f.new") || &quit("$f.new: create: $!");
+    print(NEW "$v") || &quit("$f.new: write: $!");
+    close(NEW) || &quit("$f.new: close: $!");
+    rename("$f.new","$f") || &quit("rename $f.new to $f: $!");
+}
+
+sub appendlog {
+    my $hash = get_hashname($ref);
+    if (!open(AP,">>db-h/$hash/$ref.log")) {
+        print DEBUG "failed open log<\n";
+        print DEBUG "failed open log err $!<\n";
+        &quit("opening 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");
+    # 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 $exit;
+}
+
+&quit("wot no exit");
+
+sub htmllog {
+    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>".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): $!");
+}    
+
+sub stripbccs {
+    my $msg = shift;
+    my $ret = '';
+    my $bcc = 0;
+    while ($msg =~ s/(.*\n)//) {
+       local $_ = $1;
+       if (/^$/) {
+           $ret .= $_;
+           last;
+       }
+       if ($bcc) {
+           # strip continuation lines too
+           next if /^\s/;
+           $bcc = 0;
+       }
+       if (/^Bcc:/i) {
+           $bcc = 1;
+       } else {
+           $ret .= $_;
+       }
+    }
+    return $ret . $msg;
+}
+
+=head2 send_message
+
+     send_message($the_message,\@recipients,\@bcc,$do_not_encode)
+
+The first argument is the scalar message, the second argument is the
+arrayref of recipients, the third is the arrayref of Bcc:'ed
+recipients.
+
+The final argument turns off header encoding and the addition of the
+X-Loop header if true, defaults to false.
+
+=cut
+
+
+sub sendmessage {
+    my ($msg,$recips,$bcc,$no_encode) = @_;
+    if (not defined $recips or (!ref($recips) && $recips eq '')
+       or @$recips == 0) {
+       $recips = ['-t'];
+    }
+    # This is suboptimal. The right solution is to send headers
+    # separately from the rest of the message and encode them rather
+    # than doing this.
+    $msg = "X-Loop: $gMaintainerEmail\n" . $msg unless $no_encode;
+    # The original message received is written out in appendlog, so
+    # before writing out the other messages we've sent out, we need to
+    # RFC1522 encode the header.
+    $msg = encode_headers($msg) unless $no_encode;
+
+    my $hash = get_hashname($ref);
+    #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",
+          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): $!");
+
+    if (ref($bcc)) {
+        shift @$recips if $recips->[0] eq '-t';
+        push @$recips, @$bcc;
+    }
+
+    send_mail_message(message        => $msg,
+                     # Because we encode the headers above, we do not want to encode them here
+                     encode_headers => 0,
+                     recipients     => $recips);
+}
+
+=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;
+       m/^\s*$/ && next;
+        m/^(\S+)\s+(\S.*\S)\s*\n$/ || &quit("maintainers bogus \`$_'");
+        $a= $1; $b= $2; $a =~ y/A-Z/a-z/;
+       # use the package which is normalized to lower case; we do this because we lc the pseudo headers.
+        $maintainerof{$a}= $2;
+    }
+    close(MAINT);
+    open(MAINT,"$gMaintainerFileOverride") || die &quit("maintainers.override open: $!");
+    while (<MAINT>) {
+       m/^\n$/ && next;
+       m/^\s*$/ && next;
+        m/^(\S+)\s+(\S.*\S)\s*\n$/ || &quit("maintainers.override bogus \`$_'");
+        $a= $1; $b= $2; $a =~ y/A-Z/a-z/;
+       # use the package which is normalized to lower case; we do this because we lc the pseudo headers.
+        $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*$/;
+       ($a,$b)=($1,$2);
+       $a =~ y/A-Z/a-z/;
+       $pkgsrc{$a} = $b;
+    }
+    close(SOURCES);
+    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;
+       next unless defined $p;
+       if (defined $gSubscriptionDomain) {
+           if (defined($pkgsrc{$p})) {
+               push @addsrcaddrs, "$pkgsrc{$p}\@$gSubscriptionDomain";
+           } else {
+               push @addsrcaddrs, "$p\@$gSubscriptionDomain";
+           }
+       }
+        if (defined($maintainerof{$p})) {
+           print DEBUG "maintainer add >$p|$maintainerof{$p}<\n";
+            my $addmaint= $maintainerof{$p};
+            push(@maintaddrs,$addmaint) unless
+                $addmaint eq $replyto || grep($_ eq $addmaint, @maintaddrs);
+            $anymaintfound++;
+        } else {
+           print DEBUG "maintainer none >$p<\n";
+           push(@maintaddrs,$gUnknownMaintainerEmail) unless $anymaintnotfound;
+            $anymaintnotfound++;
+            last;
+        }
+    }
+
+    if (defined $data->{owner} and length $data->{owner}) {
+        print DEBUG "owner add >$data->{package}|$data->{owner}<\n";
+        my $addmaint = $data->{owner};
+        push(@maintaddrs, $addmaint) unless
+            $addmaint eq $replyto or grep($_ eq $addmaint, @maintaddrs);
+    }
+}
+
+=head2 bug_list_forward
+
+     bug_list_forward($spool_filename) if $codeletter eq 'L';
+
+
+Given the spool file, will forward a bug to the per bug mailing list
+subscription system.
+
+=cut
+
+sub bug_list_forward{
+     my ($bug_fn) = @_;
+     # Read the bug information and package information for passing to
+     # the mailing list
+     my ($bug_number) = $bug_fn =~ /^L(\d+)\./;
+     my ($bfound, $data)= lockreadbugmerge($bug_number);
+     my $bug_fh = IO::File->new("incoming/P$bug_fn",'r') or die "Unable to open incoming/P$bug_fn $!";
+
+     local $/ = undef;
+     my $bug_message = <$bug_fh>;
+     my ($bug_address) = $bug_message =~ /^Received: \(at ([^\)]+)\) by/;
+     my ($envelope_from) = $bug_message =~ s/\nFrom\s+([^\s]+)[^\n]+\n/\n/;
+     if (not defined $envelope_from) {
+         # Try to use the From: header or something to set it 
+          ($envelope_from) = $bug_message =~ /\nFrom:\s+(.+?)\n/;
+         # Kludgy, and should really be using a full scale header
+         # parser to do this.
+         $envelope_from =~ s/^.+?<([^>]+)>.+$/$1/;
+     }
+     my ($header,$body) = split /\n\n/, $bug_message, 2;
+     # Add X-$gProject-PR-Message: list bug_number, package name, and bug title headers
+     $header .= qq(\nX-$gProject-PR-Message: list $bug_number\n).
+         qq(X-$gProject-PR-Package: $data->{package}\n).
+              qq(X-$gProject-PR-Title: $data->{subject})
+              if defined $data;
+     print STDERR "Tried to loop me with $envelope_from\n"
+         and exit 1 if $envelope_from =~ /\Q$gListDomain\E|\Q$gEmailDomain\E/;
+     print DEBUG $envelope_from,qq(\n);
+     # If we don't have a bug address, something has gone horribly wrong.
+     print STDERR "Doesn't match: $bug_address\n" and exit 1 unless defined $bug_address;
+     $bug_address =~ s/\@.+//;
+     print DEBUG "Sending message to bugs=$bug_address\@$gListDomain\n";
+     print DEBUG $header.qq(\n\n).$body;
+     send_mail_message(message        => $header.qq(\n\n).$body,
+                      recipients     => ["bugs=$bug_address\@$gListDomain"],
+                      envelope_from  => $envelope_from,
+                      encode_headers => 0,
+                     );
+     unlink("incoming/P$bug_fn") || &quit("unlinking incoming/P$bug_fn: $!");
+     exit 0;
+}
diff --git a/scripts/process.in b/scripts/process.in
deleted file mode 100755 (executable)
index e17127b..0000000
+++ /dev/null
@@ -1,1169 +0,0 @@
-#!/usr/bin/perl
-# $Id: process.in,v 1.109 2006/02/09 22:02:04 don Exp $
-#
-# Usage: process nn
-# Temps:  incoming/Pnn
-
-use warnings;
-use strict;
-
-use POSIX qw(strftime);
-
-use IO::File;
-
-use MIME::Parser;
-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 Debbugs::CGI qw(html_escape bug_url);
-
-use Debbugs::Log qw(:misc);
-
-use Debbugs::Text qw(:templates);
-
-use Debbugs::Status qw(:versions);
-use Debbugs::Config qw(:globals :config);
-
-chdir( "$gSpoolDir" ) || die "chdir spool: $!\n";
-
-#open(DEBUG,"> /tmp/debbugs.debug");
-umask(002);
-open DEBUG, ">/dev/null";
-
-my $intdate = time or quit("failed to get time: $!");
-
-$_=shift;
-m/^([BMQFDUL])(\d*)\.\d+$/ or quit("bad argument: $_");
-my $codeletter= $1;
-my $tryref= length($2) ? $2 : -1;
-my $nn= $_;
-
-if (!rename("incoming/G$nn","incoming/P$nn")) 
-{
-    $_=$!.'';  m/no such file or directory/i && exit 0;
-    &quit("renaming to lock: $!");
-}
-
-my $baddress= 'submit' if $codeletter eq 'B';
-$baddress= 'maintonly' if $codeletter eq 'M';
-$baddress= 'quiet' if $codeletter eq 'Q';
-$baddress= 'forwarded' if $codeletter eq 'F';
-$baddress= 'done' if $codeletter eq 'D';
-$baddress= 'submitter' if $codeletter eq 'U';
-bug_list_forward($nn) if $codeletter eq 'L';
-$baddress || &quit("bad codeletter $codeletter");
-my $baddressroot= $baddress;
-$baddress= "$tryref-$baddress" if $tryref>=0;
-
-open(M,"incoming/P$nn");
-my @log=<M>;
-close(M);
-
-my @msg = @log;
-chomp @msg;
-
-print DEBUG "###\n",join("##\n",@msg),"\n###\n";
-
-my $tdate = strftime "%a, %d %h %Y %T +0000", gmtime;
-my $fwd= <<END;
-Received: via spool by $baddress\@$gEmailDomain id=$nn
-          (code $codeletter ref $tryref); $tdate
-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");
-my $entity = eval { $parser->parse_data(join('',@log)) };
-
-my $i;
-if ($entity and $entity->head->tags) {
-    @headerlines = @{$entity->head->header};
-    chomp @headerlines;
-
-    my $entity_body = getmailbody($entity);
-    @bodylines = map {s/\r?\n$//; $_;}
-        $entity_body ? $entity_body->as_lines() : ();
-
-    # set $i to beginning of encoded body data, so we can dump it out
-    # verbatim later
-    $i = 0;
-    ++$i while $msg[$i] =~ /./;
-} else {
-    # Legacy pre-MIME code, kept around in case MIME::Parser fails.
-    for ($i = 0; $i <= $#msg; $i++) {
-       $_ = $msg[$i];
-       last unless length($_);
-       while ($msg[$i+1] =~ m/^\s/) {
-           $i++;
-           $_ .= "\n".$msg[$i];
-       }
-       push @headerlines, $_;
-    }
-
-    @bodylines = @msg[$i..$#msg];
-}
-
-my %header;
-
-for my $hdr (@headerlines) {
-    $hdr = decode_rfc1522($hdr);
-    $_ = $hdr;
-    s/\n\s/ /g;
-    &finish if m/^x-loop: (\S+)$/i && $1 eq "$gMaintainerEmail";
-    my $ins = !m/^subject:/i && !m/^reply-to:/i && !m/^return-path:/i
-           && !m/^From / && !m/^X-Debbugs-/i;
-    $fwd .= $hdr."\n" if $ins;
-    # print DEBUG ">$_<\n";
-    if (s/^(\S+):\s*//) {
-       my $v = lc $1;
-       print DEBUG ">$v=$_<\n";
-       $header{$v} = $_;
-    } else {
-       print DEBUG "!>$_<\n";
-    }
-}
-$header{'message-id'} = '' if not defined $header{'message-id'};
-
-# remove blank lines
-shift @bodylines while @bodylines and $bodylines[0] !~ /\S/;
-
-# Strip off RFC2440-style PGP clearsigning.
-if (@bodylines and $bodylines[0] =~ /^-----BEGIN PGP SIGNED/) {
-    shift @bodylines while @bodylines and length $bodylines[0];
-    shift @bodylines while @bodylines and $bodylines[0] !~ /\S/;
-    for my $findsig (0 .. $#bodylines) {
-       if ($bodylines[$findsig] =~ /^-----BEGIN PGP SIGNATURE/) {
-           $#bodylines = $findsig - 1;
-           last;
-       }
-    }
-    map { s/^- // } @bodylines;
-}
-
-#psuedoheaders
-my %pheader;
-# extract pseudo-headers
-for my $phline (@bodylines)
-{
-    last if $phline !~ m/^([\w-]+):\s*(\S.*)/;
-    my ($fn, $fv) = ($1, $2);
-    $fv =~ s/\s*$//;
-    print DEBUG ">$fn|$fv|\n";
-    $fn = lc $fn;
-    # Don't lc owner or forwarded
-    $fv = lc $fv unless $fn =~ /^(?:owner|forwarded|usertags|version|source-version)$/;
-    $pheader{$fn} = $fv;
-    print DEBUG ">$fn~$fv<\n";
-}
-
-# Allow pseudo headers to set x-debbugs- stuff [#179340]
-for my $key (grep /X-Debbugs-.*/i, keys %pheader) {
-     $header{$key} = $pheader{$key} if not exists $header{$key};
-}
-
-$fwd .= join("\n",@msg[$i..$#msg]);
-
-print DEBUG "***\n$fwd\n***\n";
-
-if (defined $header{'resent-from'} && !defined $header{'from'}) {
-    $header{'from'} = $header{'resent-from'};
-}
-defined($header{'from'}) || &quit("no From header");
-
-my $replyto = $header{'reply-to'};
-$replyto = '' unless defined $replyto;
-$replyto =~ s/^ +//;
-$replyto =~ s/ +$//;
-unless (length $replyto) {
-    $replyto = $header{'from'};
-}
-
-my $subject = '(no subject)';
-if (!defined($header{'subject'})) 
-{
-       $brokenness.= fill_template('mail/process_broken_subject');
-
-} else { 
-    $subject= $header{'subject'}; 
-}
-
-my $ref=-1;
-$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>.");
-        &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;
-    }
-} else { 
-    &filelock('lock/-1'); 
-}
-
-# Attempt to determine which source package this is
-my $source_pr_header = '';
-my $source_package = '';
-if (defined $pheader{source}) {
-     $source_package = $pheader{source};
-}
-elsif (defined $data->{package} or defined $pheader{package}) {
-     my $pkg_src = getpkgsrc();
-     $source_package = $pkg_src->{defined $data->{package}?$data->{package}:$pheader{package}};
-}
-$source_pr_header = "X-$gProject-PR-Source: $source_package\n"
-     if defined $source_package and length $source_package;
-
-# Done and Forwarded Bugs
-if ($codeletter eq 'D' || $codeletter eq 'F') 
-{
-    if ($replyto =~ m/$gBounceFroms/o ||
-        $header{'from'} =~ m/$gBounceFroms/o)
-    {
-        print STDERR "bounce detected !  Mwaap! Mwaap!";
-        exit 1;
-    }
-    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 defined $data->{forwarded} and length($data->{forwarded});
-        $receivedat= "forwarded\@$gEmailDomain";
-        $markaswhat= 'forwarded';
-        $set_forwarded= $header{'to'};
-       # Dissallow forwarded being set to this bug tracking system
-       if (defined $set_forwarded and $set_forwarded =~ /\Q$gEmailDomain\E/) {
-            undef $set_forwarded;
-       }
-       if ( length( $gListDomain ) > 0 && length( $gForwardList ) > 0 ) {
-           push @generalcc, "$gForwardList\@$gListDomain";
-           $generalcc= "$gForwardList\@$gListDomain";
-       } else { 
-           $generalcc=''; 
-        }
-    } else { # Done
-        if (defined $data->{done} and length($data->{done}) and
-                not defined $pheader{'source-version'} and
-                not defined $pheader{'version'}) {
-            &appendlog;
-            &finish;
-        }
-        $receivedat= "done\@$gEmailDomain";
-        $markaswhat= 'done';
-        $set_done= $header{'from'};
-       if ( length( $gListDomain ) > 0 && length( $gDoneList ) > 0 ) {
-            $generalcc= "$gDoneList\@$gListDomain";
-           push @generalcc, "$gDoneList\@$gListDomain";
-       } else { 
-           $generalcc=''; 
-       }
-    }
-    if (defined $gStrongList and isstrongseverity($data->{severity})) {
-        $generalcc = join ', ', $generalcc, "$gStrongList\@$gListDomain";
-       push @generalcc,"$gStrongList\@$gListDomain";
-    }
-    if ($ref<0) {
-       &htmllog("Warning","sent",$replyto,"Message ignored.");
-       &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;
-    }
-
-    &checkmaintainers;
-
-    my @noticecc = grep($_ ne $replyto,@maintaddrs);
-    my $noticeccval.= join(', ', grep($_ ne $replyto,@maintaddrs));
-    $noticeccval =~ s/\s+\n\s+/ /g; 
-    $noticeccval =~ s/^\s+/ /; $noticeccval =~ s/\s+$//;
-
-    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 ".join(' ',@process);
-       }
-        $data->{done}= $set_done if defined($set_done);
-        $data->{forwarded}= $set_forwarded if defined($set_forwarded);
-        if ($codeletter eq 'D') {
-            $data->{keywords} = join ' ', grep $_ ne 'pending',
-                                     split ' ', $data->{keywords};
-            if (defined $pheader{'source-version'}) {
-                if ($pheader{'source-version'} !~ m/^$config{package_version_re}$/) {
-                     $brokenness .= fill_template('mail/invalid_version',
-                                                  {version => $pheader{'source-version'}},
-                                                 );
-                }
-                else {
-                     addfixedversions($data, $pheader{source}, $pheader{'source-version'}, '');
-                }
-           } elsif (defined $pheader{version}) {
-                if ($pheader{version} !~ m/^$config{package_version_re}$/) {
-                     $brokenness .= fill_template('mail/invalid_version',
-                                                  {version => $pheader{version}},
-                                                 );
-                }
-                else {
-                     addfixedversions($data, $pheader{package}, $pheader{version}, '');
-                }
-           }
-        }
-
-       # Add bug mailing list to $generalbcc as appropriate
-       # This array is used to specify bcc in the cases where we're using create_mime_message.
-       my @generalbcc = (@generalcc,@addsrcaddrs,"bugs=$ref\@$gListDomain");
-       my $generalbcc = join(', ', $generalcc, @addsrcaddrs,"bugs=$ref\@$gListDomain");
-       $generalbcc =~ s/\s+\n\s+/ /g;
-       $generalbcc =~ s/^\s+/ /; $generalbcc =~ s/\s+$//;
-       if (length $generalbcc) {$generalbcc = "Bcc: $generalbcc\n"};
-
-       writebug($ref, $data);
-
-       my $hash = get_hashname($ref);
-        open(O,"db-h/$hash/$ref.report") || &quit("read original report: $!");
-        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(
-            ["X-Loop"      => "$gMaintainerEmail",
-             From          => "$gMaintainerEmail ($gProject $gBug Tracking System)",
-              To            => "$replyto",
-              Subject       => "$gBug#$ref: marked as forwarded ($data->{subject})",
-              "Message-ID"  => "<header.$ref.$nn.ackfwdd\@$gEmailDomain>",
-              "In-Reply-To" => $header{'message-id'},
-              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
-             (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(
-            ["X-Loop"      => "$gMaintainerEmail",
-             From          => "$gMaintainerEmail ($gProject $gBug Tracking System)",
-              To            => $replyto,
-              Subject       => "$gBug#$ref: marked as done ($data->{subject})",
-              "Message-ID"  => "<handler.$ref.$nn.ackdone\@$gEmailDomain>",
-              "In-Reply-To" => $header{'message-id'},
-              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
-             (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",
-             From          => "$gMaintainerEmail ($gProject $gBug Tracking System)",
-              To            => "$data->{originator}",
-              Subject       => "$gBug#$ref closed by $markedby ($header{'subject'})",
-              "Message-ID"  => "<handler.$ref.$nn.notifdone\@$gEmailDomain>",
-              "In-Reply-To" => "$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
-             (defined($source_package) and length($source_package))?("X-$gProject-PR-Source" => $source_package):(),
-              "Reply-To"                => "$ref\@$gEmailDomain",
-              "Content-Type"            => 'text/plain; charset="utf-8"',
-             ],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;
-    }
-    &finish;
-}
-
-if ($ref<0) { # new bug report
-    if ($codeletter eq 'U') { # -submitter
-        &htmllog("Warning","sent",$replyto,"Message not forwarded.");
-       &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;
-    }
-
-    $data->{found_versions} = [];
-    $data->{fixed_versions} = [];
-
-    if (defined $pheader{source}) {
-        $data->{package} = $pheader{source};
-    } elsif (defined $pheader{package}) {
-        $data->{package} = $pheader{package};
-    } 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)",
-                        To            => $replyto,
-                        Subject       => "Message with no Package: tag cannot be processed! ($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_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'};
-    } elsif (defined($pheader{'tags'})) {
-        $data->{keywords}= $pheader{'tags'};
-    }
-    if (length($data->{keywords})) {
-        my @kws;
-        my %gkws = map { ($_, 1) } @gTags;
-        foreach my $kw (sort split(/[,\s]+/, lc($data->{keywords}))) {
-            push @kws, $kw if (defined $gkws{$kw});
-        }
-        $data->{keywords} = join(" ", @kws);
-    }
-    $data->{severity}= '';
-    if (defined($pheader{'severity'}) || defined($pheader{'priority'})) {
-       $data->{severity}= $pheader{'severity'};
-       $data->{severity}= $pheader{'priority'} unless ($data->{severity});
-       $data->{severity} =~ s/^\s*(.+)\s*$/$1/;
-
-       if (!grep($_ eq $data->{severity}, @gSeverityList, "$gDefaultSeverity")) {
-            $brokenness.= fill_template('mail/invalid_severity',
-                                       {severity=>$data->{severity}}
-                                      );
-            $data->{severity}= '';
-        }
-    }
-    if (defined($pheader{owner})) {
-        $data->{owner}= $pheader{owner};
-    }
-    if (defined($pheader{forwarded})) {
-       $data->{'forwarded-to'} = $pheader{forwarded};
-    }
-    &filelock("nextnumber.lock");
-    open(N,"nextnumber") || &quit("nextnumber: read: $!");
-    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",'');
-    $data->{originator} = $replyto;
-    $data->{date} = $intdate;
-    $data->{subject} = $subject;
-    $data->{msgid} = $header{'message-id'};
-    writebug($ref, $data);
-    # Deal with usertags
-    if (exists $pheader{usertags}) {
-        my $user = $replyto;
-        $user = $pheader{user} if exists $pheader{user};
-        $user =~ s/,.*//;
-        $user =~ s/^.*<(.*)>.*$/$1/;
-        $user =~ s/[(].*[)]//;
-        $user =~ s/^\s*(\S+)\s+.*$/$1/;
-        if ($user ne '' and Debbugs::User::is_valid_user($user)) {
-             $pheader{usertags} =~ s/(?:^\s+|\s+$)//g;
-             my %user_tags;
-             read_usertags(\%user_tags,$user);
-             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{$ref} = 1;
-                       $user_tags{$tag} = [keys %bugs_with_tag];
-                  }
-             }
-             write_usertags(\%user_tags,$user);
-        }
-        else {
-             $brokenness .= fill_template('mail/invalid_user',
-                                          {user => $user}
-                                         );
-        }
-    }
-    &overwrite("db-h/$hash/$ref.report",
-               join("\n",@msg)."\n");
-}
-
-&checkmaintainers;
-
-print DEBUG "maintainers >".join(' ',@maintaddrs)."<\n";
-
-my $orgsender= defined($header{'sender'}) ? "Original-Sender: $header{'sender'}\n" : '';
-my $newsubject= $subject;  $newsubject =~ s/^$gBug#$ref:*\s*//;
-
-my $xcchdr= $header{ 'x-debbugs-cc' } || '';
-if ($xcchdr =~ m/\S/) {
-    push(@resentccs,$xcchdr);
-    $resentccexplain.= fill_template('mail/xdebbugscc',
-                                    {xcchdr => $xcchdr},
-                                   );
-}
-
-if (@maintaddrs && ($codeletter eq 'B' || $codeletter eq 'M')) {
-    push(@resentccs,@maintaddrs);
-    $resentccexplain.= fill_template('mail/maintainercc',
-                                    {maintaddrs => \@maintaddrs,
-                                    },
-                                   );
-}
-
-@bccs = @addsrcaddrs;
-if (defined $gStrongList and isstrongseverity($data->{severity})) {
-    push @bccs, "$gStrongList\@$gListDomain";
-}
-
-# Send mail to the per bug list subscription too
-push @bccs, "bugs=$ref\@$gListDomain";
-
-if (defined $pheader{source}) {
-    # Prefix source versions with the name of the source package. They
-    # appear that way in version trees so that we can deal with binary
-    # packages moving from one source package to another.
-    if (defined $pheader{'source-version'}) {
-        if ($pheader{'source-version'} !~ m/^$config{package_version_re}$/) {
-             $brokenness .= fill_template('mail/invalid_version',
-                                          {version => $pheader{'source-version'}},
-                                         );
-        }
-        else {
-             addfoundversions($data, $pheader{source}, $pheader{'source-version'}, '');
-        }
-    } elsif (defined $pheader{version}) {
-        if ($pheader{version} !~ m/^$config{package_version_re}$/) {
-             $brokenness .= fill_template('mail/invalid_version',
-                                          {version => $pheader{version}},
-                                         );
-        }
-        else {
-             addfoundversions($data, $pheader{source}, $pheader{version}, '');
-        }
-    }
-    writebug($ref, $data);
-} elsif (defined $pheader{package}) {
-    # TODO: could handle Source-Version: by looking up the source package?
-     if (defined $pheader{version}) {
-         if ($pheader{version} !~ m/^$config{package_version_re}$/) {
-              $brokenness .= fill_template('mail/invalid_version',
-                                           {version => $pheader{version}},
-                                          );
-         }
-         else {
-              addfoundversions($data, $pheader{package}, $pheader{version}, 'binary');
-         }
-     }
-     writebug($ref, $data);
-}
-
-my $veryquiet= $codeletter eq 'Q';
-if ($codeletter eq 'M' && !@maintaddrs) {
-    $veryquiet= 1;
-    $brokenness.= fill_template('mail/invalid_maintainer',
-                               {},
-                              );
-}
-
-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"; 
-}
-
-if ($codeletter eq 'U') { # sent to -submitter
-    &htmllog("Message", "sent on", $data->{originator}, "$gBug#$ref.");
-    &sendmessage(<<END,[$data->{originator},@resentccs],[@bccs]);
-Subject: $gBug#$ref: $newsubject
-Reply-To: $replyto, $ref-quiet\@$gEmailDomain
-${orgsender}Resent-To: $data->{originator}
-${resentcc}Resent-Date: $tdate
-Resent-Message-ID: <handler.$ref.$nn\@$gEmailDomain>
-Resent-Sender: $gMaintainerEmail
-X-$gProject-PR-Message: report $ref
-X-$gProject-PR-Package: $data->{package}
-X-$gProject-PR-Keywords: $data->{keywords}
-${source_pr_header}$fwd
-END
-} elsif ($codeletter eq 'B') { # Sent to submit
-    my $report_followup = $newref ? 'report' : 'followup';
-    &htmllog($newref ? "Report" : "Information", "forwarded",
-             join(', ',"$gSubmitList\@$gListDomain",@resentccs),
-             "<code>$gBug#$ref</code>".
-             (length($data->{package})? "; Package <code>".html_escape($data->{package})."</code>" : '').
-             ".");
-    &sendmessage(<<END,["$gSubmitList\@$gListDomain",@resentccs],[@bccs]);
-Subject: $gBug#$ref: $newsubject
-Reply-To: $replyto, $ref\@$gEmailDomain
-Resent-From: $header{'from'}
-${orgsender}Resent-To: $gSubmitList\@$gListDomain
-${resentcc}Resent-Date: $tdate
-Resent-Message-ID: <handler.$ref.$nn\@$gEmailDomain>
-Resent-Sender: $gMaintainerEmail
-X-$gProject-PR-Message: $report_followup $ref
-X-$gProject-PR-Package: $data->{package}
-X-$gProject-PR-Keywords: $data->{keywords}
-${source_pr_header}$fwd
-END
-} elsif (@resentccs or @bccs) { # Quiet or Maintainer
-    # D and F done far earlier; B just done - so this must be M or Q
-    # We preserve whichever it was in the Reply-To (possibly adding
-    # the $gBug#).
-    my $report_followup = $newref ? 'report' : 'followup';
-    if (@resentccs) {
-        &htmllog($newref ? "Report" : "Information", "forwarded",
-                 $resentccval,
-                 "<code>$gBug#$ref</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>".html_escape($data->{package})."</code>" : '').
-                 ".");
-    }
-    &sendmessage(<<END,[@resentccs],[@bccs]);
-Subject: $gBug#$ref: $newsubject
-Reply-To: $replyto, $ref-$baddressroot\@$gEmailDomain
-Resent-From: $header{'from'}
-${orgsender}Resent-To: $resentccval
-Resent-Date: $tdate
-Resent-Message-ID: <handler.$ref.$nn\@$gEmailDomain>
-Resent-Sender: $gMaintainerEmail
-X-$gProject-PR-Message: $report_followup $ref
-X-$gProject-PR-Package: $data->{package}
-X-$gProject-PR-Keywords: $data->{keywords}
-${source_pr_header}$fwd
-END
-}
-
-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>".html_escape($resentccval)."</code>.".
-        $htmlbreak;
-}
-
-# 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       => $t_h{subject},
-                       "Message-ID"  => $t_h{messageid},
-                       "In-Reply-To" => $header{'message-id'},
-                        References    => $header{'message-id'},
-                        Precedence    => 'bulk',
-                       "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
-                       (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 {
-    my ($f,$v) = @_;
-    open(NEW,">$f.new") || &quit("$f.new: create: $!");
-    print(NEW "$v") || &quit("$f.new: write: $!");
-    close(NEW) || &quit("$f.new: close: $!");
-    rename("$f.new","$f") || &quit("rename $f.new to $f: $!");
-}
-
-sub appendlog {
-    my $hash = get_hashname($ref);
-    if (!open(AP,">>db-h/$hash/$ref.log")) {
-        print DEBUG "failed open log<\n";
-        print DEBUG "failed open log err $!<\n";
-        &quit("opening 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");
-    # 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 $exit;
-}
-
-&quit("wot no exit");
-
-sub htmllog {
-    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>".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): $!");
-}    
-
-sub stripbccs {
-    my $msg = shift;
-    my $ret = '';
-    my $bcc = 0;
-    while ($msg =~ s/(.*\n)//) {
-       local $_ = $1;
-       if (/^$/) {
-           $ret .= $_;
-           last;
-       }
-       if ($bcc) {
-           # strip continuation lines too
-           next if /^\s/;
-           $bcc = 0;
-       }
-       if (/^Bcc:/i) {
-           $bcc = 1;
-       } else {
-           $ret .= $_;
-       }
-    }
-    return $ret . $msg;
-}
-
-=head2 send_message
-
-     send_message($the_message,\@recipients,\@bcc,$do_not_encode)
-
-The first argument is the scalar message, the second argument is the
-arrayref of recipients, the third is the arrayref of Bcc:'ed
-recipients.
-
-The final argument turns off header encoding and the addition of the
-X-Loop header if true, defaults to false.
-
-=cut
-
-
-sub sendmessage {
-    my ($msg,$recips,$bcc,$no_encode) = @_;
-    if (not defined $recips or (!ref($recips) && $recips eq '')
-       or @$recips == 0) {
-       $recips = ['-t'];
-    }
-    # This is suboptimal. The right solution is to send headers
-    # separately from the rest of the message and encode them rather
-    # than doing this.
-    $msg = "X-Loop: $gMaintainerEmail\n" . $msg unless $no_encode;
-    # The original message received is written out in appendlog, so
-    # before writing out the other messages we've sent out, we need to
-    # RFC1522 encode the header.
-    $msg = encode_headers($msg) unless $no_encode;
-
-    my $hash = get_hashname($ref);
-    #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",
-          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): $!");
-
-    if (ref($bcc)) {
-        shift @$recips if $recips->[0] eq '-t';
-        push @$recips, @$bcc;
-    }
-
-    send_mail_message(message        => $msg,
-                     # Because we encode the headers above, we do not want to encode them here
-                     encode_headers => 0,
-                     recipients     => $recips);
-}
-
-=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;
-       m/^\s*$/ && next;
-        m/^(\S+)\s+(\S.*\S)\s*\n$/ || &quit("maintainers bogus \`$_'");
-        $a= $1; $b= $2; $a =~ y/A-Z/a-z/;
-       # use the package which is normalized to lower case; we do this because we lc the pseudo headers.
-        $maintainerof{$a}= $2;
-    }
-    close(MAINT);
-    open(MAINT,"$gMaintainerFileOverride") || die &quit("maintainers.override open: $!");
-    while (<MAINT>) {
-       m/^\n$/ && next;
-       m/^\s*$/ && next;
-        m/^(\S+)\s+(\S.*\S)\s*\n$/ || &quit("maintainers.override bogus \`$_'");
-        $a= $1; $b= $2; $a =~ y/A-Z/a-z/;
-       # use the package which is normalized to lower case; we do this because we lc the pseudo headers.
-        $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*$/;
-       ($a,$b)=($1,$2);
-       $a =~ y/A-Z/a-z/;
-       $pkgsrc{$a} = $b;
-    }
-    close(SOURCES);
-    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;
-       next unless defined $p;
-       if (defined $gSubscriptionDomain) {
-           if (defined($pkgsrc{$p})) {
-               push @addsrcaddrs, "$pkgsrc{$p}\@$gSubscriptionDomain";
-           } else {
-               push @addsrcaddrs, "$p\@$gSubscriptionDomain";
-           }
-       }
-        if (defined($maintainerof{$p})) {
-           print DEBUG "maintainer add >$p|$maintainerof{$p}<\n";
-            my $addmaint= $maintainerof{$p};
-            push(@maintaddrs,$addmaint) unless
-                $addmaint eq $replyto || grep($_ eq $addmaint, @maintaddrs);
-            $anymaintfound++;
-        } else {
-           print DEBUG "maintainer none >$p<\n";
-           push(@maintaddrs,$gUnknownMaintainerEmail) unless $anymaintnotfound;
-            $anymaintnotfound++;
-            last;
-        }
-    }
-
-    if (defined $data->{owner} and length $data->{owner}) {
-        print DEBUG "owner add >$data->{package}|$data->{owner}<\n";
-        my $addmaint = $data->{owner};
-        push(@maintaddrs, $addmaint) unless
-            $addmaint eq $replyto or grep($_ eq $addmaint, @maintaddrs);
-    }
-}
-
-=head2 bug_list_forward
-
-     bug_list_forward($spool_filename) if $codeletter eq 'L';
-
-
-Given the spool file, will forward a bug to the per bug mailing list
-subscription system.
-
-=cut
-
-sub bug_list_forward{
-     my ($bug_fn) = @_;
-     # Read the bug information and package information for passing to
-     # the mailing list
-     my ($bug_number) = $bug_fn =~ /^L(\d+)\./;
-     my ($bfound, $data)= lockreadbugmerge($bug_number);
-     my $bug_fh = IO::File->new("incoming/P$bug_fn",'r') or die "Unable to open incoming/P$bug_fn $!";
-
-     local $/ = undef;
-     my $bug_message = <$bug_fh>;
-     my ($bug_address) = $bug_message =~ /^Received: \(at ([^\)]+)\) by/;
-     my ($envelope_from) = $bug_message =~ s/\nFrom\s+([^\s]+)[^\n]+\n/\n/;
-     if (not defined $envelope_from) {
-         # Try to use the From: header or something to set it 
-          ($envelope_from) = $bug_message =~ /\nFrom:\s+(.+?)\n/;
-         # Kludgy, and should really be using a full scale header
-         # parser to do this.
-         $envelope_from =~ s/^.+?<([^>]+)>.+$/$1/;
-     }
-     my ($header,$body) = split /\n\n/, $bug_message, 2;
-     # Add X-$gProject-PR-Message: list bug_number, package name, and bug title headers
-     $header .= qq(\nX-$gProject-PR-Message: list $bug_number\n).
-         qq(X-$gProject-PR-Package: $data->{package}\n).
-              qq(X-$gProject-PR-Title: $data->{subject})
-              if defined $data;
-     print STDERR "Tried to loop me with $envelope_from\n"
-         and exit 1 if $envelope_from =~ /\Q$gListDomain\E|\Q$gEmailDomain\E/;
-     print DEBUG $envelope_from,qq(\n);
-     # If we don't have a bug address, something has gone horribly wrong.
-     print STDERR "Doesn't match: $bug_address\n" and exit 1 unless defined $bug_address;
-     $bug_address =~ s/\@.+//;
-     print DEBUG "Sending message to bugs=$bug_address\@$gListDomain\n";
-     print DEBUG $header.qq(\n\n).$body;
-     send_mail_message(message        => $header.qq(\n\n).$body,
-                      recipients     => ["bugs=$bug_address\@$gListDomain"],
-                      envelope_from  => $envelope_from,
-                      encode_headers => 0,
-                     );
-     unlink("incoming/P$bug_fn") || &quit("unlinking incoming/P$bug_fn: $!");
-     exit 0;
-}
diff --git a/scripts/processall b/scripts/processall
new file mode 100755 (executable)
index 0000000..2606b26
--- /dev/null
@@ -0,0 +1,88 @@
+#!/usr/bin/perl
+# $Id: processall.in,v 1.13 2005/10/06 03:46:13 ajt Exp $
+#
+# Usage: processall
+#
+# Uses up: incoming/I<code><bugnum>.nn
+# Temps:   incoming/[GP].nn
+# Creates: incoming/E.nn
+# Stop:    stop
+
+use warnings;
+use strict;
+
+
+use Debbugs::Config qw(:globals);
+use Debbugs::Common qw(:lock);
+
+my $lib_path = $gLibPath;
+
+use File::Path;
+
+chdir( $gSpoolDir ) || die "chdir spool: $!\n";
+
+#open(DEBUG,">&4");
+
+umask(002);
+
+$|=1;
+my %fudged;
+my @ids;
+
+my $ndone = 0;
+&filelock('incoming-cleaner');
+for (;;) {
+    if (-f 'stop') {
+        print(STDERR "stop file created\n") || die $!;
+        last;
+    }
+    if (!@ids) {
+        opendir(DIR,"incoming") || die $!;
+        while ( defined( $_= readdir(DIR) )) { push(@ids,$_) if s/^I//; }
+        last unless @ids;
+        @ids= sort(@ids);
+    }
+    stat("$gMaintainerFile") || die "stat $gMaintainerFile: $!\n";
+    my $nf= @ids;
+    my $id= shift(@ids);
+    unless (rename("incoming/I$id","incoming/G$id")) {
+        if ($fudged{$id}) {
+            die "$id already fudged once! $!\n";
+        }
+        $fudged{$id}= 1;
+        next;
+    }
+    my $c;
+    if ($id =~ m/^[RC]/) {
+        print(STDOUT "[$nf] $id service ...") || die $!;
+        defined($c=fork) || die $!;
+        if (!$c) { exec("$lib_path/service",$id); die "unable to execute $lib_path/service: $!"; }
+    } elsif ($id =~ m/^[BMQFDUL]/) {
+        print(STDOUT "[$nf] $id process ...") || die $!;
+        defined($c=fork) || die $!;
+        if (!$c) { exec("$lib_path/process",$id); die "unable to execute $lib_path/process: $!"; }
+    } else {
+        die "bad name $id";
+    }
+    my $cc=waitpid($c,0); $cc == $c || die "$cc $c $!";
+    my $status=$?;
+    if ($status) {
+        print(STDERR "$id: process failed ($status $!) - now in [PG]$id\n") || die $!;
+    }
+    print(STDOUT " done\n") || die $!;
+    rmtree("$gSpoolDir/mime.tmp",0,1);
+    $ndone++;
+}
+
+
+system("$lib_path/gen-indices",'--quick') == 0 or print STDERR "gen-indices failed\n";
+
+if (@gPostProcessall) {
+    system @gPostProcessall == 0 or print STDERR "\@gPostProcessall failed: ".join(' ',@gPostProcessall)."\n";
+}
+
+
+
+&unfilelock;
+
+exit(0);
diff --git a/scripts/processall.in b/scripts/processall.in
deleted file mode 100755 (executable)
index 2606b26..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-#!/usr/bin/perl
-# $Id: processall.in,v 1.13 2005/10/06 03:46:13 ajt Exp $
-#
-# Usage: processall
-#
-# Uses up: incoming/I<code><bugnum>.nn
-# Temps:   incoming/[GP].nn
-# Creates: incoming/E.nn
-# Stop:    stop
-
-use warnings;
-use strict;
-
-
-use Debbugs::Config qw(:globals);
-use Debbugs::Common qw(:lock);
-
-my $lib_path = $gLibPath;
-
-use File::Path;
-
-chdir( $gSpoolDir ) || die "chdir spool: $!\n";
-
-#open(DEBUG,">&4");
-
-umask(002);
-
-$|=1;
-my %fudged;
-my @ids;
-
-my $ndone = 0;
-&filelock('incoming-cleaner');
-for (;;) {
-    if (-f 'stop') {
-        print(STDERR "stop file created\n") || die $!;
-        last;
-    }
-    if (!@ids) {
-        opendir(DIR,"incoming") || die $!;
-        while ( defined( $_= readdir(DIR) )) { push(@ids,$_) if s/^I//; }
-        last unless @ids;
-        @ids= sort(@ids);
-    }
-    stat("$gMaintainerFile") || die "stat $gMaintainerFile: $!\n";
-    my $nf= @ids;
-    my $id= shift(@ids);
-    unless (rename("incoming/I$id","incoming/G$id")) {
-        if ($fudged{$id}) {
-            die "$id already fudged once! $!\n";
-        }
-        $fudged{$id}= 1;
-        next;
-    }
-    my $c;
-    if ($id =~ m/^[RC]/) {
-        print(STDOUT "[$nf] $id service ...") || die $!;
-        defined($c=fork) || die $!;
-        if (!$c) { exec("$lib_path/service",$id); die "unable to execute $lib_path/service: $!"; }
-    } elsif ($id =~ m/^[BMQFDUL]/) {
-        print(STDOUT "[$nf] $id process ...") || die $!;
-        defined($c=fork) || die $!;
-        if (!$c) { exec("$lib_path/process",$id); die "unable to execute $lib_path/process: $!"; }
-    } else {
-        die "bad name $id";
-    }
-    my $cc=waitpid($c,0); $cc == $c || die "$cc $c $!";
-    my $status=$?;
-    if ($status) {
-        print(STDERR "$id: process failed ($status $!) - now in [PG]$id\n") || die $!;
-    }
-    print(STDOUT " done\n") || die $!;
-    rmtree("$gSpoolDir/mime.tmp",0,1);
-    $ndone++;
-}
-
-
-system("$lib_path/gen-indices",'--quick') == 0 or print STDERR "gen-indices failed\n";
-
-if (@gPostProcessall) {
-    system @gPostProcessall == 0 or print STDERR "\@gPostProcessall failed: ".join(' ',@gPostProcessall)."\n";
-}
-
-
-
-&unfilelock;
-
-exit(0);
diff --git a/scripts/rebuild b/scripts/rebuild
new file mode 100755 (executable)
index 0000000..6c98f25
--- /dev/null
@@ -0,0 +1,60 @@
+#!/usr/bin/perl -w
+# $Id: rebuild.in,v 1.13 2003/08/23 15:12:57 cjwatson Exp $
+
+# Load modules and set environment
+use File::Copy;
+$config_path = '/etc/debbugs';
+$lib_path = '/usr/lib/debbugs';
+
+require("$config_path/config");
+require("$lib_path/errorlib");
+use vars qw($gSpoolDir);
+
+$ENV{'PATH'} = $lib_path.':'.$ENV{'PATH'};
+
+chdir("$gSpoolDir") || die "chdir spool: $!\n";
+
+#global variables
+$debug = 0;
+
+@ARGV==0 and &quit( "no archive given on the commandline" );
+my $archive = shift(@ARGV);
+my $index = "index.$archive";
+$index = 'index.db' if $archive eq 'db-h';
+open IDXFILE, "> $index" or &quit( "trying to reset index file: $!" );
+
+#get list of bugs (ie, status files)
+my @files;
+for ($subdir=0; $subdir<100; $subdir++ )
+{
+    my $path = sprintf( "$archive/%.2d", $subdir );
+    opendir(DIR,$path) || next; 
+    my @list= grep(m/^\d+\.summary$/,readdir(DIR));
+    closedir DIR;
+    grep(s/\.summary$//,@list);
+    push @files, @list;
+}
+
+@files = sort { $a <=> $b } @files;
+
+#process each bug (ie, status file)
+for my $ref (@files) 
+{
+    print STDERR "$ref considering\n" if $debug;
+    my $data = readbug($ref, $archive);
+    $data->{severity} =~ y/A-Z/a-z/;
+
+    (my $pkglist = $data->{package}) =~ s/[,\s]+/,/g;
+    $pkglist =~ s/^,+//;
+    $pkglist =~ s/,+$//;
+
+    my $whendone = 'open';
+    $whendone = 'forwarded' if length $data->{forwarded};
+    $whendone = 'done' if length $data->{done};
+
+    printf IDXFILE "%s %d %d %s [%s] %s %s\n",
+       $pkglist, $ref, $data->{date}, $whendone, $data->{originator},
+       $data->{severity}, $data->{keywords};
+}
+
+close IDXFILE;
diff --git a/scripts/rebuild.in b/scripts/rebuild.in
deleted file mode 100755 (executable)
index 6c98f25..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-#!/usr/bin/perl -w
-# $Id: rebuild.in,v 1.13 2003/08/23 15:12:57 cjwatson Exp $
-
-# Load modules and set environment
-use File::Copy;
-$config_path = '/etc/debbugs';
-$lib_path = '/usr/lib/debbugs';
-
-require("$config_path/config");
-require("$lib_path/errorlib");
-use vars qw($gSpoolDir);
-
-$ENV{'PATH'} = $lib_path.':'.$ENV{'PATH'};
-
-chdir("$gSpoolDir") || die "chdir spool: $!\n";
-
-#global variables
-$debug = 0;
-
-@ARGV==0 and &quit( "no archive given on the commandline" );
-my $archive = shift(@ARGV);
-my $index = "index.$archive";
-$index = 'index.db' if $archive eq 'db-h';
-open IDXFILE, "> $index" or &quit( "trying to reset index file: $!" );
-
-#get list of bugs (ie, status files)
-my @files;
-for ($subdir=0; $subdir<100; $subdir++ )
-{
-    my $path = sprintf( "$archive/%.2d", $subdir );
-    opendir(DIR,$path) || next; 
-    my @list= grep(m/^\d+\.summary$/,readdir(DIR));
-    closedir DIR;
-    grep(s/\.summary$//,@list);
-    push @files, @list;
-}
-
-@files = sort { $a <=> $b } @files;
-
-#process each bug (ie, status file)
-for my $ref (@files) 
-{
-    print STDERR "$ref considering\n" if $debug;
-    my $data = readbug($ref, $archive);
-    $data->{severity} =~ y/A-Z/a-z/;
-
-    (my $pkglist = $data->{package}) =~ s/[,\s]+/,/g;
-    $pkglist =~ s/^,+//;
-    $pkglist =~ s/,+$//;
-
-    my $whendone = 'open';
-    $whendone = 'forwarded' if length $data->{forwarded};
-    $whendone = 'done' if length $data->{done};
-
-    printf IDXFILE "%s %d %d %s [%s] %s %s\n",
-       $pkglist, $ref, $data->{date}, $whendone, $data->{originator},
-       $data->{severity}, $data->{keywords};
-}
-
-close IDXFILE;
diff --git a/scripts/receive b/scripts/receive
new file mode 100755 (executable)
index 0000000..eb101a4
--- /dev/null
@@ -0,0 +1,147 @@
+#!/usr/bin/perl
+# $Id: receive.in,v 1.17 2005/07/24 18:42:41 don Exp $
+# usage: mail is piped directly into program
+
+#set umask in order to have group-writable incoming/*
+#umask(002);
+
+use Debbugs::Config qw(:globals :text);
+my $lib_path = $gLibPath;
+
+$ENV{'PATH'} = '/usr/lib/debbugs:'.$ENV{'PATH'};
+
+#set source of mail delivery
+#sets any prefix needed to get mailer to add it to error mail
+if ( $gMailer eq 'exim' ) 
+{      $gBadEmailPrefix = '';
+       $_ = $ENV{'LOCAL_PART'};
+} elsif ( $gMailer eq 'qmail' )
+{      $gBadEmailPrefix = '//';
+       $_ = $ENV{'DEFAULT'};
+#      $_ = $ENV{'RECIPIENT'};
+#      s/^\w+-bugs--?//;
+} else 
+{      $gBadEmailPrefix = '';
+       $_ = $ARGV[0];
+       s/\>//;
+       s/\<//;
+}
+
+#remove everything from @ to end of line
+s/\@.*$//;
+
+#convert remaining upper case to lower case
+y/A-Z/a-z/;
+
+#set up to determine command
+%withbugaddressmap= ('-submit',     'B',
+                   '',            'B',
+                   '-maintonly',  'M',
+                   '-quiet',      'Q',
+                   '-forwarded',  'F',
+                   '-done',       'D',
+                   '-close',      'D',
+                  '-request',    'R',
+                   '-submitter',  'U',
+                  # Used for bug subscription
+                  #'-list-nothing-will-match-this', 'L',
+                   );
+
+%withpkgaddressmap= ('-request',     'R');
+
+%withoutaddressmap= ('submit',     'B',
+                      'bugs',       'B',
+                      'maintonly',  'M',
+                      'quiet',      'Q',
+                      'forwarded',  'F',
+                      'done',       'D',
+                      'close',      'D',
+                      'request',    'R',
+                      'submitter',  'U',
+                      'control',    'C');
+
+#determine command
+if (s/^(\d{1,9})\b//) {
+    $bugnumber= $1;
+    if (not exists $withbugaddressmap{$_} and
+/-(?:(?:un)?subscribe|subhelp|help|ignore|(?:sub(?:yes|approve|reject)
+ |unsubyes|bounce|probe|approve|reject|
+ setlistyes|setlistsilentyes).*)/x
+       ) {
+        $map = 'L';
+    }
+    else {
+        $map= $withbugaddressmap{$_};
+    }
+    $addrrec= "$bugnumber$_";
+} elsif (s/^(\w+)-//) {
+    $bugnumber= $1;
+    $map= $withpkgaddressmap{"-$_"};
+    $addrrec= "$bugnumber-$_";
+} else {
+    $bugnumber= '';
+    $map= $withoutaddressmap{$_};
+    $addrrec= "$_";
+}
+
+#print no command received
+if (!defined($map)) {
+    print STDERR <<ENDTEXT;
+$gBadEmailPrefix
+$gBadEmailPrefix Unknown $gBug service address $_\@$gEmailDomain.
+$gBadEmailPrefix Recognised addresses are:
+$gBadEmailPrefix
+$gBadEmailPrefix     General:       Read $gBug# in Subject:    $gBug# is NNNN:
+$gBadEmailPrefix
+$gBadEmailPrefix      request        submit  $gBug             NNNN  NNNN-submit
+$gBadEmailPrefix      control        maintonly                NNNN-maintonly
+$gBadEmailPrefix      owner          quiet                    NNNN-quiet
+$gBadEmailPrefix      postmaster     forwarded                NNNN-forwarded
+$gBadEmailPrefix                     done  close              NNNN-done  NNNN-close
+$gBadEmailPrefix                     submitter                NNNN-submitter
+$gBadEmailPrefix
+$gBadEmailPrefix (all \@$gEmailDomain.)
+$gBadEmailPrefix
+$gBadEmailPrefix For instructions via the WWW see:
+$gBadEmailPrefix   http://$gWebDomain/
+$gBadEmailPrefix   http://$gWebDomain/Reporting$gHTMLSuffix
+$gBadEmailPrefix   http://$gWebDomain/Developer$gHTMLSuffix
+$gBadEmailPrefix   http://$gWebDomain/Access$gHTMLSuffix
+$gTextInstructions
+$gBadEmailPrefix For details of how to access $gBug report logs by email:
+$gBadEmailPrefix   send \`request\@$gEmailDomain' the word \`help'
+$gBadEmailPrefix
+ENDTEXT
+    exit(100);
+}
+
+@months=qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
+($sec,$min,$hour,$mday,$mon,$year)= gmtime(time);
+
+$queue= "$map$bugnumber";
+
+chdir("$gSpoolDir/incoming") || &failure("chdir to spool: $!");
+
+$id= time.$$;
+open(FILE,">T.$id") || &failure("open temporary file: $!");
+printf(FILE "Received: (at %s) by $gEmailDomain; %d %s %d %02d:%02d:%02d +0000\n",
+       $addrrec, $mday,$months[$mon],$year+1900, $hour,$min,$sec) ||
+    &failure("write header to temporary file: $!");
+while(<STDIN>) { print(FILE) || &failure("write temporary file: $!"); }
+close(FILE) || &failure("close temporary file: $!");
+
+my $prefix;
+if ($gSpamScan) {
+    $prefix = 'S';
+} else {
+    $prefix = 'I';
+}
+rename("T.$id","$prefix$queue.$id") || &failure("rename spool message: $!");
+
+exit(0);
+
+sub failure {
+    length($id) && unlink("T.$id");
+    print STDERR "bugs receive failure: @_\n";
+    exit(75); # EX_TEMPFAIL
+}
diff --git a/scripts/receive.in b/scripts/receive.in
deleted file mode 100755 (executable)
index eb101a4..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-#!/usr/bin/perl
-# $Id: receive.in,v 1.17 2005/07/24 18:42:41 don Exp $
-# usage: mail is piped directly into program
-
-#set umask in order to have group-writable incoming/*
-#umask(002);
-
-use Debbugs::Config qw(:globals :text);
-my $lib_path = $gLibPath;
-
-$ENV{'PATH'} = '/usr/lib/debbugs:'.$ENV{'PATH'};
-
-#set source of mail delivery
-#sets any prefix needed to get mailer to add it to error mail
-if ( $gMailer eq 'exim' ) 
-{      $gBadEmailPrefix = '';
-       $_ = $ENV{'LOCAL_PART'};
-} elsif ( $gMailer eq 'qmail' )
-{      $gBadEmailPrefix = '//';
-       $_ = $ENV{'DEFAULT'};
-#      $_ = $ENV{'RECIPIENT'};
-#      s/^\w+-bugs--?//;
-} else 
-{      $gBadEmailPrefix = '';
-       $_ = $ARGV[0];
-       s/\>//;
-       s/\<//;
-}
-
-#remove everything from @ to end of line
-s/\@.*$//;
-
-#convert remaining upper case to lower case
-y/A-Z/a-z/;
-
-#set up to determine command
-%withbugaddressmap= ('-submit',     'B',
-                   '',            'B',
-                   '-maintonly',  'M',
-                   '-quiet',      'Q',
-                   '-forwarded',  'F',
-                   '-done',       'D',
-                   '-close',      'D',
-                  '-request',    'R',
-                   '-submitter',  'U',
-                  # Used for bug subscription
-                  #'-list-nothing-will-match-this', 'L',
-                   );
-
-%withpkgaddressmap= ('-request',     'R');
-
-%withoutaddressmap= ('submit',     'B',
-                      'bugs',       'B',
-                      'maintonly',  'M',
-                      'quiet',      'Q',
-                      'forwarded',  'F',
-                      'done',       'D',
-                      'close',      'D',
-                      'request',    'R',
-                      'submitter',  'U',
-                      'control',    'C');
-
-#determine command
-if (s/^(\d{1,9})\b//) {
-    $bugnumber= $1;
-    if (not exists $withbugaddressmap{$_} and
-/-(?:(?:un)?subscribe|subhelp|help|ignore|(?:sub(?:yes|approve|reject)
- |unsubyes|bounce|probe|approve|reject|
- setlistyes|setlistsilentyes).*)/x
-       ) {
-        $map = 'L';
-    }
-    else {
-        $map= $withbugaddressmap{$_};
-    }
-    $addrrec= "$bugnumber$_";
-} elsif (s/^(\w+)-//) {
-    $bugnumber= $1;
-    $map= $withpkgaddressmap{"-$_"};
-    $addrrec= "$bugnumber-$_";
-} else {
-    $bugnumber= '';
-    $map= $withoutaddressmap{$_};
-    $addrrec= "$_";
-}
-
-#print no command received
-if (!defined($map)) {
-    print STDERR <<ENDTEXT;
-$gBadEmailPrefix
-$gBadEmailPrefix Unknown $gBug service address $_\@$gEmailDomain.
-$gBadEmailPrefix Recognised addresses are:
-$gBadEmailPrefix
-$gBadEmailPrefix     General:       Read $gBug# in Subject:    $gBug# is NNNN:
-$gBadEmailPrefix
-$gBadEmailPrefix      request        submit  $gBug             NNNN  NNNN-submit
-$gBadEmailPrefix      control        maintonly                NNNN-maintonly
-$gBadEmailPrefix      owner          quiet                    NNNN-quiet
-$gBadEmailPrefix      postmaster     forwarded                NNNN-forwarded
-$gBadEmailPrefix                     done  close              NNNN-done  NNNN-close
-$gBadEmailPrefix                     submitter                NNNN-submitter
-$gBadEmailPrefix
-$gBadEmailPrefix (all \@$gEmailDomain.)
-$gBadEmailPrefix
-$gBadEmailPrefix For instructions via the WWW see:
-$gBadEmailPrefix   http://$gWebDomain/
-$gBadEmailPrefix   http://$gWebDomain/Reporting$gHTMLSuffix
-$gBadEmailPrefix   http://$gWebDomain/Developer$gHTMLSuffix
-$gBadEmailPrefix   http://$gWebDomain/Access$gHTMLSuffix
-$gTextInstructions
-$gBadEmailPrefix For details of how to access $gBug report logs by email:
-$gBadEmailPrefix   send \`request\@$gEmailDomain' the word \`help'
-$gBadEmailPrefix
-ENDTEXT
-    exit(100);
-}
-
-@months=qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
-($sec,$min,$hour,$mday,$mon,$year)= gmtime(time);
-
-$queue= "$map$bugnumber";
-
-chdir("$gSpoolDir/incoming") || &failure("chdir to spool: $!");
-
-$id= time.$$;
-open(FILE,">T.$id") || &failure("open temporary file: $!");
-printf(FILE "Received: (at %s) by $gEmailDomain; %d %s %d %02d:%02d:%02d +0000\n",
-       $addrrec, $mday,$months[$mon],$year+1900, $hour,$min,$sec) ||
-    &failure("write header to temporary file: $!");
-while(<STDIN>) { print(FILE) || &failure("write temporary file: $!"); }
-close(FILE) || &failure("close temporary file: $!");
-
-my $prefix;
-if ($gSpamScan) {
-    $prefix = 'S';
-} else {
-    $prefix = 'I';
-}
-rename("T.$id","$prefix$queue.$id") || &failure("rename spool message: $!");
-
-exit(0);
-
-sub failure {
-    length($id) && unlink("T.$id");
-    print STDERR "bugs receive failure: @_\n";
-    exit(75); # EX_TEMPFAIL
-}
diff --git a/scripts/service b/scripts/service
new file mode 100755 (executable)
index 0000000..05925d2
--- /dev/null
@@ -0,0 +1,1818 @@
+#!/usr/bin/perl
+# $Id: service.in,v 1.118 2005/10/19 01:22:14 don Exp $
+#
+# Usage: service <code>.nn
+# Temps:  incoming/P<code>.nn
+
+use File::Copy;
+use MIME::Parser;
+use Debbugs::MIME qw(decode_rfc1522 encode_rfc1522);
+use Debbugs::Mail qw(send_mail_message);
+use Debbugs::User;
+use HTML::Entities qw(encode_entities);
+use Debbugs::Versions::Dpkg;
+
+use Debbugs::Config qw(:globals :config);
+use Debbugs::CGI qw(html_escape);
+use Debbugs::Control qw(:archive :log);
+use Debbugs::Log qw(:misc);
+use Debbugs::Text qw(:templates);
+
+use Mail::RFC822::Address;
+
+$lib_path = $gLibPath;
+require "$lib_path/errorlib";
+$ENV{'PATH'} = $lib_path.':'.$ENV{'PATH'};
+
+chdir("$gSpoolDir") || die "chdir spool: $!\n";
+
+# open(DEBUG,">&4");
+open DEBUG, ">/dev/null";
+$debug = 0;
+umask(002);
+
+$_=shift;
+m/^[RC]\.\d+$/ || &quit("bad argument");
+$control= m/C/;
+$nn= $_;
+if (!rename("incoming/G$nn","incoming/P$nn")) {
+    $_=$!.'';  m/no such file or directory/i && exit 0;
+    &quit("renaming to lock: $!");
+}    
+
+open(M,"incoming/P$nn");
+@log=<M>;
+@msg=@log;
+close(M);
+
+chomp @msg;
+
+print "###\n",join("##\n",@msg),"\n###\n" if $debug;
+
+my $parser = new MIME::Parser;
+mkdir "$gSpoolDir/mime.tmp", 0777;
+$parser->output_under("$gSpoolDir/mime.tmp");
+my $entity = eval { $parser->parse_data(join('',@log)) };
+
+# header and decoded body respectively
+my (@headerlines, @bodylines);
+# Bug numbers to send e-mail to, hash so that we don't send to the
+# same bug twice.
+my (%bug_affected);
+
+if ($entity and $entity->head->tags) {
+    # Use map instead of chomp to also kill \r.
+    @headerlines = map {s/\r?\n?$//; $_;}
+        @{$entity->head->header};
+
+    my $entity_body = getmailbody($entity);
+    @bodylines = map {s/\r?\n$//; $_;}
+        $entity_body ? $entity_body->as_lines() : ();
+} else {
+    # Legacy pre-MIME code, kept around in case MIME::Parser fails.
+    my $i;
+    for ($i = 0; $i <= $#msg; $i++) {
+       $_ = $msg[$i];
+       last unless length($_);
+       while ($msg[$i+1] =~ m/^\s/) {
+           $i++;
+           $_ .= "\n".$msg[$i];
+       }
+       push @headerlines, $_;
+    }
+
+    @bodylines = @msg[$i..$#msg];
+}
+
+for (@headerlines) {
+    $_ = decode_rfc1522($_);
+    s/\n\s/ /g;
+    print ">$_<\n" if $debug;
+    if (s/^(\S+):\s*//) {
+       my $v = lc $1;
+       print ">$v=$_<\n" if $debug;
+       $header{$v} = $_;
+    } else {
+       print "!>$_<\n" if $debug;
+    }
+}
+
+# Strip off RFC2440-style PGP clearsigning.
+if (@bodylines and $bodylines[0] =~ /^-----BEGIN PGP SIGNED/) {
+    shift @bodylines while @bodylines and length $bodylines[0];
+    shift @bodylines while @bodylines and $bodylines[0] !~ /\S/;
+    for my $findsig (0 .. $#bodylines) {
+       if ($bodylines[$findsig] =~ /^-----BEGIN PGP SIGNATURE/) {
+           $#bodylines = $findsig - 1;
+           last;
+       }
+    }
+    map { s/^- // } @bodylines;
+}
+
+grep(s/\s+$//,@bodylines);
+
+print "***\n",join("\n",@bodylines),"\n***\n" if $debug;
+
+if (defined $header{'resent-from'} && !defined $header{'from'}) {
+    $header{'from'} = $header{'resent-from'};
+}
+
+defined($header{'from'}) || &quit("no From header");
+
+delete $header{'reply-to'} 
+       if ( defined($header{'reply-to'}) && $header{'reply-to'} =~ m/^\s*$/ );
+
+if ( defined($header{'reply-to'}) && $header{'reply-to'} ne "" ) {
+    $replyto = $header{'reply-to'};
+} else {
+    $replyto = $header{'from'};
+}
+
+# This is an error counter which should be incremented every time there is an error.
+my $errors = 0;
+$controlrequestaddr= $control ? "control\@$gEmailDomain" : "request\@$gEmailDomain";
+$transcript='';
+&transcript("Processing commands for $controlrequestaddr:\n\n");
+
+$dl= 0;
+$state= 'idle';
+$lowstate= 'idle';
+$mergelowstate= 'idle';
+$midix=0;    
+$extras="";
+
+my $user = $replyto;
+$user =~ s/,.*//;
+$user =~ s/^.*<(.*)>.*$/$1/;
+$user =~ s/[(].*[)]//;
+$user =~ s/^\s*(\S+)\s+.*$/$1/;
+$user = "" unless (Debbugs::User::is_valid_user($user));
+my $indicated_user = 0;
+
+my $quickabort = 0;
+
+my $fuckheads = "(" . join("|", @gExcludeFromControl) . ")";
+if (@gExcludeFromControl and $replyto =~ m/$fuckheads/) {
+       &transcript(fill_template('mail/excluded_from_control'));
+       $quickabort = 1;
+}
+
+my %limit_pkgs = ();
+my %clonebugs = ();
+my @bcc = ();
+
+sub addbcc {
+    push @bcc, $_[0] unless grep { $_ eq $_[0] } @bcc;
+}
+
+for ($procline=0; $procline<=$#bodylines; $procline++) {
+    $state eq 'idle' || print "$state ?\n";
+    $lowstate eq 'idle' || print "$lowstate ?\n";
+    $mergelowstate eq 'idle' || print "$mergelowstate ?\n";
+    if ($quickabort) {
+         &transcript("Stopping processing here.\n\n");
+        last;
+    }
+    $_= $bodylines[$procline]; s/\s+$//;
+    next unless m/\S/;
+    &transcript("> $_\n");
+    next if m/^\s*\#/;
+    $action= '';
+    if (m/^stop\s*$/i || m/^quit\s*$/i || m/^--\s*$/ || m/^thank(?:s|\s*you)?\s*$/i || m/^kthxbye\s*$/i) {
+       &transcript("Stopping processing here.\n\n");
+        last;
+    } elsif (m/^debug\s+(\d+)$/i && $1 >= 0 && $1 <= 1000) {
+        $dl= $1+0;
+        &transcript("Debug level $dl.\n\n");
+    } elsif (m/^(send|get)\s+\#?(\d{2,})$/i) {
+        $ref= $2+0;
+        &sendlynxdoc("bugreport.cgi?bug=$ref","logs for $gBug#$ref");
+    } elsif (m/^send-detail\s+\#?(\d{2,})$/i) {
+       $ref= $1+0;
+       &sendlynxdoc("bugreport.cgi?bug=$ref&boring=yes",
+                    "detailed logs for $gBug#$ref");
+    } elsif (m/^index(\s+full)?$/i) {
+       &transcript("This BTS function is currently disabled, sorry.\n\n");
+       $errors++;
+       $ok++; # well, it's not really ok, but it fixes #81224 :)
+    } elsif (m/^index-summary\s+by-package$/i) {
+       &transcript("This BTS function is currently disabled, sorry.\n\n");
+       $errors++;
+       $ok++; # well, it's not really ok, but it fixes #81224 :)
+    } elsif (m/^index-summary(\s+by-number)?$/i) {
+       &transcript("This BTS function is currently disabled, sorry.\n\n");
+       $errors++;
+       $ok++; # well, it's not really ok, but it fixes #81224 :)
+    } elsif (m/^index(\s+|-)pack(age)?s?$/i) {
+       &sendlynxdoc("pkgindex.cgi?indexon=pkg",'index of packages');
+    } elsif (m/^index(\s+|-)maints?$/i) {
+       &sendlynxdoc("pkgindex.cgi?indexon=maint",'index of maintainers');
+    } elsif (m/^index(\s+|-)maint\s+(\S+)$/i) {
+       $maint = $2;
+       &sendlynxdoc("pkgreport.cgi?maint=" . urlsanit($maint),
+                    "$gBug list for maintainer \`$maint'");
+        $ok++;
+    } elsif (m/^index(\s+|-)pack(age)?s?\s+(\S.*\S)$/i) {
+       $package = $+;
+       &sendlynxdoc("pkgreport.cgi?pkg=" . urlsanit($package),
+                    "$gBug list for package $package");
+        $ok++;
+    } elsif (m/^send-unmatched(\s+this|\s+-?0)?$/i) {
+       &transcript("This BTS function is currently disabled, sorry.\n\n");
+       $errors++;
+       $ok++; # well, it's not really ok, but it fixes #81224 :)
+    } elsif (m/^send-unmatched\s+(last|-1)$/i) {
+       &transcript("This BTS function is currently disabled, sorry.\n\n");
+       $errors++;
+       $ok++; # well, it's not really ok, but it fixes #81224 :)
+    } elsif (m/^send-unmatched\s+(old|-2)$/i) {
+       &transcript("This BTS function is currently disabled, sorry.\n\n");
+       $errors++;
+       $ok++; # well, it's not really ok, but it fixes #81224 :)
+    } elsif (m/^getinfo\s+([\w-.]+)$/i) {
+        # the following is basically a Debian-specific kludge, but who cares
+        $req = $1;
+       if ($req =~ /^maintainers$/i && -f "$gConfigDir/Maintainers") {
+           &sendinfo("local", "$gConfigDir/Maintainers", "Maintainers file");
+       } elsif ($req =~ /^override\.(\w+)\.([\w-.]+)$/i) {
+           $req =~ s/.gz$//;
+           &sendinfo("ftp.d.o", "$req", "override file for $2 part of $1 distribution");
+       } elsif ($req =~ /^pseudo-packages\.(description|maintainers)$/i && -f "$gConfigDir/$req") {
+           &sendinfo("local", "$gConfigDir/$req", "$req file");
+       } else {
+           &transcript("Info file $req does not exist.\n\n");
+       }
+    } elsif (m/^help/i) {
+        &sendhelp;
+        &transcript("\n");
+        $ok++;
+    } elsif (m/^refcard/i) {
+        &sendtxthelp("bug-mailserver-refcard.txt","mail servers' reference card");
+    } elsif (m/^subscribe/i) {
+        &transcript(<<END);
+There is no $gProject $gBug mailing list.  If you wish to review bug reports
+please do so via http://$gWebDomain/ or ask this mail server
+to send them to you.
+soon: MAILINGLISTS_TEXT
+END
+    } elsif (m/^unsubscribe/i) {
+        &transcript(<<END);
+soon: UNSUBSCRIBE_TEXT
+soon: MAILINGLISTS_TEXT
+END
+    } elsif (m/^user\s+(\S+)\s*$/i) {
+        my $newuser = $1;
+       if (Debbugs::User::is_valid_user($newuser)) {
+           my $olduser = ($user ne "" ? " (was $user)" : "");
+            &transcript("Setting user to $newuser$olduser.\n");
+           $user = $newuser;
+           $indicated_user = 1;
+       } else {
+           &transcript("Selected user id ($newuser) invalid, sorry\n");
+           $errors++;
+           $user = "";
+           $indicated_user = 1;
+       }
+    } elsif (m/^usercategory\s+(\S+)(\s+\[hidden\])?\s*$/i) {
+        $ok++;
+       my $catname = $1;
+       my $hidden = ($2 ne "");
+
+        my $prefix = "";
+        my @cats;
+        my $bad = 0;
+       my $catsec = 0;
+       if ($user eq "") {
+           &transcript("No valid user selected\n");
+           $errors++;
+           next;
+        }
+       if (not $indicated_user and defined $user) {
+            &transcript("User is $user\n");
+            $indicated_user = 1;
+       }
+       while (++$procline <= $#bodylines) {
+            unless ($bodylines[$procline] =~ m/^\s*([*+])\s*(\S.*)$/) {
+                $procline--;
+                last;
+            }
+            &transcript("> $bodylines[$procline]\n");
+            next if $bad;
+            my ($o, $txt) = ($1, $2);
+            if ($#cats == -1 && $o eq "+") {
+                &transcript("User defined category specification must start with a category name. Skipping.\n\n");
+               $errors++;
+                $bad = 1;
+                next;
+            }
+            if ($o eq "+") {
+               unless (ref($cats[-1]) eq "HASH") {
+                   $cats[-1] = { "nam" => $cats[-1], 
+                                 "pri" => [], "ttl" => [] };
+               }
+               $catsec++;
+               my ($desc, $ord, $op);
+                if ($txt =~ m/^(.*\S)\s*\[((\d+):\s*)?\]\s*$/) {
+                    $desc = $1; $ord = $3; $op = "";
+                } elsif ($txt =~ m/^(.*\S)\s*\[((\d+):\s*)?(\S+)\]\s*$/) {
+                    $desc = $1; $ord = $3; $op = $4;
+                } elsif ($txt =~ m/^([^[\s]+)\s*$/) {
+                    $desc = ""; $op = $1;
+                } else {
+                    &transcript("Unrecognised syntax for category section. Skipping.\n\n");
+                   $errors++;
+                    $bad = 1;
+                    next;
+                }
+               $ord = 999 unless defined $ord;
+
+               if ($op) {
+                    push @{$cats[-1]->{"pri"}}, $prefix . $op;
+                   push @{$cats[-1]->{"ttl"}}, $desc;
+                   push @ords, "$ord $catsec";
+               } else {
+                   @cats[-1]->{"def"} = $desc;
+                   push @ords, "$ord DEF";
+                   $catsec--;
+               }
+               @ords = sort { my ($a1, $a2, $b1, $b2) = split / /, "$a $b";
+                              $a1 <=> $b1 || $a2 <=> $b2; } @ords;
+               $cats[-1]->{"ord"} = [map { m/^.* (\S+)/; $1 eq "DEF" ? $catsec + 1 : $1 } @ords];
+            } elsif ($o eq "*") {
+               $catsec = 0;
+                my ($name);
+                if ($txt =~ m/^(.*\S)(\s*\[(\S+)\])\s*$/) {
+                    $name = $1; $prefix = $3;
+                } else {
+                    $name = $txt; $prefix = "";
+                }
+                push @cats, $name;
+            }
+        }
+        # XXX: got @cats, now do something with it
+       my $u = Debbugs::User::get_user($user);
+       if (@cats) {
+           &transcript("Added usercategory $catname.\n\n");
+           $u->{"categories"}->{$catname} = [ @cats ];
+           if (not $hidden) {
+                push @{$u->{visible_cats}},$catname;
+           }
+       } else {
+           &transcript("Removed usercategory $catname.\n\n");
+           delete $u->{"categories"}->{$catname};
+           @{$u->{visible_cats}} = grep {$_ ne $catname} @{$u->{visible_cats}};
+       }
+       $u->write();
+    } elsif (m/^usertags?\s+\#?(-?\d+)\s+(([=+-])\s*)?(\S.*)?$/i) {
+       $ok++;
+       $ref = $1; $addsubcode = $3 || "+"; $tags = $4;
+       if ($ref =~ m/^-\d+$/ && defined $clonebugs{$ref}) {
+            $ref = $clonebugs{$ref};
+        }
+       if ($user eq "") {
+           &transcript("No valid user selected\n");
+           $errors++;
+           $indicated_user = 1;
+        } elsif (&setbug) {
+           if (not $indicated_user and defined $user) {
+                &transcript("User is $user\n");
+                $indicated_user = 1;
+           }
+           &nochangebug;
+           my %ut;
+           Debbugs::User::read_usertags(\%ut, $user);
+            my @oldtags = (); my @newtags = (); my @badtags = ();
+           my %chtags;
+           for my $t (split /[,\s]+/, $tags) {
+               if ($t =~ m/^[a-zA-Z0-9.+\@-]+$/) {
+                   $chtags{$t} = 1;
+               } else {
+                   push @badtags, $t;
+               }
+           }
+           if (@badtags) {
+                &transcript("Ignoring illegal tag/s: ".join(', ', @badtags).".\nPlease use only alphanumerics, at, dot, plus and dash.\n");
+               $errors++;
+           }
+            for my $t (keys %chtags) {
+               $ut{$t} = [] unless defined $ut{$t};
+           }
+           for my $t (keys %ut) {
+               my %res = map { ($_, 1) } @{$ut{$t}};
+               push @oldtags, $t if defined $res{$ref};
+               my $addop = ($addsubcode eq "+" or $addsubcode eq "=");
+               my $del = (defined $chtags{$t} ? $addsubcode eq "-" 
+                                              : $addsubcode eq "=");
+               $res{$ref} = 1 if ($addop && defined $chtags{$t});
+               delete $res{$ref} if ($del);
+               push @newtags, $t if defined $res{$ref};
+               $ut{$t} = [ sort { $a <=> $b } (keys %res) ];
+           }
+           if (@oldtags == 0) {
+               &transcript("There were no usertags set.\n");
+           } else {
+               &transcript("Usertags were: " . join(" ", @oldtags) . ".\n");
+           }
+           &transcript("Usertags are now: " . join(" ", @newtags) . ".\n");
+           Debbugs::User::write_usertags(\%ut, $user);
+       }
+    } elsif (!$control) {
+        &transcript(<<END);
+Unknown command or malformed arguments to command.
+(Use control\@$gEmailDomain to manipulate reports.)
+
+END
+       $errors++;
+        if (++$unknowns >= 3) {
+            &transcript("Too many unknown commands, stopping here.\n\n");
+            last;
+        }
+#### "developer only" ones start here
+    } elsif (m/^close\s+\#?(-?\d+)(?:\s+(\d.*))?$/i) {
+       $ok++;
+       $ref= $1;
+       $bug_affected{$ref}=1;
+       $version= $2;
+       if (&setbug) {
+           &transcript("'close' is deprecated; see http://$gWebDomain/Developer$gHTMLSuffix#closing.\n");
+           if (length($data->{done}) and not defined($version)) {
+               &transcript("$gBug is already closed, cannot re-close.\n\n");
+                &nochangebug;
+            } else {
+                $action= "$gBug " .
+                    (defined($version) ?
+                        "marked as fixed in version $version" :
+                        "closed") .
+                    ", send any further explanations to $data->{originator}";
+                do {
+                    &addmaintainers($data);
+                                       if ( length( $gDoneList ) > 0 && length( $gListDomain ) >
+                                       0 ) { &addccaddress("$gDoneList\@$gListDomain"); }
+                    $data->{done}= $replyto;
+                    my @keywords= split ' ', $data->{keywords};
+                    if (grep $_ eq 'pending', @keywords) {
+                        $extramessage= "Removed pending tag.\n";
+                        $data->{keywords}= join ' ', grep $_ ne 'pending',
+                                                @keywords;
+                    }
+                    addfixedversions($data, $data->{package}, $version, 'binary');
+
+                   $message= <<END;
+From: $gMaintainerEmail ($gProject $gBug Tracking System)
+To: $data->{originator}
+Subject: $gBug#$ref acknowledged by developer
+         ($header{'subject'})
+References: $header{'message-id'} $data->{msgid}
+In-Reply-To: $data->{msgid}
+Message-ID: <handler.$ref.$nn.notifdonectrl.$midix\@$gEmailDomain>
+Reply-To: $ref\@$gEmailDomain
+X-$gProject-PR-Message: they-closed-control $ref
+
+This is an automatic notification regarding your $gBug report
+#$ref: $data->{subject},
+which was filed against the $data->{package} package.
+
+It has been marked as closed by one of the developers, namely
+$replyto.
+
+You should be hearing from them with a substantive response shortly,
+in case you haven't already. If not, please contact them directly.
+
+$gMaintainer
+(administrator, $gProject $gBugs database)
+
+END
+                    &sendmailmessage($message,$data->{originator});
+                } while (&getnextbug);
+            }
+        }
+    } elsif (m/^reassign\s+\#?(-?\d+)\s+(\S+)(?:\s+(\d.*))?$/i) {
+        $ok++;
+        $ref= $1; $newpackage= $2;
+       $bug_affected{$ref}=1;
+        $version= $3;
+       $newpackage =~ y/A-Z/a-z/;
+        if (&setbug) {
+            if (length($data->{package})) {
+                $action= "$gBug reassigned from package \`$data->{package}'".
+                         " to \`$newpackage'.";
+            } else {
+                $action= "$gBug assigned to package \`$newpackage'.";
+            }
+            do {
+                &addmaintainers($data);
+                $data->{package}= $newpackage;
+                $data->{found_versions}= [];
+                $data->{fixed_versions}= [];
+                # TODO: what if $newpackage is a source package?
+                addfoundversions($data, $data->{package}, $version, 'binary');
+                &addmaintainers($data);
+            } while (&getnextbug);
+        }
+    } elsif (m/^reopen\s+\#?(-?\d+)$/i ? ($noriginator='', 1) :
+             m/^reopen\s+\#?(-?\d+)\s+\=$/i ? ($noriginator='', 1) :
+             m/^reopen\s+\#?(-?\d+)\s+\!$/i ? ($noriginator=$replyto, 1) :
+             m/^reopen\s+\#?(-?\d+)\s+(\S.*\S)$/i ? ($noriginator=$2, 1) : 0) {
+        $ok++;
+        $ref= $1;
+       $bug_affected{$ref}=1;
+        if (&setbug) {
+            if (@{$data->{fixed_versions}}) {
+                &transcript("'reopen' may be inappropriate when a bug has been closed with a version;\nyou may need to use 'found' to remove fixed versions.\n");
+            }
+            if (!length($data->{done})) {
+                &transcript("$gBug is already open, cannot reopen.\n\n");
+                &nochangebug;
+            } else {
+                $action=
+                    $noriginator eq '' ? "$gBug reopened, originator not changed." :
+                        "$gBug reopened, originator set to $noriginator.";
+                do {
+                    &addmaintainers($data);
+                    $data->{originator}= $noriginator eq '' ?  $data->{originator} : $noriginator;
+                    $data->{fixed_versions}= [];
+                    $data->{done}= '';
+                } while (&getnextbug);
+            }
+        }
+    } elsif (m{^found\s+\#?(-?\d+)
+              (?:\s+((?:$config{package_name_re}\/)?
+                   $config{package_version_re}))?$}ix) {
+        $ok++;
+        $ref= $1;
+        $version= $2;
+        if (&setbug) {
+            if (!length($data->{done}) and not defined($version)) {
+                &transcript("$gBug is already open, cannot reopen.\n\n");
+               $errors++;
+                &nochangebug;
+            } else {
+                $action=
+                    defined($version) ?
+                        "$gBug marked as found in version $version." :
+                        "$gBug reopened.";
+                do {
+                    &addmaintainers($data);
+                    # The 'done' field gets a bit weird with version
+                    # tracking, because a bug may be closed by multiple
+                    # people in different branches. Until we have something
+                    # more flexible, we set it every time a bug is fixed,
+                    # and clear it when a bug is found in a version greater
+                   # than any version in which the bug is fixed or when
+                   # a bug is found and there is no fixed version
+                   if (defined $version) {
+                       my ($version_only) = $version =~ m{([^/]+)$};
+                        addfoundversions($data, $data->{package}, $version, 'binary');
+                       my @fixed_order = sort {Debbugs::Versions::Dpkg::vercmp($a,$b);}
+                            map {s{.+/}{}; $_;} @{$data->{fixed_versions}};
+                       if (not @fixed_order or (Debbugs::Versions::Dpkg::vercmp($version_only,$fixed_order[-1]) >= 0)) {
+                            $action = "$gBug marked as found in version $version and reopened."
+                                 if length $data->{done};
+                            $data->{done} = '';
+                       }
+                    } else {
+                        # Versionless found; assume old-style "not fixed at
+                        # all".
+                        $data->{fixed_versions} = [];
+                        $data->{done} = '';
+                    }
+                } while (&getnextbug);
+            }
+        }
+    } elsif (m[^notfound\s+\#?(-?\d+)\s+
+              ((?:$config{package_name_re}\/)?
+                   \S+)\s*$]ix) {
+        $ok++;
+        $ref= $1;
+        $version= $2;
+        if (&setbug) {
+            $action= "$gBug no longer marked as found in version $version.";
+            if (length($data->{done})) {
+                $extramessage= "(By the way, this $gBug is currently marked as done.)\n";
+            }
+            do {
+                &addmaintainers($data);
+                removefoundversions($data, $data->{package}, $version, 'binary');
+            } while (&getnextbug);
+       }
+   }
+    elsif (m[^fixed\s+\#?(-?\d+)\s+
+            ((?:$config{package_name_re}\/)?
+                 $config{package_version_re})\s*$]ix) {
+        $ok++;
+        $ref= $1;
+        $version= $2;
+        if (&setbug) {
+            $action=
+                 defined($version) ?
+                      "$gBug marked as fixed in version $version." :
+                           "$gBug reopened.";
+                do {
+                    &addmaintainers($data);
+                    addfixedversions($data, $data->{package}, $version, 'binary');
+              } while (&getnextbug);
+       }
+   }
+    elsif (m[^notfixed\s+\#?(-?\d+)\s+
+            ((?:$config{package_name_re}\/)?
+                 \S+)\s*$]ix) {
+        $ok++;
+        $ref= $1;
+        $version= $2;
+        if (&setbug) {
+            $action=
+                 defined($version) ?
+                      "$gBug no longer marked as fixed in version $version." :
+                           "$gBug reopened.";
+                do {
+                    &addmaintainers($data);
+                    removefixedversions($data, $data->{package}, $version, 'binary');
+              } while (&getnextbug);
+       }
+   }
+    elsif (m/^submitter\s+\#?(-?\d+)\s+\!$/i ? ($newsubmitter=$replyto, 1) :
+             m/^submitter\s+\#?(-?\d+)\s+(\S.*\S)$/i ? ($newsubmitter=$2, 1) : 0) {
+        $ok++;
+        $ref= $1;
+       $bug_affected{$ref}=1;
+        if ($ref =~ m/^-\d+$/ && defined $clonebugs{$ref}) {
+            $ref = $clonebugs{$ref};
+        }
+       if (not Mail::RFC822::Address::valid($newsubmitter)) {
+            transcript("$newsubmitter is not a valid e-mail address; not changing submitter\n");
+            $errors++;
+       }
+        elsif (&getbug) {
+            if (&checkpkglimit) {
+                &foundbug;
+                &addmaintainers($data);
+                $oldsubmitter= $data->{originator};
+                $data->{originator}= $newsubmitter;
+                $action= "Changed $gBug submitter from $oldsubmitter to $newsubmitter.";
+                &savebug;
+                &transcript("$action\n");
+                if (length($data->{done})) {
+                    &transcript("(By the way, that $gBug is currently marked as done.)\n");
+                }
+                &transcript("\n");
+                $message= <<END;
+From: $gMaintainerEmail ($gProject $gBug Tracking System)
+To: $oldsubmitter
+Subject: $gBug#$ref submitter address changed
+         ($header{'subject'})
+References: $header{'message-id'} $data->{msgid}
+In-Reply-To: $data->{msgid}
+Message-ID: <handler.$ref.$nn.newsubmitter.$midix\@$gEmailDomain>
+Reply-To: $ref\@$gEmailDomain
+X-$gProject-PR-Message: submitter-changed $ref
+
+The submitter address recorded for your $gBug report
+#$ref: $data->{subject}
+has been changed.
+
+The old submitter address for this report was
+$oldsubmitter.
+The new submitter address is
+$newsubmitter.
+
+This change was made by
+$replyto.
+If it was incorrect, please contact them directly.
+
+$gMaintainer
+(administrator, $gProject $gBugs database)
+
+END
+                &sendmailmessage($message,$oldsubmitter);
+            } else {
+                &cancelbug;
+            }
+        } else {
+            &notfoundbug;
+        }
+    } elsif (m/^forwarded\s+\#?(-?\d+)\s+(\S.*\S)$/i) {
+        $ok++;
+        $ref= $1; $whereto= $2;
+       $bug_affected{$ref}=1;
+        if (&setbug) {
+            if (length($data->{forwarded})) {
+    $action= "Forwarded-to-address changed from $data->{forwarded} to $whereto.";
+            } else {
+    $action= "Noted your statement that $gBug has been forwarded to $whereto.";
+            }
+            if (length($data->{done})) {
+                $extramessage= "(By the way, this $gBug is currently marked as done.)\n";
+            }
+            do {
+                &addmaintainers($data);
+               if (length($gForwardList)>0 && length($gListDomain)>0 ) {
+                    &addccaddress("$gForwardList\@$gListDomain"); 
+               }
+                $data->{forwarded}= $whereto;
+            } while (&getnextbug);
+        }
+    } elsif (m/^notforwarded\s+\#?(-?\d+)$/i) {
+        $ok++;
+        $ref= $1;
+       $bug_affected{$ref}=1;
+        if (&setbug) {
+            if (!length($data->{forwarded})) {
+                &transcript("$gBug is not marked as having been forwarded.\n\n");
+                &nochangebug;
+            } else {
+    $action= "Removed annotation that $gBug had been forwarded to $data->{forwarded}.";
+                do {
+                    &addmaintainers($data);
+                    $data->{forwarded}= '';
+                } while (&getnextbug);
+            }
+        }
+    } elsif (m/^severity\s+\#?(-?\d+)\s+([-0-9a-z]+)$/i ||
+       m/^priority\s+\#?(-?\d+)\s+([-0-9a-z]+)$/i) {
+        $ok++;
+        $ref= $1;
+       $bug_affected{$ref}=1;
+        $newseverity= $2;
+        if (!grep($_ eq $newseverity, @gSeverityList, "$gDefaultSeverity")) {
+            &transcript("Severity level \`$newseverity' is not known.\n".
+                       "Recognized are: $gShowSeverities.\n\n");
+           $errors++;
+        } elsif (exists $gObsoleteSeverities{$newseverity}) {
+            &transcript("Severity level \`$newseverity' is obsolete. " .
+                        "Use $gObsoleteSeverities{$newseverity} instead.\n\n");
+               $errors++;
+        } elsif (&setbug) {
+            $printseverity= $data->{severity};
+            $printseverity= "$gDefaultSeverity" if $printseverity eq '';
+           $action= "Severity set to \`$newseverity' from \`$printseverity'";
+           do {
+                &addmaintainers($data);
+                if (defined $gStrongList and isstrongseverity($newseverity)) {
+                    addbcc("$gStrongList\@$gListDomain");
+                }
+                $data->{severity}= $newseverity;
+            } while (&getnextbug);
+        }
+    } elsif (m/^tags?\s+\#?(-?\d+)\s+(([=+-])\s*)?(\S.*)?$/i) {
+       $ok++;
+       $ref = $1; $addsubcode = $3; $tags = $4;
+       $bug_affected{$ref}=1;
+       $addsub = "add";
+       if (defined $addsubcode) {
+           $addsub = "sub" if ($addsubcode eq "-");
+           $addsub = "add" if ($addsubcode eq "+");
+           $addsub = "set" if ($addsubcode eq "=");
+       }
+       my @okaytags = ();
+       my @badtags = ();
+       foreach my $t (split /[\s,]+/, $tags) {
+           if (!grep($_ eq $t, @gTags)) {
+               push @badtags, $t;
+           } else {
+               push @okaytags, $t;
+           }
+       }
+       if (@badtags) {
+            &transcript("Unknown tag/s: ".join(', ', @badtags).".\n".
+                       "Recognized are: ".join(' ', @gTags).".\n\n");
+           $errors++;
+       }
+       if (&setbug) {
+           if ($data->{keywords} eq '') {
+               &transcript("There were no tags set.\n");
+           } else {
+               &transcript("Tags were: $data->{keywords}\n");
+           }
+           if ($addsub eq "set") {
+               $action= "Tags set to: " . join(", ", @okaytags);
+           } elsif ($addsub eq "add") {
+               $action= "Tags added: " . join(", ", @okaytags);
+           } elsif ($addsub eq "sub") {
+               $action= "Tags removed: " . join(", ", @okaytags);
+           }
+           do {
+                &addmaintainers($data);
+               $data->{keywords} = '' if ($addsub eq "set");
+               # Allow removing obsolete tags.
+               if ($addsub eq "sub") {
+                   foreach my $t (@badtags) {
+                       $data->{keywords} = join ' ', grep $_ ne $t, 
+                           split ' ', $data->{keywords};
+                   }
+               }
+               # Now process all other additions and subtractions.
+               foreach my $t (@okaytags) {
+                   $data->{keywords} = join ' ', grep $_ ne $t, 
+                       split ' ', $data->{keywords};
+                   $data->{keywords} = "$t $data->{keywords}" unless($addsub eq "sub");
+               }
+               $data->{keywords} =~ s/\s*$//;
+            } while (&getnextbug);
+       }
+    } elsif (m/^(un)?block\s+\#?(-?\d+)\s+(by|with)\s+(\S.*)?$/i) {
+       $ok++;
+       my $bugnum = $2; my $blockers = $4;
+       $addsub = "add";
+       $addsub = "sub" if ($1 eq "un");
+       if ($bugnum =~ m/^-\d+$/ && defined $clonebugs{$bugnum}) {
+            $bugnum = $clonebugs{$bugnum};
+       }
+
+       my @okayblockers;
+       my @badblockers;
+       foreach my $b (split /[\s,]+/, $blockers) {
+           $b=~s/^\#//;
+           if ($b=~/[0-9]+/) {
+               $ref=$b;
+               if ($ref =~ m/^-\d+$/ && defined $clonebugs{$ref}) {
+                    $ref = $clonebugs{$ref};
+               }
+               if (&getbug) {
+                   &foundbug;
+                   push @okayblockers, $ref;
+
+                   # add to the list all bugs that are merged with $b,
+                   # because all of their data must be kept in sync
+                   @thisbugmergelist= split(/ /,$data->{mergedwith});
+                   &cancelbug;
+
+                   foreach $ref (@thisbugmergelist) {
+                       if (&getbug) {
+                          push @okayblockers, $ref;
+                          &cancelbug;
+                       }
+                   }
+               }
+               else {
+                   &notfoundbug;
+                    push @badblockers, $ref;
+               }
+           }
+           else {
+                push @badblockers, $b;
+           }
+       }
+       if (@badblockers) {
+            &transcript("Unknown blocking bug/s: ".join(', ', @badblockers).".\n");
+           $errors++;
+       }
+       
+       $ref=$bugnum;
+       if (&setbug) {
+           if ($data->{blockedby} eq '') {
+               &transcript("Was not blocked by any bugs.\n");
+           } else {
+               &transcript("Was blocked by: $data->{blockedby}\n");
+           }
+           if ($addsub eq "set") {
+               $action= "Blocking bugs of $bugnum set to: " . join(", ", @okayblockers);
+           } elsif ($addsub eq "add") {
+               $action= "Blocking bugs of $bugnum added: " . join(", ", @okayblockers);
+           } elsif ($addsub eq "sub") {
+               $action= "Blocking bugs of $bugnum removed: " . join(", ", @okayblockers);
+           }
+           my %removedblocks;
+           my %addedblocks;
+           do {
+                &addmaintainers($data);
+               my @oldblockerlist = split ' ', $data->{blockedby};
+               $data->{blockedby} = '' if ($addsub eq "set");
+               foreach my $b (@okayblockers) {
+                       $data->{blockedby} = manipset($data->{blockedby}, $b,
+                               ($addsub ne "sub"));
+               }
+
+               foreach my $b (@oldblockerlist) {
+                       if (! grep { $_ eq $b } split ' ', $data->{blockedby}) {
+                               push @{$removedblocks{$b}}, $ref;
+                       }
+               }
+               foreach my $b (split ' ', $data->{blockedby}) {
+                       if (! grep { $_ eq $b } @oldblockerlist) {
+                               push @{$addedblocks{$b}}, $ref;
+                       }
+               }
+            } while (&getnextbug);
+
+           # Now that the blockedby data is updated, change blocks data
+           # to match the changes.
+           foreach $ref (keys %addedblocks) {
+               if (&getbug) {
+                   foreach my $b (@{$addedblocks{$ref}}) {
+                       $data->{blocks} = manipset($data->{blocks}, $b, 1);
+                   }
+                   &savebug;
+                }
+           }
+           foreach $ref (keys %removedblocks) {
+               if (&getbug) {
+                   foreach my $b (@{$removedblocks{$ref}}) {
+                       $data->{blocks} = manipset($data->{blocks}, $b, 0);
+                   }
+                   &savebug;
+                }
+           }
+       }
+    } elsif (m/^retitle\s+\#?(-?\d+)\s+(\S.*\S)\s*$/i) {
+        $ok++;
+        $ref= $1; $newtitle= $2;
+       $bug_affected{$ref}=1;
+       if ($ref =~ m/^-\d+$/ && defined $clonebugs{$ref}) {
+           $ref = $clonebugs{$ref};
+       }
+        if (&getbug) {
+            if (&checkpkglimit) {
+                &foundbug;
+                &addmaintainers($data);
+               my $oldtitle = $data->{subject};
+                $data->{subject}= $newtitle;
+                $action= "Changed $gBug title to `$newtitle' from `$oldtitle'.";
+                &savebug;
+                &transcript("$action\n");
+                if (length($data->{done})) {
+                    &transcript("(By the way, that $gBug is currently marked as done.)\n");
+                }
+                &transcript("\n");
+            } else {
+                &cancelbug;
+            }
+        } else {
+            &notfoundbug;
+        }
+    } elsif (m/^unmerge\s+\#?(-?\d+)$/i) {
+       $ok++;
+       $ref= $1;
+       $bug_affected{$ref} = 1;
+       if (&setbug) {
+           if (!length($data->{mergedwith})) {
+               &transcript("$gBug is not marked as being merged with any others.\n\n");
+               &nochangebug;
+           } else {
+                $mergelowstate eq 'locked' || die "$mergelowstate ?";
+               $action= "Disconnected #$ref from all other report(s).";
+               @newmergelist= split(/ /,$data->{mergedwith});
+                $discref= $ref;
+               @bug_affected{@newmergelist} = 1 x @newmergelist;
+                do {
+                    &addmaintainers($data);
+                   $data->{mergedwith}= ($ref == $discref) ? ''
+                        : join(' ',grep($_ ne $ref,@newmergelist));
+                } while (&getnextbug);
+           }
+       }
+    } elsif (m/^merge\s+#?(-?\d+(\s+#?-?\d+)+)\s*$/i) {
+       $ok++;
+        my @tomerge= sort { $a <=> $b } split(/\s+#?/,$1);
+        my @newmergelist= ();
+       my %tags = ();
+       my %found = ();
+       my %fixed = ();
+        &getmerge;
+        while (defined($ref= shift(@tomerge))) {
+            &transcript("D| checking merge $ref\n") if $dl;
+           $ref+= 0;
+           if ($ref =~ m/^-\d+$/ && defined $clonebugs{$ref}) {
+               $ref = $clonebugs{$ref};
+           }
+           next if grep($_ == $ref,@newmergelist);
+           if (!&getbug) { &notfoundbug; @newmergelist=(); last }
+            if (!&checkpkglimit) { &cancelbug; @newmergelist=(); last; }
+            &foundbug;
+            &transcript("D| adding $ref ($data->{mergedwith})\n") if $dl;
+           $mismatch= '';
+           &checkmatch('package','m_package',$data->{package},@newmergelist);
+           &checkmatch('forwarded addr','m_forwarded',$data->{forwarded},@newmergelist);
+           $data->{severity} = '$gDefaultSeverity' if $data->{severity} eq '';
+           &checkmatch('severity','m_severity',$data->{severity},@newmergelist);
+           &checkmatch('blocks','m_blocks',$data->{blocks},@newmergelist);
+           &checkmatch('blocked-by','m_blockedby',$data->{blockedby},@newmergelist);
+           &checkmatch('done mark','m_done',length($data->{done}) ? 'done' : 'open',@newmergelist);
+           &checkmatch('owner','m_owner',$data->{owner},@newmergelist);
+           foreach my $t (split /\s+/, $data->{keywords}) { $tags{$t} = 1; }
+           foreach my $f (@{$data->{found_versions}}) { $found{$f} = 1; }
+           foreach my $f (@{$data->{fixed_versions}}) { $fixed{$f} = 1; }
+           if (length($mismatch)) {
+               &transcript("Mismatch - only $gBugs in same state can be merged:\n".
+                            $mismatch."\n");
+               $errors++;
+               &cancelbug; @newmergelist=(); last;
+           }
+            push(@newmergelist,$ref);
+            push(@tomerge,split(/ /,$data->{mergedwith}));
+           &cancelbug;
+       }
+       if (@newmergelist) {
+            @newmergelist= sort { $a <=> $b } @newmergelist;
+            $action= "Merged @newmergelist.";
+           delete @fixed{keys %found};
+           for $ref (@newmergelist) {
+               &getbug || die "huh ?  $gBug $ref disappeared during merge";
+                &addmaintainers($data);
+               @bug_affected{@newmergelist} = 1 x @newmergelist;
+               $data->{mergedwith}= join(' ',grep($_ != $ref,@newmergelist));
+               $data->{keywords}= join(' ', keys %tags);
+               $data->{found_versions}= [sort keys %found];
+               $data->{fixed_versions}= [sort keys %fixed];
+               &savebug;
+           }
+           &transcript("$action\n\n");
+       }
+        &endmerge;
+    } elsif (m/^forcemerge\s+\#?(-?\d+(?:\s+\#?-?\d+)+)\s*$/i) {
+       $ok++;
+       my @temp = split /\s+\#?/,$1;
+       my $master_bug = shift @temp;
+       my $master_bug_data;
+       my @tomerge = sort { $a <=> $b } @temp;
+        unshift @tomerge,$master_bug;
+       &transcript("D| force merging ".join(',',@tomerge)."\n") if $dl;
+       my @newmergelist= ();
+       my %tags = ();
+       my %found = ();
+       my %fixed = ();
+       # Here we try to do the right thing.
+       # First, if the bugs are in the same package, we merge all of the found, fixed, and tags.
+       # If not, we discard the found and fixed.
+       # Everything else we set to the values of the first bug.
+        &getmerge;
+        while (defined($ref= shift(@tomerge))) {
+            &transcript("D| checking merge $ref\n") if $dl;
+           $ref+= 0;
+           if ($ref =~ m/^-\d+$/ && defined $clonebugs{$ref}) {
+               $ref = $clonebugs{$ref};
+           }
+           next if grep($_ == $ref,@newmergelist);
+           if (!&getbug) { &notfoundbug; @newmergelist=(); last }
+            if (!&checkpkglimit) { &cancelbug; @newmergelist=(); last; }
+            &foundbug;
+            &transcript("D| adding $ref ($data->{mergedwith})\n") if $dl;
+           $master_bug_data = $data if not defined $master_bug_data;
+           if ($data->{package} ne $master_bug_data->{package}) {
+               &transcript("Mismatch - only $gBugs in the same package can be forcibly merged:\n".
+                           "$gBug $ref is not in the same package as $master_bug\n");
+               $errors++;
+               &cancelbug; @newmergelist=(); last;
+           }
+           for my $t (split /\s+/,$data->{keywords}) {
+                $tags{$t} = 1;
+           }
+           @found{@{$data->{found_versions}}} = (1) x @{$data->{found_versions}};
+           @fixed{@{$data->{fixed_versions}}} = (1) x @{$data->{fixed_versions}};
+           push(@newmergelist,$ref);
+            push(@tomerge,split(/ /,$data->{mergedwith}));
+           &cancelbug;
+       }
+       if (@newmergelist) {
+            @newmergelist= sort { $a <=> $b } @newmergelist;
+            $action= "Forcibly Merged @newmergelist.";
+           delete @fixed{keys %found};
+           for $ref (@newmergelist) {
+               &getbug || die "huh ?  $gBug $ref disappeared during merge";
+                &addmaintainers($data);
+               @bug_affected{@newmergelist} = 1 x @newmergelist;
+               $data->{mergedwith}= join(' ',grep($_ != $ref,@newmergelist));
+               $data->{keywords}= join(' ', keys %tags);
+               $data->{found_versions}= [sort keys %found];
+               $data->{fixed_versions}= [sort keys %fixed];
+               my @field_list = qw(forwarded package severity blocks blockedby owner done);
+               @{$data}{@field_list} = @{$master_bug_data}{@field_list};
+               &savebug;
+           }
+           &transcript("$action\n\n");
+       }
+        &endmerge;
+    } elsif (m/^clone\s+#?(\d+)\s+((-\d+\s+)*-\d+)\s*$/i) {
+       $ok++;
+
+       $origref = $1;
+       @newclonedids = split /\s+/, $2;
+       $newbugsneeded = scalar(@newclonedids);
+
+       $ref = $origref;
+       $bug_affected{$ref} = 1;
+       if (&setbug) {
+           if (length($data->{mergedwith})) {
+               &transcript("$gBug is marked as being merged with others. Use an existing clone.\n\n");
+               $errors++;
+               &nochangebug;
+           } else {
+               &filelock("nextnumber.lock");
+               open(N,"nextnumber") || &quit("nextnumber: read: $!");
+               $v=<N>; $v =~ s/\n$// || &quit("nextnumber bad format");
+               $firstref= $v+0;  $v += $newbugsneeded;
+               open(NN,">nextnumber"); print NN "$v\n"; close(NN);
+               &unfilelock;
+
+               $lastref = $firstref + $newbugsneeded - 1;
+
+               if ($newbugsneeded == 1) {
+                   $action= "$gBug $origref cloned as bug $firstref.";
+               } else {
+                   $action= "$gBug $origref cloned as bugs $firstref-$lastref.";
+               }
+
+               my $blocks = $data->{blocks};
+               my $blockedby = $data->{blockedby};
+               
+               &getnextbug;
+               my $ohash = get_hashname($origref);
+               my $clone = $firstref;
+                @bug_affected{@newclonedids} = 1 x @newclonedids;
+               for $newclonedid (@newclonedids) {
+                   $clonebugs{$newclonedid} = $clone;
+           
+                   my $hash = get_hashname($clone);
+                   copy("db-h/$ohash/$origref.log", "db-h/$hash/$clone.log");
+                   copy("db-h/$ohash/$origref.status", "db-h/$hash/$clone.status");
+                   copy("db-h/$ohash/$origref.summary", "db-h/$hash/$clone.summary");
+                   copy("db-h/$ohash/$origref.report", "db-h/$hash/$clone.report");
+                   &bughook('new', $clone, $data);
+               
+                   # Update blocking info of bugs blocked by or blocking the
+                   # cloned bug.
+                   foreach $ref (split ' ', $blocks) {
+                       &getbug;
+                       $data->{blockedby} = manipset($data->{blockedby}, $clone, 1);
+                       &savebug;
+                   }
+                   foreach $ref (split ' ', $blockedby) {
+                       &getbug;
+                       $data->{blocks} = manipset($data->{blocks}, $clone, 1);
+                       &savebug;
+                   }
+
+                   $clone++;
+               }
+           }
+       }
+    } elsif (m/^package\:?\s+(\S.*\S)?\s*$/i) {
+        $ok++;
+       my @pkgs = split /\s+/, $1;
+       if (scalar(@pkgs) > 0) {
+               %limit_pkgs = map { ($_, 1) } @pkgs;
+               &transcript("Ignoring bugs not assigned to: " . 
+                       join(" ", keys(%limit_pkgs)) . "\n\n");
+       } else {
+               %limit_pkgs = ();
+               &transcript("Not ignoring any bugs.\n\n");
+       }
+    } elsif (m/^owner\s+\#?(-?\d+)\s+!$/i ? ($newowner = $replyto, 1) :
+             m/^owner\s+\#?(-?\d+)\s+(\S.*\S)$/i ? ($newowner = $2, 1) : 0) {
+        $ok++;
+        $ref = $1;
+       $bug_affected{$ref} = 1;
+        if (&setbug) {
+            if (length $data->{owner}) {
+                $action = "Owner changed from $data->{owner} to $newowner.";
+            } else {
+                $action = "Owner recorded as $newowner.";
+            }
+            if (length $data->{done}) {
+                $extramessage = "(By the way, this $gBug is currently " .
+                                "marked as done.)\n";
+            }
+            do {
+                &addmaintainers($data);
+                $data->{owner} = $newowner;
+            } while (&getnextbug);
+        }
+    } elsif (m/^noowner\s+\#?(-?\d+)$/i) {
+        $ok++;
+        $ref = $1;
+       $bug_affected{$ref} = 1;
+        if (&setbug) {
+            if (length $data->{owner}) {
+                $action = "Removed annotation that $gBug was owned by " .
+                          "$data->{owner}.";
+                do {
+                    &addmaintainers($data);
+                    $data->{owner} = '';
+                } while (&getnextbug);
+            } else {
+                &transcript("$gBug is not marked as having an owner.\n\n");
+                &nochangebug;
+            }
+        }
+    } elsif (m/^unarchive\s+#?(\d+)$/i) {
+        $ok++;
+        $ref = $1;
+        $bug_affected{$ref} = 1;
+        my $transcript;
+        eval {
+             bug_unarchive(bug        => $ref,
+                           transcript => \$transcript,
+                           affected_bugs => \%bug_affected,
+                           requester => $header{from},
+                           request_addr => $controlrequestaddr,
+                           message => \@log,
+                          );
+        };
+        if ($@) {
+             $errors++;
+        }
+        transcript($transcript."\n");
+    } elsif (m/^archive\s+#?(\d+)$/i) {
+        $ok++;
+        $ref = $1;
+        $bug_affected{$ref} = 1;
+        if (&setbug) {
+             if (exists $data->{unarchived}) {
+                  my $transcript;
+                  nochangebug();
+                  eval {
+                       bug_archive(bug => $ref,
+                                   transcript => \$transcript,
+                                   ignore_time => 1,
+                                   affected_bugs => \%bug_affected,
+                                   requester => $header{from},
+                                   request_addr => $controlrequestaddr,
+                                   message => \@log,
+                                  );
+                  };
+                  if ($@) {
+                       $errors++;
+                  }
+                  transcript($transcript."\n");
+             }
+             else {
+                  transcript("$gBug $ref has not been archived previously\n\n");
+                  nochangebug();
+                  $errors++;
+             }
+        }
+    } else {
+        &transcript("Unknown command or malformed arguments to command.\n\n");
+       $errors++;
+        if (++$unknowns >= 5) {
+            &transcript("Too many unknown commands, stopping here.\n\n");
+            last;
+        }
+    }
+}
+if ($procline>$#bodylines) {
+    &transcript(">\nEnd of message, stopping processing here.\n\n");
+}
+if (!$ok && !quickabort) {
+    $errors++;
+    &transcript("No commands successfully parsed; sending the help text(s).\n");
+    &sendhelp;
+    &transcript("\n");
+}
+
+&transcript("MC\n") if $dl>1;
+@maintccs= ();
+for $maint (keys %maintccreasons) {
+&transcript("MM|$maint|\n") if $dl>1;
+    next if $maint eq $replyto;
+    $reasonstring= '';
+    $reasonsref= $maintccreasons{$maint};
+&transcript("MY|$maint|\n") if $dl>2;
+    for $p (sort keys %$reasonsref) {
+&transcript("MP|$p|\n") if $dl>2;
+        $reasonstring.= ', ' if length($reasonstring);
+        $reasonstring.= $p.' ' if length($p);
+        $reasonstring.= join(' ',map("#$_",sort keys %{$$reasonsref{$p}}));
+    }
+    if (length($reasonstring) > 40) {
+       (substr $reasonstring, 37) = "...";
+    }
+    $reasonstring = "" if (!defined($reasonstring));
+    push(@maintccs,"$maint ($reasonstring)");
+    push(@maintccaddrs,"$maint");
+}
+
+$maintccs = ""; 
+if (@maintccs) {
+    &transcript("MC|@maintccs|\n") if $dl>2;
+    $maintccs .= "Cc: " . join(",\n    ",@maintccs) . "\n";
+}
+
+my %packagepr;
+for my $maint (keys %maintccreasons) {
+     for my $package (keys %{$maintccreasons{$maint}}) {
+         next unless length $package;
+         $packagepr{$package} = 1;
+     }
+}
+my $packagepr = '';
+$packagepr = "X-${gProject}-PR-Package: " . join(keys %packagepr) . "\n" if keys %packagepr;
+
+# Add Bcc's to subscribed bugs
+push @bcc, map {"bugs=$_\@$gListDomain"} keys %bug_affected;
+
+if (!defined $header{'subject'} || $header{'subject'} eq "") {
+  $header{'subject'} = "your mail";
+}
+
+# Error text here advertises how many errors there were
+my $error_text = $errors > 0 ? " (with $errors errors)":'';
+
+$reply= <<END;
+From: $gMaintainerEmail ($gProject $gBug Tracking System)
+To: $replyto
+${maintccs}Subject: Processed${error_text}: $header{'subject'}
+In-Reply-To: $header{'message-id'}
+References: $header{'message-id'}
+Message-ID: <handler.s.$nn.transcript\@$gEmailDomain>
+Precedence: bulk
+${packagepr}X-$gProject-PR-Message: transcript
+
+${transcript}Please contact me if you need assistance.
+
+$gMaintainer
+(administrator, $gProject $gBugs database)
+$extras
+END
+
+$repliedshow= join(', ',$replyto,@maintccaddrs);
+# -1 is the service.in log
+&filelock("lock/-1");
+open(AP,">>db-h/-1.log") || &quit("open db-h/-1.log: $!");
+print(AP
+      "\2\n$repliedshow\n\5\n$reply\n\3\n".
+      "\6\n".
+      "<strong>Request received</strong> from <code>".
+      html_escape($header{'from'})."</code>\n".
+      "to <code>".html_escape($controlrequestaddr)."</code>\n".
+      "\3\n".
+      "\7\n",escape_log(@log),"\n\3\n") || &quit("writing db-h/-1.log: $!");
+close(AP) || &quit("open db-h/-1.log: $!");
+&unfilelock;
+utime(time,time,"db-h");
+
+&sendmailmessage($reply,exists $header{'x-debbugs-no-ack'}?():$replyto,@maintccaddrs,@bcc);
+
+unlink("incoming/P$nn") || &quit("unlinking incoming/P$nn: $!");
+
+sub sendmailmessage {
+    local ($message,@recips) = @_;
+    $message = "X-Loop: $gMaintainerEmail\n" . $message;
+    send_mail_message(message    => $message,
+                     recipients => \@recips,
+                    );
+    $midix++;
+}
+
+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,
+                           );
+}
+
+=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,
+                         },
+                        );
+}
+
+sub sendhelp {
+        &sendtxthelpraw("bug-log-mailserver.txt","instructions for request\@$gEmailDomain");
+        &sendtxthelpraw("bug-maint-mailcontrol.txt","instructions for control\@$gEmailDomain")
+            if $control;
+}
+
+#sub unimplemented {
+#    &transcript("Sorry, command $_[0] not yet implemented.\n\n");
+#}
+
+sub checkmatch {
+    local ($string,$mvarname,$svarvalue,@newmergelist) = @_;
+    local ($mvarvalue);
+    if (@newmergelist) {
+        eval "\$mvarvalue= \$$mvarname";
+        &transcript("D| checkmatch \`$string' /$mvarname/$mvarvalue/$svarvalue/\n")
+            if $dl;
+        $mismatch .=
+            "Values for \`$string' don't match:\n".
+            " #$newmergelist[0] has \`$mvarvalue';\n".
+            " #$ref has \`$svarvalue'\n"
+            if $mvarvalue ne $svarvalue;
+    } else {
+        &transcript("D| setupmatch \`$string' /$mvarname/$svarvalue/\n")
+            if $dl;
+        eval "\$$mvarname= \$svarvalue";
+    }
+}
+
+sub checkpkglimit {
+    if (keys %limit_pkgs and not defined $limit_pkgs{$data->{package}}) {
+        &transcript("$gBug number $ref belongs to package $data->{package}, skipping.\n\n");
+        $errors++;
+        return 0;
+    }
+    return 1;
+}
+
+sub manipset {
+    my $list = shift;
+    my $elt = shift;
+    my $add = shift;
+
+    my %h = map { $_ => 1 } split ' ', $list;
+    if ($add) {
+        $h{$elt}=1;
+    }
+    else {
+       delete $h{$elt};
+    }
+    return join ' ', sort keys %h;
+}
+
+# High-level bug manipulation calls
+# Do announcements themselves
+#
+# Possible calling sequences:
+#    setbug (returns 0)
+#    
+#    setbug (returns 1)
+#    &transcript(something)
+#    nochangebug
+#
+#    setbug (returns 1)
+#    $action= (something)
+#    do {
+#      (modify s_* variables)
+#    } while (getnextbug);
+
+sub nochangebug {
+    &dlen("nochangebug");
+    $state eq 'single' || $state eq 'multiple' || die "$state ?";
+    &cancelbug;
+    &endmerge if $manybugs;
+    $state= 'idle';
+    &dlex("nochangebug");
+}
+
+sub setbug {
+    &dlen("setbug $ref");
+    if ($ref =~ m/^-\d+/) {
+        if (!defined $clonebugs{$ref}) {
+            &notfoundbug;
+            &dlex("setbug => noclone");
+            return 0;
+        }
+        $ref = $clonebugs{$ref};
+    }
+    $state eq 'idle' || die "$state ?";
+    if (!&getbug) {
+        &notfoundbug;
+        &dlex("setbug => 0s");
+        return 0;
+    }
+
+    if (!&checkpkglimit) {
+        &cancelbug;
+        return 0;
+    }
+
+    @thisbugmergelist= split(/ /,$data->{mergedwith});
+    if (!@thisbugmergelist) {
+        &foundbug;
+        $manybugs= 0;
+        $state= 'single';
+        $sref=$ref;
+        &dlex("setbug => 1s");
+        return 1;
+    }
+    &cancelbug;
+    &getmerge;
+    $manybugs= 1;
+    if (!&getbug) {
+        &notfoundbug;
+        &endmerge;
+        &dlex("setbug => 0mc");
+        return 0;
+    }
+    &foundbug;
+    $state= 'multiple'; $sref=$ref;
+    &dlex("setbug => 1m");
+    return 1;
+}
+
+sub getnextbug {
+    &dlen("getnextbug");
+    $state eq 'single' || $state eq 'multiple' || die "$state ?";
+    &savebug;
+    if (!$manybugs || !@thisbugmergelist) {
+        length($action) || die;
+        &transcript("$action\n$extramessage\n");
+        &endmerge if $manybugs;
+        $state= 'idle';
+        &dlex("getnextbug => 0");
+        return 0;
+    }
+    $ref= shift(@thisbugmergelist);
+    &getbug || die "bug $ref disappeared";
+    &foundbug;
+    &dlex("getnextbug => 1");
+    return 1;
+}
+
+# Low-level bug-manipulation calls
+# Do no announcements
+#
+#    getbug (returns 0)
+#
+#    getbug (returns 1)
+#    cancelbug
+#
+#    getmerge
+#    $action= (something)
+#    getbug (returns 1)
+#    savebug/cancelbug
+#    getbug (returns 1)
+#    savebug/cancelbug
+#    [getbug (returns 0)]
+#    &transcript("$action\n\n")
+#    endmerge
+
+sub notfoundbug { &transcript("$gBug number $ref not found. (Is it archived?)\n\n"); }
+sub foundbug { &transcript("$gBug#$ref: $data->{subject}\n"); }
+
+sub getmerge {
+    &dlen("getmerge");
+    $mergelowstate eq 'idle' || die "$mergelowstate ?";
+    &filelock('lock/merge');
+    $mergelowstate='locked';
+    &dlex("getmerge");
+}
+
+sub endmerge {
+    &dlen("endmerge");
+    $mergelowstate eq 'locked' || die "$mergelowstate ?";
+    &unfilelock;
+    $mergelowstate='idle';
+    &dlex("endmerge");
+}
+
+sub getbug {
+    &dlen("getbug $ref");
+    $lowstate eq 'idle' || die "$state ?";
+    # Only use unmerged bugs here
+    if (($data = &lockreadbug($ref,'db-h'))) {
+        $sref= $ref;
+        $lowstate= "open";
+        &dlex("getbug => 1");
+        $extramessage='';
+        return 1;
+    }
+    $lowstate= 'idle';
+    &dlex("getbug => 0");
+    return 0;
+}
+
+sub cancelbug {
+    &dlen("cancelbug");
+    $lowstate eq 'open' || die "$state ?";
+    &unfilelock;
+    $lowstate= 'idle';
+    &dlex("cancelbug");
+}
+
+sub savebug {
+    &dlen("savebug $ref");
+    $lowstate eq 'open' || die "$lowstate ?";
+    length($action) || die;
+    $ref == $sref || die "read $sref but saving $ref ?";
+    append_action_to_log(bug => $ref,
+                        action => $action,
+                        requester => $header{from},
+                        request_addr => $controlrequestaddr,
+                        message => \@log,
+                        get_lock => 0,
+                       );
+    unlockwritebug($ref, $data);
+    $lowstate= "idle";
+    &dlex("savebug");
+}
+
+sub dlen {
+    return if !$dl;
+    &transcript("C> @_ ($state $lowstate $mergelowstate)\n");
+}
+
+sub dlex {
+    return if !$dl;
+    &transcript("R> @_ ($state $lowstate $mergelowstate)\n");
+}
+
+sub transcript {
+    print $_[0] if $debug;
+    $transcript.= $_[0];
+}
+
+sub urlsanit {
+    my $url = shift;
+    $url =~ s/%/%25/g;
+    $url =~ s/\+/%2b/g;
+    my %saniarray = ('<','lt', '>','gt', '&','amp', '"','quot');
+    $url =~ s/([<>&"])/\&$saniarray{$1};/g;
+    return $url;
+}
+
+sub sendlynxdoc {
+    &sendlynxdocraw;
+    &transcript("\n");
+    $ok++;
+}
+
+sub sendtxthelp {
+    &sendtxthelpraw;
+    &transcript("\n");
+    $ok++;
+}
+
+sub sendtxthelpraw {
+    local ($relpath,$description) = @_;
+    $doc='';
+    open(D,"$gDocDir/$relpath") || &quit("open doc file $relpath: $!");
+    while(<D>) { $doc.=$_; }
+    close(D);
+    &transcript("Sending $description in separate message.\n");
+    &sendmailmessage(<<END.$doc,$replyto);
+From: $gMaintainerEmail ($gProject $gBug Tracking System)
+To: $replyto
+Subject: $gProject $gBug help: $description
+References: $header{'message-id'}
+In-Reply-To: $header{'message-id'}
+Message-ID: <handler.s.$nn.help.$midix\@$gEmailDomain>
+Precedence: bulk
+X-$gProject-PR-Message: doc-text $relpath
+
+END
+    $ok++;
+}
+
+sub sendlynxdocraw {
+    local ($relpath,$description) = @_;
+    $doc='';
+    open(L,"lynx -nolist -dump http://$gCGIDomain/\Q$relpath\E 2>&1 |") || &quit("fork for lynx: $!");
+    while(<L>) { $doc.=$_; }
+    $!=0; close(L);
+    if ($? == 255 && $doc =~ m/^\n*lynx: Can\'t access start file/) {
+        &transcript("Information ($description) is not available -\n".
+                    "perhaps the $gBug does not exist or is not on the WWW yet.\n");
+         $ok++;
+    } elsif ($?) {
+        &transcript("Error getting $description (code $? $!):\n$doc\n");
+    } else {
+        &transcript("Sending $description.\n");
+        &sendmailmessage(<<END.$doc,$replyto);
+From: $gMaintainerEmail ($gProject $gBug Tracking System)
+To: $replyto
+Subject: $gProject $gBugs information: $description
+References: $header{'message-id'}
+In-Reply-To: $header{'message-id'}
+Message-ID: <handler.s.$nn.info.$midix\@$gEmailDomain>
+Precedence: bulk
+X-$gProject-PR-Message: doc-html $relpath
+
+END
+         $ok++;
+    }
+}
+
+sub addccaddress {
+    my ($cca) = @_;
+    $maintccreasons{$cca}{''}{$ref}= 1;
+}
+
+sub addmaintainers {
+    # Data structure is:
+    #   maintainer email address &c -> assoc of packages -> assoc of bug#'s
+    my $data = shift;
+    my ($p, $addmaint);
+    &ensuremaintainersloaded;
+    $anymaintfound=0; $anymaintnotfound=0;
+    for $p (split(m/[ \t?,():]+/, $data->{package})) {
+       $p =~ y/A-Z/a-z/;
+       $p =~ /([a-z0-9.+-]+)/;
+       $p = $1;
+       next unless defined $p;
+       if (defined $gSubscriptionDomain) {
+           if (defined($pkgsrc{$p})) {
+               addbcc("$pkgsrc{$p}\@$gSubscriptionDomain");
+           } else {
+               addbcc("$p\@$gSubscriptionDomain");
+           }
+       }
+        if (defined $data->{severity} and defined $gStrongList and
+                isstrongseverity($data->{severity})) {
+            addbcc("$gStrongList\@$gListDomain");
+        }
+        if (defined($maintainerof{$p})) {
+           $addmaint= $maintainerof{$p};
+           &transcript("MR|$addmaint|$p|$ref|\n") if $dl>2;
+            $maintccreasons{$addmaint}{$p}{$ref}= 1;
+           print "maintainer add >$p|$addmaint<\n" if $debug;
+        } else { 
+           print "maintainer none >$p<\n" if $debug; 
+           &transcript("Warning: Unknown package '$p'\n");
+           &transcript("MR|unknown-package|$p|$ref|\n") if $dl>2;
+            $maintccreasons{$gUnknownMaintainerEmail}{$p}{$ref}= 1;
+       }
+    }
+
+    if (length $data->{owner}) {
+        $addmaint = $data->{owner};
+        &transcript("MO|$addmaint|$data->{package}|$ref|\n") if $dl>2;
+        $maintccreasons{$addmaint}{$data->{package}}{$ref} = 1;
+        print "owner add >$data->{package}|$addmaint<\n" if $debug;
+    }
+}
+
+sub ensuremaintainersloaded {
+    my ($a,$b);
+    return if $maintainersloaded++;
+    open(MAINT,"$gMaintainerFile") || die &quit("maintainers open: $!");
+    while (<MAINT>) {
+       m/^\n$/ && next;
+       m/^\s*$/ && next;
+        m/^(\S+)\s+(\S.*\S)\s*\n$/ || &quit("maintainers bogus \`$_'");
+        $a= $1; $b= $2; $a =~ y/A-Z/a-z/;
+        $maintainerof{$a}= $2;
+    }
+    close(MAINT);
+    open(MAINT,"$gMaintainerFileOverride") || die &quit("maintainers.override open: $!");
+    while (<MAINT>) {
+        m/^\n$/ && next;
+        m/^\s*$/ && next;
+        m/^(\S+)\s+(\S.*\S)\s*\n$/ || &quit("maintainers.override bogus \`$_'");
+        $a= $1; $b= $2; $a =~ y/A-Z/a-z/;
+        $maintainerof{$a}= $2;
+    }
+
+    open(SOURCES, "$gPackageSource") || &quit("pkgsrc open: $!");
+    while (<SOURCES>) {
+       next unless m/^(\S+)\s+\S+\s+(\S.*\S)\s*$/;
+       my ($a, $b) = ($1, $2);
+       $pkgsrc{lc($a)} = $b;
+    }
+    close(SOURCES);
+}
+
+sub sendinfo {
+    local ($wherefrom,$path,$description) = @_;
+    if ($wherefrom eq "ftp.d.o") {
+      $doc = `lynx -nolist -dump http://ftp.debian.org/debian/indices/$path.gz 2>&1 | gunzip -cf` or &quit("fork for lynx/gunzip: $!");
+      $! = 0;
+      if ($? == 255 && $doc =~ m/^\n*lynx: Can\'t access start file/) {
+          &transcript("$description is not available.\n");
+          $ok++; return;
+      } elsif ($?) {
+          &transcript("Error getting $description (code $? $!):\n$doc\n");
+          return;
+      }
+    } elsif ($wherefrom eq "local") {
+      open P, "$path";
+      $doc = do { local $/; <P> };
+      close P;
+    } else {
+      &transcript("internal errror: info files location unknown.\n");
+      $ok++; return;
+    }
+    &transcript("Sending $description.\n");
+    &sendmailmessage(<<END.$doc,$replyto);
+From: $gMaintainerEmail ($gProject $gBug Tracking System)
+To: $replyto
+Subject: $gProject $gBugs information: $description
+References: $header{'message-id'}
+In-Reply-To: $header{'message-id'}
+Message-ID: <handler.s.$nn.info.$midix\@$gEmailDomain>
+Precedence: bulk
+X-$gProject-PR-Message: getinfo
+
+$description follows:
+
+END
+    $ok++;
+    &transcript("\n");
+}
diff --git a/scripts/service.in b/scripts/service.in
deleted file mode 100755 (executable)
index 05925d2..0000000
+++ /dev/null
@@ -1,1818 +0,0 @@
-#!/usr/bin/perl
-# $Id: service.in,v 1.118 2005/10/19 01:22:14 don Exp $
-#
-# Usage: service <code>.nn
-# Temps:  incoming/P<code>.nn
-
-use File::Copy;
-use MIME::Parser;
-use Debbugs::MIME qw(decode_rfc1522 encode_rfc1522);
-use Debbugs::Mail qw(send_mail_message);
-use Debbugs::User;
-use HTML::Entities qw(encode_entities);
-use Debbugs::Versions::Dpkg;
-
-use Debbugs::Config qw(:globals :config);
-use Debbugs::CGI qw(html_escape);
-use Debbugs::Control qw(:archive :log);
-use Debbugs::Log qw(:misc);
-use Debbugs::Text qw(:templates);
-
-use Mail::RFC822::Address;
-
-$lib_path = $gLibPath;
-require "$lib_path/errorlib";
-$ENV{'PATH'} = $lib_path.':'.$ENV{'PATH'};
-
-chdir("$gSpoolDir") || die "chdir spool: $!\n";
-
-# open(DEBUG,">&4");
-open DEBUG, ">/dev/null";
-$debug = 0;
-umask(002);
-
-$_=shift;
-m/^[RC]\.\d+$/ || &quit("bad argument");
-$control= m/C/;
-$nn= $_;
-if (!rename("incoming/G$nn","incoming/P$nn")) {
-    $_=$!.'';  m/no such file or directory/i && exit 0;
-    &quit("renaming to lock: $!");
-}    
-
-open(M,"incoming/P$nn");
-@log=<M>;
-@msg=@log;
-close(M);
-
-chomp @msg;
-
-print "###\n",join("##\n",@msg),"\n###\n" if $debug;
-
-my $parser = new MIME::Parser;
-mkdir "$gSpoolDir/mime.tmp", 0777;
-$parser->output_under("$gSpoolDir/mime.tmp");
-my $entity = eval { $parser->parse_data(join('',@log)) };
-
-# header and decoded body respectively
-my (@headerlines, @bodylines);
-# Bug numbers to send e-mail to, hash so that we don't send to the
-# same bug twice.
-my (%bug_affected);
-
-if ($entity and $entity->head->tags) {
-    # Use map instead of chomp to also kill \r.
-    @headerlines = map {s/\r?\n?$//; $_;}
-        @{$entity->head->header};
-
-    my $entity_body = getmailbody($entity);
-    @bodylines = map {s/\r?\n$//; $_;}
-        $entity_body ? $entity_body->as_lines() : ();
-} else {
-    # Legacy pre-MIME code, kept around in case MIME::Parser fails.
-    my $i;
-    for ($i = 0; $i <= $#msg; $i++) {
-       $_ = $msg[$i];
-       last unless length($_);
-       while ($msg[$i+1] =~ m/^\s/) {
-           $i++;
-           $_ .= "\n".$msg[$i];
-       }
-       push @headerlines, $_;
-    }
-
-    @bodylines = @msg[$i..$#msg];
-}
-
-for (@headerlines) {
-    $_ = decode_rfc1522($_);
-    s/\n\s/ /g;
-    print ">$_<\n" if $debug;
-    if (s/^(\S+):\s*//) {
-       my $v = lc $1;
-       print ">$v=$_<\n" if $debug;
-       $header{$v} = $_;
-    } else {
-       print "!>$_<\n" if $debug;
-    }
-}
-
-# Strip off RFC2440-style PGP clearsigning.
-if (@bodylines and $bodylines[0] =~ /^-----BEGIN PGP SIGNED/) {
-    shift @bodylines while @bodylines and length $bodylines[0];
-    shift @bodylines while @bodylines and $bodylines[0] !~ /\S/;
-    for my $findsig (0 .. $#bodylines) {
-       if ($bodylines[$findsig] =~ /^-----BEGIN PGP SIGNATURE/) {
-           $#bodylines = $findsig - 1;
-           last;
-       }
-    }
-    map { s/^- // } @bodylines;
-}
-
-grep(s/\s+$//,@bodylines);
-
-print "***\n",join("\n",@bodylines),"\n***\n" if $debug;
-
-if (defined $header{'resent-from'} && !defined $header{'from'}) {
-    $header{'from'} = $header{'resent-from'};
-}
-
-defined($header{'from'}) || &quit("no From header");
-
-delete $header{'reply-to'} 
-       if ( defined($header{'reply-to'}) && $header{'reply-to'} =~ m/^\s*$/ );
-
-if ( defined($header{'reply-to'}) && $header{'reply-to'} ne "" ) {
-    $replyto = $header{'reply-to'};
-} else {
-    $replyto = $header{'from'};
-}
-
-# This is an error counter which should be incremented every time there is an error.
-my $errors = 0;
-$controlrequestaddr= $control ? "control\@$gEmailDomain" : "request\@$gEmailDomain";
-$transcript='';
-&transcript("Processing commands for $controlrequestaddr:\n\n");
-
-$dl= 0;
-$state= 'idle';
-$lowstate= 'idle';
-$mergelowstate= 'idle';
-$midix=0;    
-$extras="";
-
-my $user = $replyto;
-$user =~ s/,.*//;
-$user =~ s/^.*<(.*)>.*$/$1/;
-$user =~ s/[(].*[)]//;
-$user =~ s/^\s*(\S+)\s+.*$/$1/;
-$user = "" unless (Debbugs::User::is_valid_user($user));
-my $indicated_user = 0;
-
-my $quickabort = 0;
-
-my $fuckheads = "(" . join("|", @gExcludeFromControl) . ")";
-if (@gExcludeFromControl and $replyto =~ m/$fuckheads/) {
-       &transcript(fill_template('mail/excluded_from_control'));
-       $quickabort = 1;
-}
-
-my %limit_pkgs = ();
-my %clonebugs = ();
-my @bcc = ();
-
-sub addbcc {
-    push @bcc, $_[0] unless grep { $_ eq $_[0] } @bcc;
-}
-
-for ($procline=0; $procline<=$#bodylines; $procline++) {
-    $state eq 'idle' || print "$state ?\n";
-    $lowstate eq 'idle' || print "$lowstate ?\n";
-    $mergelowstate eq 'idle' || print "$mergelowstate ?\n";
-    if ($quickabort) {
-         &transcript("Stopping processing here.\n\n");
-        last;
-    }
-    $_= $bodylines[$procline]; s/\s+$//;
-    next unless m/\S/;
-    &transcript("> $_\n");
-    next if m/^\s*\#/;
-    $action= '';
-    if (m/^stop\s*$/i || m/^quit\s*$/i || m/^--\s*$/ || m/^thank(?:s|\s*you)?\s*$/i || m/^kthxbye\s*$/i) {
-       &transcript("Stopping processing here.\n\n");
-        last;
-    } elsif (m/^debug\s+(\d+)$/i && $1 >= 0 && $1 <= 1000) {
-        $dl= $1+0;
-        &transcript("Debug level $dl.\n\n");
-    } elsif (m/^(send|get)\s+\#?(\d{2,})$/i) {
-        $ref= $2+0;
-        &sendlynxdoc("bugreport.cgi?bug=$ref","logs for $gBug#$ref");
-    } elsif (m/^send-detail\s+\#?(\d{2,})$/i) {
-       $ref= $1+0;
-       &sendlynxdoc("bugreport.cgi?bug=$ref&boring=yes",
-                    "detailed logs for $gBug#$ref");
-    } elsif (m/^index(\s+full)?$/i) {
-       &transcript("This BTS function is currently disabled, sorry.\n\n");
-       $errors++;
-       $ok++; # well, it's not really ok, but it fixes #81224 :)
-    } elsif (m/^index-summary\s+by-package$/i) {
-       &transcript("This BTS function is currently disabled, sorry.\n\n");
-       $errors++;
-       $ok++; # well, it's not really ok, but it fixes #81224 :)
-    } elsif (m/^index-summary(\s+by-number)?$/i) {
-       &transcript("This BTS function is currently disabled, sorry.\n\n");
-       $errors++;
-       $ok++; # well, it's not really ok, but it fixes #81224 :)
-    } elsif (m/^index(\s+|-)pack(age)?s?$/i) {
-       &sendlynxdoc("pkgindex.cgi?indexon=pkg",'index of packages');
-    } elsif (m/^index(\s+|-)maints?$/i) {
-       &sendlynxdoc("pkgindex.cgi?indexon=maint",'index of maintainers');
-    } elsif (m/^index(\s+|-)maint\s+(\S+)$/i) {
-       $maint = $2;
-       &sendlynxdoc("pkgreport.cgi?maint=" . urlsanit($maint),
-                    "$gBug list for maintainer \`$maint'");
-        $ok++;
-    } elsif (m/^index(\s+|-)pack(age)?s?\s+(\S.*\S)$/i) {
-       $package = $+;
-       &sendlynxdoc("pkgreport.cgi?pkg=" . urlsanit($package),
-                    "$gBug list for package $package");
-        $ok++;
-    } elsif (m/^send-unmatched(\s+this|\s+-?0)?$/i) {
-       &transcript("This BTS function is currently disabled, sorry.\n\n");
-       $errors++;
-       $ok++; # well, it's not really ok, but it fixes #81224 :)
-    } elsif (m/^send-unmatched\s+(last|-1)$/i) {
-       &transcript("This BTS function is currently disabled, sorry.\n\n");
-       $errors++;
-       $ok++; # well, it's not really ok, but it fixes #81224 :)
-    } elsif (m/^send-unmatched\s+(old|-2)$/i) {
-       &transcript("This BTS function is currently disabled, sorry.\n\n");
-       $errors++;
-       $ok++; # well, it's not really ok, but it fixes #81224 :)
-    } elsif (m/^getinfo\s+([\w-.]+)$/i) {
-        # the following is basically a Debian-specific kludge, but who cares
-        $req = $1;
-       if ($req =~ /^maintainers$/i && -f "$gConfigDir/Maintainers") {
-           &sendinfo("local", "$gConfigDir/Maintainers", "Maintainers file");
-       } elsif ($req =~ /^override\.(\w+)\.([\w-.]+)$/i) {
-           $req =~ s/.gz$//;
-           &sendinfo("ftp.d.o", "$req", "override file for $2 part of $1 distribution");
-       } elsif ($req =~ /^pseudo-packages\.(description|maintainers)$/i && -f "$gConfigDir/$req") {
-           &sendinfo("local", "$gConfigDir/$req", "$req file");
-       } else {
-           &transcript("Info file $req does not exist.\n\n");
-       }
-    } elsif (m/^help/i) {
-        &sendhelp;
-        &transcript("\n");
-        $ok++;
-    } elsif (m/^refcard/i) {
-        &sendtxthelp("bug-mailserver-refcard.txt","mail servers' reference card");
-    } elsif (m/^subscribe/i) {
-        &transcript(<<END);
-There is no $gProject $gBug mailing list.  If you wish to review bug reports
-please do so via http://$gWebDomain/ or ask this mail server
-to send them to you.
-soon: MAILINGLISTS_TEXT
-END
-    } elsif (m/^unsubscribe/i) {
-        &transcript(<<END);
-soon: UNSUBSCRIBE_TEXT
-soon: MAILINGLISTS_TEXT
-END
-    } elsif (m/^user\s+(\S+)\s*$/i) {
-        my $newuser = $1;
-       if (Debbugs::User::is_valid_user($newuser)) {
-           my $olduser = ($user ne "" ? " (was $user)" : "");
-            &transcript("Setting user to $newuser$olduser.\n");
-           $user = $newuser;
-           $indicated_user = 1;
-       } else {
-           &transcript("Selected user id ($newuser) invalid, sorry\n");
-           $errors++;
-           $user = "";
-           $indicated_user = 1;
-       }
-    } elsif (m/^usercategory\s+(\S+)(\s+\[hidden\])?\s*$/i) {
-        $ok++;
-       my $catname = $1;
-       my $hidden = ($2 ne "");
-
-        my $prefix = "";
-        my @cats;
-        my $bad = 0;
-       my $catsec = 0;
-       if ($user eq "") {
-           &transcript("No valid user selected\n");
-           $errors++;
-           next;
-        }
-       if (not $indicated_user and defined $user) {
-            &transcript("User is $user\n");
-            $indicated_user = 1;
-       }
-       while (++$procline <= $#bodylines) {
-            unless ($bodylines[$procline] =~ m/^\s*([*+])\s*(\S.*)$/) {
-                $procline--;
-                last;
-            }
-            &transcript("> $bodylines[$procline]\n");
-            next if $bad;
-            my ($o, $txt) = ($1, $2);
-            if ($#cats == -1 && $o eq "+") {
-                &transcript("User defined category specification must start with a category name. Skipping.\n\n");
-               $errors++;
-                $bad = 1;
-                next;
-            }
-            if ($o eq "+") {
-               unless (ref($cats[-1]) eq "HASH") {
-                   $cats[-1] = { "nam" => $cats[-1], 
-                                 "pri" => [], "ttl" => [] };
-               }
-               $catsec++;
-               my ($desc, $ord, $op);
-                if ($txt =~ m/^(.*\S)\s*\[((\d+):\s*)?\]\s*$/) {
-                    $desc = $1; $ord = $3; $op = "";
-                } elsif ($txt =~ m/^(.*\S)\s*\[((\d+):\s*)?(\S+)\]\s*$/) {
-                    $desc = $1; $ord = $3; $op = $4;
-                } elsif ($txt =~ m/^([^[\s]+)\s*$/) {
-                    $desc = ""; $op = $1;
-                } else {
-                    &transcript("Unrecognised syntax for category section. Skipping.\n\n");
-                   $errors++;
-                    $bad = 1;
-                    next;
-                }
-               $ord = 999 unless defined $ord;
-
-               if ($op) {
-                    push @{$cats[-1]->{"pri"}}, $prefix . $op;
-                   push @{$cats[-1]->{"ttl"}}, $desc;
-                   push @ords, "$ord $catsec";
-               } else {
-                   @cats[-1]->{"def"} = $desc;
-                   push @ords, "$ord DEF";
-                   $catsec--;
-               }
-               @ords = sort { my ($a1, $a2, $b1, $b2) = split / /, "$a $b";
-                              $a1 <=> $b1 || $a2 <=> $b2; } @ords;
-               $cats[-1]->{"ord"} = [map { m/^.* (\S+)/; $1 eq "DEF" ? $catsec + 1 : $1 } @ords];
-            } elsif ($o eq "*") {
-               $catsec = 0;
-                my ($name);
-                if ($txt =~ m/^(.*\S)(\s*\[(\S+)\])\s*$/) {
-                    $name = $1; $prefix = $3;
-                } else {
-                    $name = $txt; $prefix = "";
-                }
-                push @cats, $name;
-            }
-        }
-        # XXX: got @cats, now do something with it
-       my $u = Debbugs::User::get_user($user);
-       if (@cats) {
-           &transcript("Added usercategory $catname.\n\n");
-           $u->{"categories"}->{$catname} = [ @cats ];
-           if (not $hidden) {
-                push @{$u->{visible_cats}},$catname;
-           }
-       } else {
-           &transcript("Removed usercategory $catname.\n\n");
-           delete $u->{"categories"}->{$catname};
-           @{$u->{visible_cats}} = grep {$_ ne $catname} @{$u->{visible_cats}};
-       }
-       $u->write();
-    } elsif (m/^usertags?\s+\#?(-?\d+)\s+(([=+-])\s*)?(\S.*)?$/i) {
-       $ok++;
-       $ref = $1; $addsubcode = $3 || "+"; $tags = $4;
-       if ($ref =~ m/^-\d+$/ && defined $clonebugs{$ref}) {
-            $ref = $clonebugs{$ref};
-        }
-       if ($user eq "") {
-           &transcript("No valid user selected\n");
-           $errors++;
-           $indicated_user = 1;
-        } elsif (&setbug) {
-           if (not $indicated_user and defined $user) {
-                &transcript("User is $user\n");
-                $indicated_user = 1;
-           }
-           &nochangebug;
-           my %ut;
-           Debbugs::User::read_usertags(\%ut, $user);
-            my @oldtags = (); my @newtags = (); my @badtags = ();
-           my %chtags;
-           for my $t (split /[,\s]+/, $tags) {
-               if ($t =~ m/^[a-zA-Z0-9.+\@-]+$/) {
-                   $chtags{$t} = 1;
-               } else {
-                   push @badtags, $t;
-               }
-           }
-           if (@badtags) {
-                &transcript("Ignoring illegal tag/s: ".join(', ', @badtags).".\nPlease use only alphanumerics, at, dot, plus and dash.\n");
-               $errors++;
-           }
-            for my $t (keys %chtags) {
-               $ut{$t} = [] unless defined $ut{$t};
-           }
-           for my $t (keys %ut) {
-               my %res = map { ($_, 1) } @{$ut{$t}};
-               push @oldtags, $t if defined $res{$ref};
-               my $addop = ($addsubcode eq "+" or $addsubcode eq "=");
-               my $del = (defined $chtags{$t} ? $addsubcode eq "-" 
-                                              : $addsubcode eq "=");
-               $res{$ref} = 1 if ($addop && defined $chtags{$t});
-               delete $res{$ref} if ($del);
-               push @newtags, $t if defined $res{$ref};
-               $ut{$t} = [ sort { $a <=> $b } (keys %res) ];
-           }
-           if (@oldtags == 0) {
-               &transcript("There were no usertags set.\n");
-           } else {
-               &transcript("Usertags were: " . join(" ", @oldtags) . ".\n");
-           }
-           &transcript("Usertags are now: " . join(" ", @newtags) . ".\n");
-           Debbugs::User::write_usertags(\%ut, $user);
-       }
-    } elsif (!$control) {
-        &transcript(<<END);
-Unknown command or malformed arguments to command.
-(Use control\@$gEmailDomain to manipulate reports.)
-
-END
-       $errors++;
-        if (++$unknowns >= 3) {
-            &transcript("Too many unknown commands, stopping here.\n\n");
-            last;
-        }
-#### "developer only" ones start here
-    } elsif (m/^close\s+\#?(-?\d+)(?:\s+(\d.*))?$/i) {
-       $ok++;
-       $ref= $1;
-       $bug_affected{$ref}=1;
-       $version= $2;
-       if (&setbug) {
-           &transcript("'close' is deprecated; see http://$gWebDomain/Developer$gHTMLSuffix#closing.\n");
-           if (length($data->{done}) and not defined($version)) {
-               &transcript("$gBug is already closed, cannot re-close.\n\n");
-                &nochangebug;
-            } else {
-                $action= "$gBug " .
-                    (defined($version) ?
-                        "marked as fixed in version $version" :
-                        "closed") .
-                    ", send any further explanations to $data->{originator}";
-                do {
-                    &addmaintainers($data);
-                                       if ( length( $gDoneList ) > 0 && length( $gListDomain ) >
-                                       0 ) { &addccaddress("$gDoneList\@$gListDomain"); }
-                    $data->{done}= $replyto;
-                    my @keywords= split ' ', $data->{keywords};
-                    if (grep $_ eq 'pending', @keywords) {
-                        $extramessage= "Removed pending tag.\n";
-                        $data->{keywords}= join ' ', grep $_ ne 'pending',
-                                                @keywords;
-                    }
-                    addfixedversions($data, $data->{package}, $version, 'binary');
-
-                   $message= <<END;
-From: $gMaintainerEmail ($gProject $gBug Tracking System)
-To: $data->{originator}
-Subject: $gBug#$ref acknowledged by developer
-         ($header{'subject'})
-References: $header{'message-id'} $data->{msgid}
-In-Reply-To: $data->{msgid}
-Message-ID: <handler.$ref.$nn.notifdonectrl.$midix\@$gEmailDomain>
-Reply-To: $ref\@$gEmailDomain
-X-$gProject-PR-Message: they-closed-control $ref
-
-This is an automatic notification regarding your $gBug report
-#$ref: $data->{subject},
-which was filed against the $data->{package} package.
-
-It has been marked as closed by one of the developers, namely
-$replyto.
-
-You should be hearing from them with a substantive response shortly,
-in case you haven't already. If not, please contact them directly.
-
-$gMaintainer
-(administrator, $gProject $gBugs database)
-
-END
-                    &sendmailmessage($message,$data->{originator});
-                } while (&getnextbug);
-            }
-        }
-    } elsif (m/^reassign\s+\#?(-?\d+)\s+(\S+)(?:\s+(\d.*))?$/i) {
-        $ok++;
-        $ref= $1; $newpackage= $2;
-       $bug_affected{$ref}=1;
-        $version= $3;
-       $newpackage =~ y/A-Z/a-z/;
-        if (&setbug) {
-            if (length($data->{package})) {
-                $action= "$gBug reassigned from package \`$data->{package}'".
-                         " to \`$newpackage'.";
-            } else {
-                $action= "$gBug assigned to package \`$newpackage'.";
-            }
-            do {
-                &addmaintainers($data);
-                $data->{package}= $newpackage;
-                $data->{found_versions}= [];
-                $data->{fixed_versions}= [];
-                # TODO: what if $newpackage is a source package?
-                addfoundversions($data, $data->{package}, $version, 'binary');
-                &addmaintainers($data);
-            } while (&getnextbug);
-        }
-    } elsif (m/^reopen\s+\#?(-?\d+)$/i ? ($noriginator='', 1) :
-             m/^reopen\s+\#?(-?\d+)\s+\=$/i ? ($noriginator='', 1) :
-             m/^reopen\s+\#?(-?\d+)\s+\!$/i ? ($noriginator=$replyto, 1) :
-             m/^reopen\s+\#?(-?\d+)\s+(\S.*\S)$/i ? ($noriginator=$2, 1) : 0) {
-        $ok++;
-        $ref= $1;
-       $bug_affected{$ref}=1;
-        if (&setbug) {
-            if (@{$data->{fixed_versions}}) {
-                &transcript("'reopen' may be inappropriate when a bug has been closed with a version;\nyou may need to use 'found' to remove fixed versions.\n");
-            }
-            if (!length($data->{done})) {
-                &transcript("$gBug is already open, cannot reopen.\n\n");
-                &nochangebug;
-            } else {
-                $action=
-                    $noriginator eq '' ? "$gBug reopened, originator not changed." :
-                        "$gBug reopened, originator set to $noriginator.";
-                do {
-                    &addmaintainers($data);
-                    $data->{originator}= $noriginator eq '' ?  $data->{originator} : $noriginator;
-                    $data->{fixed_versions}= [];
-                    $data->{done}= '';
-                } while (&getnextbug);
-            }
-        }
-    } elsif (m{^found\s+\#?(-?\d+)
-              (?:\s+((?:$config{package_name_re}\/)?
-                   $config{package_version_re}))?$}ix) {
-        $ok++;
-        $ref= $1;
-        $version= $2;
-        if (&setbug) {
-            if (!length($data->{done}) and not defined($version)) {
-                &transcript("$gBug is already open, cannot reopen.\n\n");
-               $errors++;
-                &nochangebug;
-            } else {
-                $action=
-                    defined($version) ?
-                        "$gBug marked as found in version $version." :
-                        "$gBug reopened.";
-                do {
-                    &addmaintainers($data);
-                    # The 'done' field gets a bit weird with version
-                    # tracking, because a bug may be closed by multiple
-                    # people in different branches. Until we have something
-                    # more flexible, we set it every time a bug is fixed,
-                    # and clear it when a bug is found in a version greater
-                   # than any version in which the bug is fixed or when
-                   # a bug is found and there is no fixed version
-                   if (defined $version) {
-                       my ($version_only) = $version =~ m{([^/]+)$};
-                        addfoundversions($data, $data->{package}, $version, 'binary');
-                       my @fixed_order = sort {Debbugs::Versions::Dpkg::vercmp($a,$b);}
-                            map {s{.+/}{}; $_;} @{$data->{fixed_versions}};
-                       if (not @fixed_order or (Debbugs::Versions::Dpkg::vercmp($version_only,$fixed_order[-1]) >= 0)) {
-                            $action = "$gBug marked as found in version $version and reopened."
-                                 if length $data->{done};
-                            $data->{done} = '';
-                       }
-                    } else {
-                        # Versionless found; assume old-style "not fixed at
-                        # all".
-                        $data->{fixed_versions} = [];
-                        $data->{done} = '';
-                    }
-                } while (&getnextbug);
-            }
-        }
-    } elsif (m[^notfound\s+\#?(-?\d+)\s+
-              ((?:$config{package_name_re}\/)?
-                   \S+)\s*$]ix) {
-        $ok++;
-        $ref= $1;
-        $version= $2;
-        if (&setbug) {
-            $action= "$gBug no longer marked as found in version $version.";
-            if (length($data->{done})) {
-                $extramessage= "(By the way, this $gBug is currently marked as done.)\n";
-            }
-            do {
-                &addmaintainers($data);
-                removefoundversions($data, $data->{package}, $version, 'binary');
-            } while (&getnextbug);
-       }
-   }
-    elsif (m[^fixed\s+\#?(-?\d+)\s+
-            ((?:$config{package_name_re}\/)?
-                 $config{package_version_re})\s*$]ix) {
-        $ok++;
-        $ref= $1;
-        $version= $2;
-        if (&setbug) {
-            $action=
-                 defined($version) ?
-                      "$gBug marked as fixed in version $version." :
-                           "$gBug reopened.";
-                do {
-                    &addmaintainers($data);
-                    addfixedversions($data, $data->{package}, $version, 'binary');
-              } while (&getnextbug);
-       }
-   }
-    elsif (m[^notfixed\s+\#?(-?\d+)\s+
-            ((?:$config{package_name_re}\/)?
-                 \S+)\s*$]ix) {
-        $ok++;
-        $ref= $1;
-        $version= $2;
-        if (&setbug) {
-            $action=
-                 defined($version) ?
-                      "$gBug no longer marked as fixed in version $version." :
-                           "$gBug reopened.";
-                do {
-                    &addmaintainers($data);
-                    removefixedversions($data, $data->{package}, $version, 'binary');
-              } while (&getnextbug);
-       }
-   }
-    elsif (m/^submitter\s+\#?(-?\d+)\s+\!$/i ? ($newsubmitter=$replyto, 1) :
-             m/^submitter\s+\#?(-?\d+)\s+(\S.*\S)$/i ? ($newsubmitter=$2, 1) : 0) {
-        $ok++;
-        $ref= $1;
-       $bug_affected{$ref}=1;
-        if ($ref =~ m/^-\d+$/ && defined $clonebugs{$ref}) {
-            $ref = $clonebugs{$ref};
-        }
-       if (not Mail::RFC822::Address::valid($newsubmitter)) {
-            transcript("$newsubmitter is not a valid e-mail address; not changing submitter\n");
-            $errors++;
-       }
-        elsif (&getbug) {
-            if (&checkpkglimit) {
-                &foundbug;
-                &addmaintainers($data);
-                $oldsubmitter= $data->{originator};
-                $data->{originator}= $newsubmitter;
-                $action= "Changed $gBug submitter from $oldsubmitter to $newsubmitter.";
-                &savebug;
-                &transcript("$action\n");
-                if (length($data->{done})) {
-                    &transcript("(By the way, that $gBug is currently marked as done.)\n");
-                }
-                &transcript("\n");
-                $message= <<END;
-From: $gMaintainerEmail ($gProject $gBug Tracking System)
-To: $oldsubmitter
-Subject: $gBug#$ref submitter address changed
-         ($header{'subject'})
-References: $header{'message-id'} $data->{msgid}
-In-Reply-To: $data->{msgid}
-Message-ID: <handler.$ref.$nn.newsubmitter.$midix\@$gEmailDomain>
-Reply-To: $ref\@$gEmailDomain
-X-$gProject-PR-Message: submitter-changed $ref
-
-The submitter address recorded for your $gBug report
-#$ref: $data->{subject}
-has been changed.
-
-The old submitter address for this report was
-$oldsubmitter.
-The new submitter address is
-$newsubmitter.
-
-This change was made by
-$replyto.
-If it was incorrect, please contact them directly.
-
-$gMaintainer
-(administrator, $gProject $gBugs database)
-
-END
-                &sendmailmessage($message,$oldsubmitter);
-            } else {
-                &cancelbug;
-            }
-        } else {
-            &notfoundbug;
-        }
-    } elsif (m/^forwarded\s+\#?(-?\d+)\s+(\S.*\S)$/i) {
-        $ok++;
-        $ref= $1; $whereto= $2;
-       $bug_affected{$ref}=1;
-        if (&setbug) {
-            if (length($data->{forwarded})) {
-    $action= "Forwarded-to-address changed from $data->{forwarded} to $whereto.";
-            } else {
-    $action= "Noted your statement that $gBug has been forwarded to $whereto.";
-            }
-            if (length($data->{done})) {
-                $extramessage= "(By the way, this $gBug is currently marked as done.)\n";
-            }
-            do {
-                &addmaintainers($data);
-               if (length($gForwardList)>0 && length($gListDomain)>0 ) {
-                    &addccaddress("$gForwardList\@$gListDomain"); 
-               }
-                $data->{forwarded}= $whereto;
-            } while (&getnextbug);
-        }
-    } elsif (m/^notforwarded\s+\#?(-?\d+)$/i) {
-        $ok++;
-        $ref= $1;
-       $bug_affected{$ref}=1;
-        if (&setbug) {
-            if (!length($data->{forwarded})) {
-                &transcript("$gBug is not marked as having been forwarded.\n\n");
-                &nochangebug;
-            } else {
-    $action= "Removed annotation that $gBug had been forwarded to $data->{forwarded}.";
-                do {
-                    &addmaintainers($data);
-                    $data->{forwarded}= '';
-                } while (&getnextbug);
-            }
-        }
-    } elsif (m/^severity\s+\#?(-?\d+)\s+([-0-9a-z]+)$/i ||
-       m/^priority\s+\#?(-?\d+)\s+([-0-9a-z]+)$/i) {
-        $ok++;
-        $ref= $1;
-       $bug_affected{$ref}=1;
-        $newseverity= $2;
-        if (!grep($_ eq $newseverity, @gSeverityList, "$gDefaultSeverity")) {
-            &transcript("Severity level \`$newseverity' is not known.\n".
-                       "Recognized are: $gShowSeverities.\n\n");
-           $errors++;
-        } elsif (exists $gObsoleteSeverities{$newseverity}) {
-            &transcript("Severity level \`$newseverity' is obsolete. " .
-                        "Use $gObsoleteSeverities{$newseverity} instead.\n\n");
-               $errors++;
-        } elsif (&setbug) {
-            $printseverity= $data->{severity};
-            $printseverity= "$gDefaultSeverity" if $printseverity eq '';
-           $action= "Severity set to \`$newseverity' from \`$printseverity'";
-           do {
-                &addmaintainers($data);
-                if (defined $gStrongList and isstrongseverity($newseverity)) {
-                    addbcc("$gStrongList\@$gListDomain");
-                }
-                $data->{severity}= $newseverity;
-            } while (&getnextbug);
-        }
-    } elsif (m/^tags?\s+\#?(-?\d+)\s+(([=+-])\s*)?(\S.*)?$/i) {
-       $ok++;
-       $ref = $1; $addsubcode = $3; $tags = $4;
-       $bug_affected{$ref}=1;
-       $addsub = "add";
-       if (defined $addsubcode) {
-           $addsub = "sub" if ($addsubcode eq "-");
-           $addsub = "add" if ($addsubcode eq "+");
-           $addsub = "set" if ($addsubcode eq "=");
-       }
-       my @okaytags = ();
-       my @badtags = ();
-       foreach my $t (split /[\s,]+/, $tags) {
-           if (!grep($_ eq $t, @gTags)) {
-               push @badtags, $t;
-           } else {
-               push @okaytags, $t;
-           }
-       }
-       if (@badtags) {
-            &transcript("Unknown tag/s: ".join(', ', @badtags).".\n".
-                       "Recognized are: ".join(' ', @gTags).".\n\n");
-           $errors++;
-       }
-       if (&setbug) {
-           if ($data->{keywords} eq '') {
-               &transcript("There were no tags set.\n");
-           } else {
-               &transcript("Tags were: $data->{keywords}\n");
-           }
-           if ($addsub eq "set") {
-               $action= "Tags set to: " . join(", ", @okaytags);
-           } elsif ($addsub eq "add") {
-               $action= "Tags added: " . join(", ", @okaytags);
-           } elsif ($addsub eq "sub") {
-               $action= "Tags removed: " . join(", ", @okaytags);
-           }
-           do {
-                &addmaintainers($data);
-               $data->{keywords} = '' if ($addsub eq "set");
-               # Allow removing obsolete tags.
-               if ($addsub eq "sub") {
-                   foreach my $t (@badtags) {
-                       $data->{keywords} = join ' ', grep $_ ne $t, 
-                           split ' ', $data->{keywords};
-                   }
-               }
-               # Now process all other additions and subtractions.
-               foreach my $t (@okaytags) {
-                   $data->{keywords} = join ' ', grep $_ ne $t, 
-                       split ' ', $data->{keywords};
-                   $data->{keywords} = "$t $data->{keywords}" unless($addsub eq "sub");
-               }
-               $data->{keywords} =~ s/\s*$//;
-            } while (&getnextbug);
-       }
-    } elsif (m/^(un)?block\s+\#?(-?\d+)\s+(by|with)\s+(\S.*)?$/i) {
-       $ok++;
-       my $bugnum = $2; my $blockers = $4;
-       $addsub = "add";
-       $addsub = "sub" if ($1 eq "un");
-       if ($bugnum =~ m/^-\d+$/ && defined $clonebugs{$bugnum}) {
-            $bugnum = $clonebugs{$bugnum};
-       }
-
-       my @okayblockers;
-       my @badblockers;
-       foreach my $b (split /[\s,]+/, $blockers) {
-           $b=~s/^\#//;
-           if ($b=~/[0-9]+/) {
-               $ref=$b;
-               if ($ref =~ m/^-\d+$/ && defined $clonebugs{$ref}) {
-                    $ref = $clonebugs{$ref};
-               }
-               if (&getbug) {
-                   &foundbug;
-                   push @okayblockers, $ref;
-
-                   # add to the list all bugs that are merged with $b,
-                   # because all of their data must be kept in sync
-                   @thisbugmergelist= split(/ /,$data->{mergedwith});
-                   &cancelbug;
-
-                   foreach $ref (@thisbugmergelist) {
-                       if (&getbug) {
-                          push @okayblockers, $ref;
-                          &cancelbug;
-                       }
-                   }
-               }
-               else {
-                   &notfoundbug;
-                    push @badblockers, $ref;
-               }
-           }
-           else {
-                push @badblockers, $b;
-           }
-       }
-       if (@badblockers) {
-            &transcript("Unknown blocking bug/s: ".join(', ', @badblockers).".\n");
-           $errors++;
-       }
-       
-       $ref=$bugnum;
-       if (&setbug) {
-           if ($data->{blockedby} eq '') {
-               &transcript("Was not blocked by any bugs.\n");
-           } else {
-               &transcript("Was blocked by: $data->{blockedby}\n");
-           }
-           if ($addsub eq "set") {
-               $action= "Blocking bugs of $bugnum set to: " . join(", ", @okayblockers);
-           } elsif ($addsub eq "add") {
-               $action= "Blocking bugs of $bugnum added: " . join(", ", @okayblockers);
-           } elsif ($addsub eq "sub") {
-               $action= "Blocking bugs of $bugnum removed: " . join(", ", @okayblockers);
-           }
-           my %removedblocks;
-           my %addedblocks;
-           do {
-                &addmaintainers($data);
-               my @oldblockerlist = split ' ', $data->{blockedby};
-               $data->{blockedby} = '' if ($addsub eq "set");
-               foreach my $b (@okayblockers) {
-                       $data->{blockedby} = manipset($data->{blockedby}, $b,
-                               ($addsub ne "sub"));
-               }
-
-               foreach my $b (@oldblockerlist) {
-                       if (! grep { $_ eq $b } split ' ', $data->{blockedby}) {
-                               push @{$removedblocks{$b}}, $ref;
-                       }
-               }
-               foreach my $b (split ' ', $data->{blockedby}) {
-                       if (! grep { $_ eq $b } @oldblockerlist) {
-                               push @{$addedblocks{$b}}, $ref;
-                       }
-               }
-            } while (&getnextbug);
-
-           # Now that the blockedby data is updated, change blocks data
-           # to match the changes.
-           foreach $ref (keys %addedblocks) {
-               if (&getbug) {
-                   foreach my $b (@{$addedblocks{$ref}}) {
-                       $data->{blocks} = manipset($data->{blocks}, $b, 1);
-                   }
-                   &savebug;
-                }
-           }
-           foreach $ref (keys %removedblocks) {
-               if (&getbug) {
-                   foreach my $b (@{$removedblocks{$ref}}) {
-                       $data->{blocks} = manipset($data->{blocks}, $b, 0);
-                   }
-                   &savebug;
-                }
-           }
-       }
-    } elsif (m/^retitle\s+\#?(-?\d+)\s+(\S.*\S)\s*$/i) {
-        $ok++;
-        $ref= $1; $newtitle= $2;
-       $bug_affected{$ref}=1;
-       if ($ref =~ m/^-\d+$/ && defined $clonebugs{$ref}) {
-           $ref = $clonebugs{$ref};
-       }
-        if (&getbug) {
-            if (&checkpkglimit) {
-                &foundbug;
-                &addmaintainers($data);
-               my $oldtitle = $data->{subject};
-                $data->{subject}= $newtitle;
-                $action= "Changed $gBug title to `$newtitle' from `$oldtitle'.";
-                &savebug;
-                &transcript("$action\n");
-                if (length($data->{done})) {
-                    &transcript("(By the way, that $gBug is currently marked as done.)\n");
-                }
-                &transcript("\n");
-            } else {
-                &cancelbug;
-            }
-        } else {
-            &notfoundbug;
-        }
-    } elsif (m/^unmerge\s+\#?(-?\d+)$/i) {
-       $ok++;
-       $ref= $1;
-       $bug_affected{$ref} = 1;
-       if (&setbug) {
-           if (!length($data->{mergedwith})) {
-               &transcript("$gBug is not marked as being merged with any others.\n\n");
-               &nochangebug;
-           } else {
-                $mergelowstate eq 'locked' || die "$mergelowstate ?";
-               $action= "Disconnected #$ref from all other report(s).";
-               @newmergelist= split(/ /,$data->{mergedwith});
-                $discref= $ref;
-               @bug_affected{@newmergelist} = 1 x @newmergelist;
-                do {
-                    &addmaintainers($data);
-                   $data->{mergedwith}= ($ref == $discref) ? ''
-                        : join(' ',grep($_ ne $ref,@newmergelist));
-                } while (&getnextbug);
-           }
-       }
-    } elsif (m/^merge\s+#?(-?\d+(\s+#?-?\d+)+)\s*$/i) {
-       $ok++;
-        my @tomerge= sort { $a <=> $b } split(/\s+#?/,$1);
-        my @newmergelist= ();
-       my %tags = ();
-       my %found = ();
-       my %fixed = ();
-        &getmerge;
-        while (defined($ref= shift(@tomerge))) {
-            &transcript("D| checking merge $ref\n") if $dl;
-           $ref+= 0;
-           if ($ref =~ m/^-\d+$/ && defined $clonebugs{$ref}) {
-               $ref = $clonebugs{$ref};
-           }
-           next if grep($_ == $ref,@newmergelist);
-           if (!&getbug) { &notfoundbug; @newmergelist=(); last }
-            if (!&checkpkglimit) { &cancelbug; @newmergelist=(); last; }
-            &foundbug;
-            &transcript("D| adding $ref ($data->{mergedwith})\n") if $dl;
-           $mismatch= '';
-           &checkmatch('package','m_package',$data->{package},@newmergelist);
-           &checkmatch('forwarded addr','m_forwarded',$data->{forwarded},@newmergelist);
-           $data->{severity} = '$gDefaultSeverity' if $data->{severity} eq '';
-           &checkmatch('severity','m_severity',$data->{severity},@newmergelist);
-           &checkmatch('blocks','m_blocks',$data->{blocks},@newmergelist);
-           &checkmatch('blocked-by','m_blockedby',$data->{blockedby},@newmergelist);
-           &checkmatch('done mark','m_done',length($data->{done}) ? 'done' : 'open',@newmergelist);
-           &checkmatch('owner','m_owner',$data->{owner},@newmergelist);
-           foreach my $t (split /\s+/, $data->{keywords}) { $tags{$t} = 1; }
-           foreach my $f (@{$data->{found_versions}}) { $found{$f} = 1; }
-           foreach my $f (@{$data->{fixed_versions}}) { $fixed{$f} = 1; }
-           if (length($mismatch)) {
-               &transcript("Mismatch - only $gBugs in same state can be merged:\n".
-                            $mismatch."\n");
-               $errors++;
-               &cancelbug; @newmergelist=(); last;
-           }
-            push(@newmergelist,$ref);
-            push(@tomerge,split(/ /,$data->{mergedwith}));
-           &cancelbug;
-       }
-       if (@newmergelist) {
-            @newmergelist= sort { $a <=> $b } @newmergelist;
-            $action= "Merged @newmergelist.";
-           delete @fixed{keys %found};
-           for $ref (@newmergelist) {
-               &getbug || die "huh ?  $gBug $ref disappeared during merge";
-                &addmaintainers($data);
-               @bug_affected{@newmergelist} = 1 x @newmergelist;
-               $data->{mergedwith}= join(' ',grep($_ != $ref,@newmergelist));
-               $data->{keywords}= join(' ', keys %tags);
-               $data->{found_versions}= [sort keys %found];
-               $data->{fixed_versions}= [sort keys %fixed];
-               &savebug;
-           }
-           &transcript("$action\n\n");
-       }
-        &endmerge;
-    } elsif (m/^forcemerge\s+\#?(-?\d+(?:\s+\#?-?\d+)+)\s*$/i) {
-       $ok++;
-       my @temp = split /\s+\#?/,$1;
-       my $master_bug = shift @temp;
-       my $master_bug_data;
-       my @tomerge = sort { $a <=> $b } @temp;
-        unshift @tomerge,$master_bug;
-       &transcript("D| force merging ".join(',',@tomerge)."\n") if $dl;
-       my @newmergelist= ();
-       my %tags = ();
-       my %found = ();
-       my %fixed = ();
-       # Here we try to do the right thing.
-       # First, if the bugs are in the same package, we merge all of the found, fixed, and tags.
-       # If not, we discard the found and fixed.
-       # Everything else we set to the values of the first bug.
-        &getmerge;
-        while (defined($ref= shift(@tomerge))) {
-            &transcript("D| checking merge $ref\n") if $dl;
-           $ref+= 0;
-           if ($ref =~ m/^-\d+$/ && defined $clonebugs{$ref}) {
-               $ref = $clonebugs{$ref};
-           }
-           next if grep($_ == $ref,@newmergelist);
-           if (!&getbug) { &notfoundbug; @newmergelist=(); last }
-            if (!&checkpkglimit) { &cancelbug; @newmergelist=(); last; }
-            &foundbug;
-            &transcript("D| adding $ref ($data->{mergedwith})\n") if $dl;
-           $master_bug_data = $data if not defined $master_bug_data;
-           if ($data->{package} ne $master_bug_data->{package}) {
-               &transcript("Mismatch - only $gBugs in the same package can be forcibly merged:\n".
-                           "$gBug $ref is not in the same package as $master_bug\n");
-               $errors++;
-               &cancelbug; @newmergelist=(); last;
-           }
-           for my $t (split /\s+/,$data->{keywords}) {
-                $tags{$t} = 1;
-           }
-           @found{@{$data->{found_versions}}} = (1) x @{$data->{found_versions}};
-           @fixed{@{$data->{fixed_versions}}} = (1) x @{$data->{fixed_versions}};
-           push(@newmergelist,$ref);
-            push(@tomerge,split(/ /,$data->{mergedwith}));
-           &cancelbug;
-       }
-       if (@newmergelist) {
-            @newmergelist= sort { $a <=> $b } @newmergelist;
-            $action= "Forcibly Merged @newmergelist.";
-           delete @fixed{keys %found};
-           for $ref (@newmergelist) {
-               &getbug || die "huh ?  $gBug $ref disappeared during merge";
-                &addmaintainers($data);
-               @bug_affected{@newmergelist} = 1 x @newmergelist;
-               $data->{mergedwith}= join(' ',grep($_ != $ref,@newmergelist));
-               $data->{keywords}= join(' ', keys %tags);
-               $data->{found_versions}= [sort keys %found];
-               $data->{fixed_versions}= [sort keys %fixed];
-               my @field_list = qw(forwarded package severity blocks blockedby owner done);
-               @{$data}{@field_list} = @{$master_bug_data}{@field_list};
-               &savebug;
-           }
-           &transcript("$action\n\n");
-       }
-        &endmerge;
-    } elsif (m/^clone\s+#?(\d+)\s+((-\d+\s+)*-\d+)\s*$/i) {
-       $ok++;
-
-       $origref = $1;
-       @newclonedids = split /\s+/, $2;
-       $newbugsneeded = scalar(@newclonedids);
-
-       $ref = $origref;
-       $bug_affected{$ref} = 1;
-       if (&setbug) {
-           if (length($data->{mergedwith})) {
-               &transcript("$gBug is marked as being merged with others. Use an existing clone.\n\n");
-               $errors++;
-               &nochangebug;
-           } else {
-               &filelock("nextnumber.lock");
-               open(N,"nextnumber") || &quit("nextnumber: read: $!");
-               $v=<N>; $v =~ s/\n$// || &quit("nextnumber bad format");
-               $firstref= $v+0;  $v += $newbugsneeded;
-               open(NN,">nextnumber"); print NN "$v\n"; close(NN);
-               &unfilelock;
-
-               $lastref = $firstref + $newbugsneeded - 1;
-
-               if ($newbugsneeded == 1) {
-                   $action= "$gBug $origref cloned as bug $firstref.";
-               } else {
-                   $action= "$gBug $origref cloned as bugs $firstref-$lastref.";
-               }
-
-               my $blocks = $data->{blocks};
-               my $blockedby = $data->{blockedby};
-               
-               &getnextbug;
-               my $ohash = get_hashname($origref);
-               my $clone = $firstref;
-                @bug_affected{@newclonedids} = 1 x @newclonedids;
-               for $newclonedid (@newclonedids) {
-                   $clonebugs{$newclonedid} = $clone;
-           
-                   my $hash = get_hashname($clone);
-                   copy("db-h/$ohash/$origref.log", "db-h/$hash/$clone.log");
-                   copy("db-h/$ohash/$origref.status", "db-h/$hash/$clone.status");
-                   copy("db-h/$ohash/$origref.summary", "db-h/$hash/$clone.summary");
-                   copy("db-h/$ohash/$origref.report", "db-h/$hash/$clone.report");
-                   &bughook('new', $clone, $data);
-               
-                   # Update blocking info of bugs blocked by or blocking the
-                   # cloned bug.
-                   foreach $ref (split ' ', $blocks) {
-                       &getbug;
-                       $data->{blockedby} = manipset($data->{blockedby}, $clone, 1);
-                       &savebug;
-                   }
-                   foreach $ref (split ' ', $blockedby) {
-                       &getbug;
-                       $data->{blocks} = manipset($data->{blocks}, $clone, 1);
-                       &savebug;
-                   }
-
-                   $clone++;
-               }
-           }
-       }
-    } elsif (m/^package\:?\s+(\S.*\S)?\s*$/i) {
-        $ok++;
-       my @pkgs = split /\s+/, $1;
-       if (scalar(@pkgs) > 0) {
-               %limit_pkgs = map { ($_, 1) } @pkgs;
-               &transcript("Ignoring bugs not assigned to: " . 
-                       join(" ", keys(%limit_pkgs)) . "\n\n");
-       } else {
-               %limit_pkgs = ();
-               &transcript("Not ignoring any bugs.\n\n");
-       }
-    } elsif (m/^owner\s+\#?(-?\d+)\s+!$/i ? ($newowner = $replyto, 1) :
-             m/^owner\s+\#?(-?\d+)\s+(\S.*\S)$/i ? ($newowner = $2, 1) : 0) {
-        $ok++;
-        $ref = $1;
-       $bug_affected{$ref} = 1;
-        if (&setbug) {
-            if (length $data->{owner}) {
-                $action = "Owner changed from $data->{owner} to $newowner.";
-            } else {
-                $action = "Owner recorded as $newowner.";
-            }
-            if (length $data->{done}) {
-                $extramessage = "(By the way, this $gBug is currently " .
-                                "marked as done.)\n";
-            }
-            do {
-                &addmaintainers($data);
-                $data->{owner} = $newowner;
-            } while (&getnextbug);
-        }
-    } elsif (m/^noowner\s+\#?(-?\d+)$/i) {
-        $ok++;
-        $ref = $1;
-       $bug_affected{$ref} = 1;
-        if (&setbug) {
-            if (length $data->{owner}) {
-                $action = "Removed annotation that $gBug was owned by " .
-                          "$data->{owner}.";
-                do {
-                    &addmaintainers($data);
-                    $data->{owner} = '';
-                } while (&getnextbug);
-            } else {
-                &transcript("$gBug is not marked as having an owner.\n\n");
-                &nochangebug;
-            }
-        }
-    } elsif (m/^unarchive\s+#?(\d+)$/i) {
-        $ok++;
-        $ref = $1;
-        $bug_affected{$ref} = 1;
-        my $transcript;
-        eval {
-             bug_unarchive(bug        => $ref,
-                           transcript => \$transcript,
-                           affected_bugs => \%bug_affected,
-                           requester => $header{from},
-                           request_addr => $controlrequestaddr,
-                           message => \@log,
-                          );
-        };
-        if ($@) {
-             $errors++;
-        }
-        transcript($transcript."\n");
-    } elsif (m/^archive\s+#?(\d+)$/i) {
-        $ok++;
-        $ref = $1;
-        $bug_affected{$ref} = 1;
-        if (&setbug) {
-             if (exists $data->{unarchived}) {
-                  my $transcript;
-                  nochangebug();
-                  eval {
-                       bug_archive(bug => $ref,
-                                   transcript => \$transcript,
-                                   ignore_time => 1,
-                                   affected_bugs => \%bug_affected,
-                                   requester => $header{from},
-                                   request_addr => $controlrequestaddr,
-                                   message => \@log,
-                                  );
-                  };
-                  if ($@) {
-                       $errors++;
-                  }
-                  transcript($transcript."\n");
-             }
-             else {
-                  transcript("$gBug $ref has not been archived previously\n\n");
-                  nochangebug();
-                  $errors++;
-             }
-        }
-    } else {
-        &transcript("Unknown command or malformed arguments to command.\n\n");
-       $errors++;
-        if (++$unknowns >= 5) {
-            &transcript("Too many unknown commands, stopping here.\n\n");
-            last;
-        }
-    }
-}
-if ($procline>$#bodylines) {
-    &transcript(">\nEnd of message, stopping processing here.\n\n");
-}
-if (!$ok && !quickabort) {
-    $errors++;
-    &transcript("No commands successfully parsed; sending the help text(s).\n");
-    &sendhelp;
-    &transcript("\n");
-}
-
-&transcript("MC\n") if $dl>1;
-@maintccs= ();
-for $maint (keys %maintccreasons) {
-&transcript("MM|$maint|\n") if $dl>1;
-    next if $maint eq $replyto;
-    $reasonstring= '';
-    $reasonsref= $maintccreasons{$maint};
-&transcript("MY|$maint|\n") if $dl>2;
-    for $p (sort keys %$reasonsref) {
-&transcript("MP|$p|\n") if $dl>2;
-        $reasonstring.= ', ' if length($reasonstring);
-        $reasonstring.= $p.' ' if length($p);
-        $reasonstring.= join(' ',map("#$_",sort keys %{$$reasonsref{$p}}));
-    }
-    if (length($reasonstring) > 40) {
-       (substr $reasonstring, 37) = "...";
-    }
-    $reasonstring = "" if (!defined($reasonstring));
-    push(@maintccs,"$maint ($reasonstring)");
-    push(@maintccaddrs,"$maint");
-}
-
-$maintccs = ""; 
-if (@maintccs) {
-    &transcript("MC|@maintccs|\n") if $dl>2;
-    $maintccs .= "Cc: " . join(",\n    ",@maintccs) . "\n";
-}
-
-my %packagepr;
-for my $maint (keys %maintccreasons) {
-     for my $package (keys %{$maintccreasons{$maint}}) {
-         next unless length $package;
-         $packagepr{$package} = 1;
-     }
-}
-my $packagepr = '';
-$packagepr = "X-${gProject}-PR-Package: " . join(keys %packagepr) . "\n" if keys %packagepr;
-
-# Add Bcc's to subscribed bugs
-push @bcc, map {"bugs=$_\@$gListDomain"} keys %bug_affected;
-
-if (!defined $header{'subject'} || $header{'subject'} eq "") {
-  $header{'subject'} = "your mail";
-}
-
-# Error text here advertises how many errors there were
-my $error_text = $errors > 0 ? " (with $errors errors)":'';
-
-$reply= <<END;
-From: $gMaintainerEmail ($gProject $gBug Tracking System)
-To: $replyto
-${maintccs}Subject: Processed${error_text}: $header{'subject'}
-In-Reply-To: $header{'message-id'}
-References: $header{'message-id'}
-Message-ID: <handler.s.$nn.transcript\@$gEmailDomain>
-Precedence: bulk
-${packagepr}X-$gProject-PR-Message: transcript
-
-${transcript}Please contact me if you need assistance.
-
-$gMaintainer
-(administrator, $gProject $gBugs database)
-$extras
-END
-
-$repliedshow= join(', ',$replyto,@maintccaddrs);
-# -1 is the service.in log
-&filelock("lock/-1");
-open(AP,">>db-h/-1.log") || &quit("open db-h/-1.log: $!");
-print(AP
-      "\2\n$repliedshow\n\5\n$reply\n\3\n".
-      "\6\n".
-      "<strong>Request received</strong> from <code>".
-      html_escape($header{'from'})."</code>\n".
-      "to <code>".html_escape($controlrequestaddr)."</code>\n".
-      "\3\n".
-      "\7\n",escape_log(@log),"\n\3\n") || &quit("writing db-h/-1.log: $!");
-close(AP) || &quit("open db-h/-1.log: $!");
-&unfilelock;
-utime(time,time,"db-h");
-
-&sendmailmessage($reply,exists $header{'x-debbugs-no-ack'}?():$replyto,@maintccaddrs,@bcc);
-
-unlink("incoming/P$nn") || &quit("unlinking incoming/P$nn: $!");
-
-sub sendmailmessage {
-    local ($message,@recips) = @_;
-    $message = "X-Loop: $gMaintainerEmail\n" . $message;
-    send_mail_message(message    => $message,
-                     recipients => \@recips,
-                    );
-    $midix++;
-}
-
-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,
-                           );
-}
-
-=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,
-                         },
-                        );
-}
-
-sub sendhelp {
-        &sendtxthelpraw("bug-log-mailserver.txt","instructions for request\@$gEmailDomain");
-        &sendtxthelpraw("bug-maint-mailcontrol.txt","instructions for control\@$gEmailDomain")
-            if $control;
-}
-
-#sub unimplemented {
-#    &transcript("Sorry, command $_[0] not yet implemented.\n\n");
-#}
-
-sub checkmatch {
-    local ($string,$mvarname,$svarvalue,@newmergelist) = @_;
-    local ($mvarvalue);
-    if (@newmergelist) {
-        eval "\$mvarvalue= \$$mvarname";
-        &transcript("D| checkmatch \`$string' /$mvarname/$mvarvalue/$svarvalue/\n")
-            if $dl;
-        $mismatch .=
-            "Values for \`$string' don't match:\n".
-            " #$newmergelist[0] has \`$mvarvalue';\n".
-            " #$ref has \`$svarvalue'\n"
-            if $mvarvalue ne $svarvalue;
-    } else {
-        &transcript("D| setupmatch \`$string' /$mvarname/$svarvalue/\n")
-            if $dl;
-        eval "\$$mvarname= \$svarvalue";
-    }
-}
-
-sub checkpkglimit {
-    if (keys %limit_pkgs and not defined $limit_pkgs{$data->{package}}) {
-        &transcript("$gBug number $ref belongs to package $data->{package}, skipping.\n\n");
-        $errors++;
-        return 0;
-    }
-    return 1;
-}
-
-sub manipset {
-    my $list = shift;
-    my $elt = shift;
-    my $add = shift;
-
-    my %h = map { $_ => 1 } split ' ', $list;
-    if ($add) {
-        $h{$elt}=1;
-    }
-    else {
-       delete $h{$elt};
-    }
-    return join ' ', sort keys %h;
-}
-
-# High-level bug manipulation calls
-# Do announcements themselves
-#
-# Possible calling sequences:
-#    setbug (returns 0)
-#    
-#    setbug (returns 1)
-#    &transcript(something)
-#    nochangebug
-#
-#    setbug (returns 1)
-#    $action= (something)
-#    do {
-#      (modify s_* variables)
-#    } while (getnextbug);
-
-sub nochangebug {
-    &dlen("nochangebug");
-    $state eq 'single' || $state eq 'multiple' || die "$state ?";
-    &cancelbug;
-    &endmerge if $manybugs;
-    $state= 'idle';
-    &dlex("nochangebug");
-}
-
-sub setbug {
-    &dlen("setbug $ref");
-    if ($ref =~ m/^-\d+/) {
-        if (!defined $clonebugs{$ref}) {
-            &notfoundbug;
-            &dlex("setbug => noclone");
-            return 0;
-        }
-        $ref = $clonebugs{$ref};
-    }
-    $state eq 'idle' || die "$state ?";
-    if (!&getbug) {
-        &notfoundbug;
-        &dlex("setbug => 0s");
-        return 0;
-    }
-
-    if (!&checkpkglimit) {
-        &cancelbug;
-        return 0;
-    }
-
-    @thisbugmergelist= split(/ /,$data->{mergedwith});
-    if (!@thisbugmergelist) {
-        &foundbug;
-        $manybugs= 0;
-        $state= 'single';
-        $sref=$ref;
-        &dlex("setbug => 1s");
-        return 1;
-    }
-    &cancelbug;
-    &getmerge;
-    $manybugs= 1;
-    if (!&getbug) {
-        &notfoundbug;
-        &endmerge;
-        &dlex("setbug => 0mc");
-        return 0;
-    }
-    &foundbug;
-    $state= 'multiple'; $sref=$ref;
-    &dlex("setbug => 1m");
-    return 1;
-}
-
-sub getnextbug {
-    &dlen("getnextbug");
-    $state eq 'single' || $state eq 'multiple' || die "$state ?";
-    &savebug;
-    if (!$manybugs || !@thisbugmergelist) {
-        length($action) || die;
-        &transcript("$action\n$extramessage\n");
-        &endmerge if $manybugs;
-        $state= 'idle';
-        &dlex("getnextbug => 0");
-        return 0;
-    }
-    $ref= shift(@thisbugmergelist);
-    &getbug || die "bug $ref disappeared";
-    &foundbug;
-    &dlex("getnextbug => 1");
-    return 1;
-}
-
-# Low-level bug-manipulation calls
-# Do no announcements
-#
-#    getbug (returns 0)
-#
-#    getbug (returns 1)
-#    cancelbug
-#
-#    getmerge
-#    $action= (something)
-#    getbug (returns 1)
-#    savebug/cancelbug
-#    getbug (returns 1)
-#    savebug/cancelbug
-#    [getbug (returns 0)]
-#    &transcript("$action\n\n")
-#    endmerge
-
-sub notfoundbug { &transcript("$gBug number $ref not found. (Is it archived?)\n\n"); }
-sub foundbug { &transcript("$gBug#$ref: $data->{subject}\n"); }
-
-sub getmerge {
-    &dlen("getmerge");
-    $mergelowstate eq 'idle' || die "$mergelowstate ?";
-    &filelock('lock/merge');
-    $mergelowstate='locked';
-    &dlex("getmerge");
-}
-
-sub endmerge {
-    &dlen("endmerge");
-    $mergelowstate eq 'locked' || die "$mergelowstate ?";
-    &unfilelock;
-    $mergelowstate='idle';
-    &dlex("endmerge");
-}
-
-sub getbug {
-    &dlen("getbug $ref");
-    $lowstate eq 'idle' || die "$state ?";
-    # Only use unmerged bugs here
-    if (($data = &lockreadbug($ref,'db-h'))) {
-        $sref= $ref;
-        $lowstate= "open";
-        &dlex("getbug => 1");
-        $extramessage='';
-        return 1;
-    }
-    $lowstate= 'idle';
-    &dlex("getbug => 0");
-    return 0;
-}
-
-sub cancelbug {
-    &dlen("cancelbug");
-    $lowstate eq 'open' || die "$state ?";
-    &unfilelock;
-    $lowstate= 'idle';
-    &dlex("cancelbug");
-}
-
-sub savebug {
-    &dlen("savebug $ref");
-    $lowstate eq 'open' || die "$lowstate ?";
-    length($action) || die;
-    $ref == $sref || die "read $sref but saving $ref ?";
-    append_action_to_log(bug => $ref,
-                        action => $action,
-                        requester => $header{from},
-                        request_addr => $controlrequestaddr,
-                        message => \@log,
-                        get_lock => 0,
-                       );
-    unlockwritebug($ref, $data);
-    $lowstate= "idle";
-    &dlex("savebug");
-}
-
-sub dlen {
-    return if !$dl;
-    &transcript("C> @_ ($state $lowstate $mergelowstate)\n");
-}
-
-sub dlex {
-    return if !$dl;
-    &transcript("R> @_ ($state $lowstate $mergelowstate)\n");
-}
-
-sub transcript {
-    print $_[0] if $debug;
-    $transcript.= $_[0];
-}
-
-sub urlsanit {
-    my $url = shift;
-    $url =~ s/%/%25/g;
-    $url =~ s/\+/%2b/g;
-    my %saniarray = ('<','lt', '>','gt', '&','amp', '"','quot');
-    $url =~ s/([<>&"])/\&$saniarray{$1};/g;
-    return $url;
-}
-
-sub sendlynxdoc {
-    &sendlynxdocraw;
-    &transcript("\n");
-    $ok++;
-}
-
-sub sendtxthelp {
-    &sendtxthelpraw;
-    &transcript("\n");
-    $ok++;
-}
-
-sub sendtxthelpraw {
-    local ($relpath,$description) = @_;
-    $doc='';
-    open(D,"$gDocDir/$relpath") || &quit("open doc file $relpath: $!");
-    while(<D>) { $doc.=$_; }
-    close(D);
-    &transcript("Sending $description in separate message.\n");
-    &sendmailmessage(<<END.$doc,$replyto);
-From: $gMaintainerEmail ($gProject $gBug Tracking System)
-To: $replyto
-Subject: $gProject $gBug help: $description
-References: $header{'message-id'}
-In-Reply-To: $header{'message-id'}
-Message-ID: <handler.s.$nn.help.$midix\@$gEmailDomain>
-Precedence: bulk
-X-$gProject-PR-Message: doc-text $relpath
-
-END
-    $ok++;
-}
-
-sub sendlynxdocraw {
-    local ($relpath,$description) = @_;
-    $doc='';
-    open(L,"lynx -nolist -dump http://$gCGIDomain/\Q$relpath\E 2>&1 |") || &quit("fork for lynx: $!");
-    while(<L>) { $doc.=$_; }
-    $!=0; close(L);
-    if ($? == 255 && $doc =~ m/^\n*lynx: Can\'t access start file/) {
-        &transcript("Information ($description) is not available -\n".
-                    "perhaps the $gBug does not exist or is not on the WWW yet.\n");
-         $ok++;
-    } elsif ($?) {
-        &transcript("Error getting $description (code $? $!):\n$doc\n");
-    } else {
-        &transcript("Sending $description.\n");
-        &sendmailmessage(<<END.$doc,$replyto);
-From: $gMaintainerEmail ($gProject $gBug Tracking System)
-To: $replyto
-Subject: $gProject $gBugs information: $description
-References: $header{'message-id'}
-In-Reply-To: $header{'message-id'}
-Message-ID: <handler.s.$nn.info.$midix\@$gEmailDomain>
-Precedence: bulk
-X-$gProject-PR-Message: doc-html $relpath
-
-END
-         $ok++;
-    }
-}
-
-sub addccaddress {
-    my ($cca) = @_;
-    $maintccreasons{$cca}{''}{$ref}= 1;
-}
-
-sub addmaintainers {
-    # Data structure is:
-    #   maintainer email address &c -> assoc of packages -> assoc of bug#'s
-    my $data = shift;
-    my ($p, $addmaint);
-    &ensuremaintainersloaded;
-    $anymaintfound=0; $anymaintnotfound=0;
-    for $p (split(m/[ \t?,():]+/, $data->{package})) {
-       $p =~ y/A-Z/a-z/;
-       $p =~ /([a-z0-9.+-]+)/;
-       $p = $1;
-       next unless defined $p;
-       if (defined $gSubscriptionDomain) {
-           if (defined($pkgsrc{$p})) {
-               addbcc("$pkgsrc{$p}\@$gSubscriptionDomain");
-           } else {
-               addbcc("$p\@$gSubscriptionDomain");
-           }
-       }
-        if (defined $data->{severity} and defined $gStrongList and
-                isstrongseverity($data->{severity})) {
-            addbcc("$gStrongList\@$gListDomain");
-        }
-        if (defined($maintainerof{$p})) {
-           $addmaint= $maintainerof{$p};
-           &transcript("MR|$addmaint|$p|$ref|\n") if $dl>2;
-            $maintccreasons{$addmaint}{$p}{$ref}= 1;
-           print "maintainer add >$p|$addmaint<\n" if $debug;
-        } else { 
-           print "maintainer none >$p<\n" if $debug; 
-           &transcript("Warning: Unknown package '$p'\n");
-           &transcript("MR|unknown-package|$p|$ref|\n") if $dl>2;
-            $maintccreasons{$gUnknownMaintainerEmail}{$p}{$ref}= 1;
-       }
-    }
-
-    if (length $data->{owner}) {
-        $addmaint = $data->{owner};
-        &transcript("MO|$addmaint|$data->{package}|$ref|\n") if $dl>2;
-        $maintccreasons{$addmaint}{$data->{package}}{$ref} = 1;
-        print "owner add >$data->{package}|$addmaint<\n" if $debug;
-    }
-}
-
-sub ensuremaintainersloaded {
-    my ($a,$b);
-    return if $maintainersloaded++;
-    open(MAINT,"$gMaintainerFile") || die &quit("maintainers open: $!");
-    while (<MAINT>) {
-       m/^\n$/ && next;
-       m/^\s*$/ && next;
-        m/^(\S+)\s+(\S.*\S)\s*\n$/ || &quit("maintainers bogus \`$_'");
-        $a= $1; $b= $2; $a =~ y/A-Z/a-z/;
-        $maintainerof{$a}= $2;
-    }
-    close(MAINT);
-    open(MAINT,"$gMaintainerFileOverride") || die &quit("maintainers.override open: $!");
-    while (<MAINT>) {
-        m/^\n$/ && next;
-        m/^\s*$/ && next;
-        m/^(\S+)\s+(\S.*\S)\s*\n$/ || &quit("maintainers.override bogus \`$_'");
-        $a= $1; $b= $2; $a =~ y/A-Z/a-z/;
-        $maintainerof{$a}= $2;
-    }
-
-    open(SOURCES, "$gPackageSource") || &quit("pkgsrc open: $!");
-    while (<SOURCES>) {
-       next unless m/^(\S+)\s+\S+\s+(\S.*\S)\s*$/;
-       my ($a, $b) = ($1, $2);
-       $pkgsrc{lc($a)} = $b;
-    }
-    close(SOURCES);
-}
-
-sub sendinfo {
-    local ($wherefrom,$path,$description) = @_;
-    if ($wherefrom eq "ftp.d.o") {
-      $doc = `lynx -nolist -dump http://ftp.debian.org/debian/indices/$path.gz 2>&1 | gunzip -cf` or &quit("fork for lynx/gunzip: $!");
-      $! = 0;
-      if ($? == 255 && $doc =~ m/^\n*lynx: Can\'t access start file/) {
-          &transcript("$description is not available.\n");
-          $ok++; return;
-      } elsif ($?) {
-          &transcript("Error getting $description (code $? $!):\n$doc\n");
-          return;
-      }
-    } elsif ($wherefrom eq "local") {
-      open P, "$path";
-      $doc = do { local $/; <P> };
-      close P;
-    } else {
-      &transcript("internal errror: info files location unknown.\n");
-      $ok++; return;
-    }
-    &transcript("Sending $description.\n");
-    &sendmailmessage(<<END.$doc,$replyto);
-From: $gMaintainerEmail ($gProject $gBug Tracking System)
-To: $replyto
-Subject: $gProject $gBugs information: $description
-References: $header{'message-id'}
-In-Reply-To: $header{'message-id'}
-Message-ID: <handler.s.$nn.info.$midix\@$gEmailDomain>
-Precedence: bulk
-X-$gProject-PR-Message: getinfo
-
-$description follows:
-
-END
-    $ok++;
-    &transcript("\n");
-}
diff --git a/scripts/spamscan b/scripts/spamscan
new file mode 100755 (executable)
index 0000000..9114b83
--- /dev/null
@@ -0,0 +1,325 @@
+#! /usr/bin/perl
+# $Id: spamscan.in,v 1.8 2005/02/01 07:54:01 blarson Exp $
+#
+# Usage: spamscan
+#
+# Performs SpamAssassin checks on a message before allowing it through to
+# the main incoming queue.
+#
+# Uses up: incoming/S<code><bugnum>.nn
+# Temps:   incoming/R.nn
+# Creates: incoming/I.nn
+# Stop:    spamscan-stop
+
+use warnings;
+use strict;
+
+use threads;
+use threads::shared;
+
+use Debbugs::Config qw(:config);
+
+use Debbugs::Common qw(:lock);
+
+use Mail::CrossAssassin;
+use Socket;
+use IO::Handle;
+use IPC::Open2;
+
+
+exit unless $config{spam_scan};
+
+chdir $config{spool_dir} or die "chdir spool: $!\n";
+
+umask 002;
+
+eval {
+    filelock('incoming-spamscan');
+};
+exit if $@;
+
+my %spamseen : shared = ();
+my @ids : shared = ();
+my %fudged : shared = ();
+my $spamscan_stop : shared = 0;
+my $cross_key : shared;
+my @cross_return : shared;
+my $cross_tid : shared;
+my $print_lock : shared;
+my $assassinated_lock : shared;
+my $crossassassinated_lock : shared;
+my $threadsrunning : shared = 0;
+
+# flush output immediately
+$| = 1;
+
+sub lprint ($) {
+    lock $print_lock;
+    print $_[0];
+}
+
+my $user_prefs = "$ENV{HOME}/.spamassassin/user_prefs";
+my $user_prefs_time;
+if (-e $user_prefs) {
+    $user_prefs_time = (stat $user_prefs)[9];
+} else {
+    die "$user_prefs not found";
+}
+
+# This thread handles the updating and querying of the crossassassin db
+sub cross {
+    ca_init('\b\d{3,8}(?:-(?:close|done|forwarded|maintonly|submitter|quiet|subscribe))?\@'.$config{email_domain}, $config{spam_crossassassin_db});
+    my $mytid = threads->self->tid();
+crosscheck:
+    while ($spamscan_stop <= 1) {
+       my ($ck, $ct);
+       {
+           lock $cross_key unless($cross_key);
+           until ($cross_key) {
+               last crosscheck if $spamscan_stop > 1;
+               lprint "{$mytid} cross waiting\n";
+               cond_timedwait $cross_key, (time() + 30);
+           }
+           last crosscheck if ($spamscan_stop > 1);
+           $ck = $cross_key;
+           $ct = $cross_tid;
+           undef $cross_key;
+       }
+       unless ($ck) {
+           lprint "{$mytid} Cross nothing\n";
+           sleep 1;
+           next crosscheck;
+       }
+       lprint "{$mytid} Cross{$ct}: $ck\n";
+       {
+           lock @cross_return;
+           $cross_return[$ct] = ca_set($ck);
+           cond_signal @cross_return;
+       }
+    }
+}
+
+# multiple threads handle spamassassin
+sub sa {
+    {
+       lock $threadsrunning;
+       $threadsrunning++;
+    }
+    my $mytid = threads->self->tid();
+    sleep $mytid + 3;
+    return if $spamscan_stop;
+    my ($sain, $saout);
+
+    my $pid = open2($saout, $sain, "/usr/lib/debbugs/spamscan-sa");
+       lprint "{$mytid} forked $pid\n";
+       my $messages_handled=0;
+pp:    until ($spamscan_stop) {
+           my ($id, $nf);
+           lprint "{$mytid} $messages_handled messages handled\n";
+           $messages_handled++;
+getid:     for (;;) {
+               {
+                   lock @ids;
+                   $nf = @ids;
+                   $id = shift @ids;
+                   last getid if $nf;
+                   cond_timedwait @ids, (time() + 30);
+                   last pp if $spamscan_stop;
+                   $nf = @ids;
+                   $id = shift @ids;
+                   last getid if $nf;
+               }
+               lprint "{$mytid} Waiting for spam to process\n";
+               sleep 1;
+           }
+           print $sain "$id\n$nf\n";
+           lprint "{$mytid} $id is $nf\n";
+           my $keys = <$saout>;
+           unless (defined $keys) {
+               lprint "{$mytid} Could not get keys: $!\n";
+               last pp;
+           }
+           chomp $keys;
+           my $messageid = <$saout>;
+           unless (defined($messageid)) {
+               lprint "{$mytid} Could not read messageid: $!\n";
+               last pp;
+           }
+           chomp $messageid;
+           lprint "{$mytid} $id $keys\n";
+           my $ca_score;
+crosskey:   for (;;) {
+               {
+                   lock $cross_key;
+                   unless ($cross_key) {
+                       $cross_tid = $mytid;
+                       $cross_key = $keys;
+                       cond_signal $cross_key;
+                       last crosskey;
+                   }
+               }
+               lprint "{$mytid} zzz...\n";
+               select undef, undef, undef, 0.1;
+           }
+crossret:   for (;;) {
+               {
+                   lock @cross_return;
+                   if ($cross_return[$mytid]) {
+                       $ca_score = $cross_return[$mytid];
+                       undef $cross_return[$mytid];
+                       last crossret;
+                   }
+               }
+               lprint "{$mytid} z z z...\n";
+               select undef, undef, undef, 0.1;
+           }
+           lprint "{$mytid} $id: ca_score: $ca_score\n";
+           my $seen = $spamseen{$messageid};
+           $seen = '' unless $seen;
+           unless(print $sain "$ca_score\n$seen\n") {
+               lprint "{$mytid} Could not send ca_score: $!\n";
+               last pp;
+           }
+           my $todo = <$saout>;
+           unless (defined($todo)) {
+               lprint "{$mytid} Could not read todo: $!\n";
+               last pp;
+           }
+           chomp $todo;
+           my $nseen;
+           if ($todo == 1) {
+               lock $assassinated_lock;
+               print $sain "$todo\n";
+               $nseen = <$saout>;
+           } elsif ($todo == 2) {
+               lock $crossassassinated_lock;
+               print $sain "$todo\n";
+               $nseen = <$saout>;
+           } else {
+               print $sain "$todo\n";
+               $nseen = <$saout>;
+           }
+           unless(defined($nseen)) {
+               lprint "{$mytid} Could not read seen: $!\n";
+               start_sa() if (scalar(@ids) > ($threadsrunning * $config{spam_spams_per_thread})
+                   && $threadsrunning < $config{spam_max_threads});
+               last pp;
+           }
+           chomp $nseen;
+           $spamseen{$messageid} = $nseen if ($nseen);
+           my $out = <$saout>;
+           unless(defined($out)) {
+               lprint "{$mytid} Could not read out: $!\n";
+               last pp;
+           }
+           chomp $out;
+           $out =~ tr/\r/\n/;
+           lprint $out;
+       }
+       {
+           lock $threadsrunning;
+           $threadsrunning--;
+       }
+        close $sain;
+        close $saout;
+       waitpid($pid,0);
+}
+
+my @sa_threads;
+sub start_sa {
+    my $s = threads->create(\&sa)
+       or die "Could not start sa threads: $!";
+    $s->detach;
+    push @sa_threads, $s;
+}
+
+my $cross_thread = threads->create(\&cross)
+    or die "Could not start cross thread: $!";
+$cross_thread->detach;
+start_sa;
+# start_sa;
+
+my $stopafter = time() + $config{spam_keep_running};
+
+for (;;) {
+    alarm 180;
+    if (-f 'spamscan-stop') {
+       lprint "spamscan-stop file created\n";
+       last;
+    }
+    if ($user_prefs_time != (stat $user_prefs)[9]) {
+       # stop and wait to be re-invoked from cron
+       lprint "File $user_prefs changed\n";
+       last;
+    }
+
+    unless (@ids) {
+       if (time() > $stopafter) {
+           lprint "KeepRunning timer expired\n";
+           last;
+       }
+        my @i;
+       opendir DIR, 'incoming' or die "opendir incoming: $!";
+       while (defined($_ = readdir DIR)) {
+           push @i, $1 if /^S(.*)/;
+       }
+       unless (@i) {
+           lprint "No more spam to process\n";
+           last;
+       }
+       @i = sort {(split(/\./,$a))[1] <=> (split(/\./,$b))[1]} @i;
+       my $m = @i;
+       lprint "Messages to process: $m\n";
+       lock @ids;
+       push @ids, @i;
+       cond_broadcast @ids;
+    }
+    start_sa if (scalar(@ids) > (($threadsrunning - 1) * $config{spam_spams_per_thread})
+                && $threadsrunning < $config{spam_max_threads});
+    sleep 30;
+}
+
+alarm 180;
+
+# wait for the spamassasin threads
+$spamscan_stop = 1;
+{
+    lock @ids;
+    cond_broadcast @ids;
+}
+
+while (my $t = shift @sa_threads) {
+    my $tid = $t->tid;
+    lprint "{} waiting for thread $tid\n";
+    my $max_wait = 60;
+    while ($t->is_running and --$max_wait > 0) {
+        sleep 1;
+    }
+#    $t->join;
+}
+
+# wait for the crossassasin thread
+$spamscan_stop = 2;
+{
+    lprint "{} waiting for cross thread\n";
+    lock $cross_key;
+    $cross_key = 1;
+    cond_signal $cross_key;
+}
+my $max_wait = 60;
+while ($cross_thread->is_running and --$max_wait > 0) {
+    sleep 1;
+}
+#$cross_thread->join;
+
+END{
+   foreach my $thread (threads->list()){
+      $thread->join;
+   }
+}
+
+&unfilelock;
+
+
+
+#exit 0;
diff --git a/scripts/spamscan.in b/scripts/spamscan.in
deleted file mode 100755 (executable)
index 9114b83..0000000
+++ /dev/null
@@ -1,325 +0,0 @@
-#! /usr/bin/perl
-# $Id: spamscan.in,v 1.8 2005/02/01 07:54:01 blarson Exp $
-#
-# Usage: spamscan
-#
-# Performs SpamAssassin checks on a message before allowing it through to
-# the main incoming queue.
-#
-# Uses up: incoming/S<code><bugnum>.nn
-# Temps:   incoming/R.nn
-# Creates: incoming/I.nn
-# Stop:    spamscan-stop
-
-use warnings;
-use strict;
-
-use threads;
-use threads::shared;
-
-use Debbugs::Config qw(:config);
-
-use Debbugs::Common qw(:lock);
-
-use Mail::CrossAssassin;
-use Socket;
-use IO::Handle;
-use IPC::Open2;
-
-
-exit unless $config{spam_scan};
-
-chdir $config{spool_dir} or die "chdir spool: $!\n";
-
-umask 002;
-
-eval {
-    filelock('incoming-spamscan');
-};
-exit if $@;
-
-my %spamseen : shared = ();
-my @ids : shared = ();
-my %fudged : shared = ();
-my $spamscan_stop : shared = 0;
-my $cross_key : shared;
-my @cross_return : shared;
-my $cross_tid : shared;
-my $print_lock : shared;
-my $assassinated_lock : shared;
-my $crossassassinated_lock : shared;
-my $threadsrunning : shared = 0;
-
-# flush output immediately
-$| = 1;
-
-sub lprint ($) {
-    lock $print_lock;
-    print $_[0];
-}
-
-my $user_prefs = "$ENV{HOME}/.spamassassin/user_prefs";
-my $user_prefs_time;
-if (-e $user_prefs) {
-    $user_prefs_time = (stat $user_prefs)[9];
-} else {
-    die "$user_prefs not found";
-}
-
-# This thread handles the updating and querying of the crossassassin db
-sub cross {
-    ca_init('\b\d{3,8}(?:-(?:close|done|forwarded|maintonly|submitter|quiet|subscribe))?\@'.$config{email_domain}, $config{spam_crossassassin_db});
-    my $mytid = threads->self->tid();
-crosscheck:
-    while ($spamscan_stop <= 1) {
-       my ($ck, $ct);
-       {
-           lock $cross_key unless($cross_key);
-           until ($cross_key) {
-               last crosscheck if $spamscan_stop > 1;
-               lprint "{$mytid} cross waiting\n";
-               cond_timedwait $cross_key, (time() + 30);
-           }
-           last crosscheck if ($spamscan_stop > 1);
-           $ck = $cross_key;
-           $ct = $cross_tid;
-           undef $cross_key;
-       }
-       unless ($ck) {
-           lprint "{$mytid} Cross nothing\n";
-           sleep 1;
-           next crosscheck;
-       }
-       lprint "{$mytid} Cross{$ct}: $ck\n";
-       {
-           lock @cross_return;
-           $cross_return[$ct] = ca_set($ck);
-           cond_signal @cross_return;
-       }
-    }
-}
-
-# multiple threads handle spamassassin
-sub sa {
-    {
-       lock $threadsrunning;
-       $threadsrunning++;
-    }
-    my $mytid = threads->self->tid();
-    sleep $mytid + 3;
-    return if $spamscan_stop;
-    my ($sain, $saout);
-
-    my $pid = open2($saout, $sain, "/usr/lib/debbugs/spamscan-sa");
-       lprint "{$mytid} forked $pid\n";
-       my $messages_handled=0;
-pp:    until ($spamscan_stop) {
-           my ($id, $nf);
-           lprint "{$mytid} $messages_handled messages handled\n";
-           $messages_handled++;
-getid:     for (;;) {
-               {
-                   lock @ids;
-                   $nf = @ids;
-                   $id = shift @ids;
-                   last getid if $nf;
-                   cond_timedwait @ids, (time() + 30);
-                   last pp if $spamscan_stop;
-                   $nf = @ids;
-                   $id = shift @ids;
-                   last getid if $nf;
-               }
-               lprint "{$mytid} Waiting for spam to process\n";
-               sleep 1;
-           }
-           print $sain "$id\n$nf\n";
-           lprint "{$mytid} $id is $nf\n";
-           my $keys = <$saout>;
-           unless (defined $keys) {
-               lprint "{$mytid} Could not get keys: $!\n";
-               last pp;
-           }
-           chomp $keys;
-           my $messageid = <$saout>;
-           unless (defined($messageid)) {
-               lprint "{$mytid} Could not read messageid: $!\n";
-               last pp;
-           }
-           chomp $messageid;
-           lprint "{$mytid} $id $keys\n";
-           my $ca_score;
-crosskey:   for (;;) {
-               {
-                   lock $cross_key;
-                   unless ($cross_key) {
-                       $cross_tid = $mytid;
-                       $cross_key = $keys;
-                       cond_signal $cross_key;
-                       last crosskey;
-                   }
-               }
-               lprint "{$mytid} zzz...\n";
-               select undef, undef, undef, 0.1;
-           }
-crossret:   for (;;) {
-               {
-                   lock @cross_return;
-                   if ($cross_return[$mytid]) {
-                       $ca_score = $cross_return[$mytid];
-                       undef $cross_return[$mytid];
-                       last crossret;
-                   }
-               }
-               lprint "{$mytid} z z z...\n";
-               select undef, undef, undef, 0.1;
-           }
-           lprint "{$mytid} $id: ca_score: $ca_score\n";
-           my $seen = $spamseen{$messageid};
-           $seen = '' unless $seen;
-           unless(print $sain "$ca_score\n$seen\n") {
-               lprint "{$mytid} Could not send ca_score: $!\n";
-               last pp;
-           }
-           my $todo = <$saout>;
-           unless (defined($todo)) {
-               lprint "{$mytid} Could not read todo: $!\n";
-               last pp;
-           }
-           chomp $todo;
-           my $nseen;
-           if ($todo == 1) {
-               lock $assassinated_lock;
-               print $sain "$todo\n";
-               $nseen = <$saout>;
-           } elsif ($todo == 2) {
-               lock $crossassassinated_lock;
-               print $sain "$todo\n";
-               $nseen = <$saout>;
-           } else {
-               print $sain "$todo\n";
-               $nseen = <$saout>;
-           }
-           unless(defined($nseen)) {
-               lprint "{$mytid} Could not read seen: $!\n";
-               start_sa() if (scalar(@ids) > ($threadsrunning * $config{spam_spams_per_thread})
-                   && $threadsrunning < $config{spam_max_threads});
-               last pp;
-           }
-           chomp $nseen;
-           $spamseen{$messageid} = $nseen if ($nseen);
-           my $out = <$saout>;
-           unless(defined($out)) {
-               lprint "{$mytid} Could not read out: $!\n";
-               last pp;
-           }
-           chomp $out;
-           $out =~ tr/\r/\n/;
-           lprint $out;
-       }
-       {
-           lock $threadsrunning;
-           $threadsrunning--;
-       }
-        close $sain;
-        close $saout;
-       waitpid($pid,0);
-}
-
-my @sa_threads;
-sub start_sa {
-    my $s = threads->create(\&sa)
-       or die "Could not start sa threads: $!";
-    $s->detach;
-    push @sa_threads, $s;
-}
-
-my $cross_thread = threads->create(\&cross)
-    or die "Could not start cross thread: $!";
-$cross_thread->detach;
-start_sa;
-# start_sa;
-
-my $stopafter = time() + $config{spam_keep_running};
-
-for (;;) {
-    alarm 180;
-    if (-f 'spamscan-stop') {
-       lprint "spamscan-stop file created\n";
-       last;
-    }
-    if ($user_prefs_time != (stat $user_prefs)[9]) {
-       # stop and wait to be re-invoked from cron
-       lprint "File $user_prefs changed\n";
-       last;
-    }
-
-    unless (@ids) {
-       if (time() > $stopafter) {
-           lprint "KeepRunning timer expired\n";
-           last;
-       }
-        my @i;
-       opendir DIR, 'incoming' or die "opendir incoming: $!";
-       while (defined($_ = readdir DIR)) {
-           push @i, $1 if /^S(.*)/;
-       }
-       unless (@i) {
-           lprint "No more spam to process\n";
-           last;
-       }
-       @i = sort {(split(/\./,$a))[1] <=> (split(/\./,$b))[1]} @i;
-       my $m = @i;
-       lprint "Messages to process: $m\n";
-       lock @ids;
-       push @ids, @i;
-       cond_broadcast @ids;
-    }
-    start_sa if (scalar(@ids) > (($threadsrunning - 1) * $config{spam_spams_per_thread})
-                && $threadsrunning < $config{spam_max_threads});
-    sleep 30;
-}
-
-alarm 180;
-
-# wait for the spamassasin threads
-$spamscan_stop = 1;
-{
-    lock @ids;
-    cond_broadcast @ids;
-}
-
-while (my $t = shift @sa_threads) {
-    my $tid = $t->tid;
-    lprint "{} waiting for thread $tid\n";
-    my $max_wait = 60;
-    while ($t->is_running and --$max_wait > 0) {
-        sleep 1;
-    }
-#    $t->join;
-}
-
-# wait for the crossassasin thread
-$spamscan_stop = 2;
-{
-    lprint "{} waiting for cross thread\n";
-    lock $cross_key;
-    $cross_key = 1;
-    cond_signal $cross_key;
-}
-my $max_wait = 60;
-while ($cross_thread->is_running and --$max_wait > 0) {
-    sleep 1;
-}
-#$cross_thread->join;
-
-END{
-   foreach my $thread (threads->list()){
-      $thread->join;
-   }
-}
-
-&unfilelock;
-
-
-
-#exit 0;
diff --git a/scripts/summary b/scripts/summary
new file mode 100755 (executable)
index 0000000..a1be697
--- /dev/null
@@ -0,0 +1,99 @@
+#!/usr/bin/perl
+# $Id: summary.in,v 1.11 2004/04/17 17:31:04 cjwatson Exp $
+
+$config_path = '/etc/debbugs';
+$lib_path = '/usr/lib/debbugs';
+
+require("$config_path/config");
+require("$lib_path/errorlib");
+$ENV{'PATH'} = $lib_path.':'.$ENV{'PATH'};
+
+chdir("$gSpoolDir") || die "chdir spool: $!\n";
+
+#open(DEBUG,">&4");
+
+$mode= shift(@ARGV);
+
+open(M,"$gMaintainerFile") || &quit("open $gMaintainerFile: $!");
+while (<M>) {
+    m/^(\S+)\s+(\S.*\S)\s*$/ || warn "$_ ?";
+    ($a,$b)=($1,$2);
+    $a =~ y/A-Z/a-z/;
+    $maintainer{$a}= $b;
+}
+close(M);
+open(M,"$gMaintainerFileOverride") || &quit("open $gMaintainerFileOverride: $!");
+while (<M>) {
+    m/^(\S+)\s+(\S.*\S)\s*$/ || warn "$_ ?";
+    ($a,$b)=($1,$2);
+    $a =~ y/A-Z/a-z/;
+    $maintainer{$a}= $b;
+}
+close(M);
+               
+
+defined($startdate= time) || &quit("failed to get time: $!");
+
+opendir(DIR,"db-h") || &quit("opendir db-h: $!\n");
+@dirs = grep(s,^,db-h/,, grep(m/^\d+$/,readdir(DIR)));
+closedir(DIR);
+foreach my $dir (@dirs) {
+        opendir(DIR,$dir);
+        push @list, grep(s/\.status$//,grep(m/^\d+\.status$/,readdir(DIR)));
+        closedir(DIR);
+}
+@list = sort { $a <=> $b } @list;
+
+$head= $mode eq 'bymaint'
+    ? ' Package     Ref    Subject'
+    : ' Ref   Package    Keywords/Subject               Package maintainer';
+$amonths=-1;
+
+while (length($f=shift(@list))) {
+    if (!($data = lockreadbug($f))) { next; }
+    $_= $data->{package}; y/A-Z/a-z/; $_= $` if m/[^-+._a-z0-9]/;
+    $data->{maintainer}=
+        defined($maintainer{$_}) ? $maintainer{$_} :
+        length($_) ? "(unknown -- \`$_')" :
+        "(unknown)";
+    if ($mode eq 'undone' || $mode eq 'veryold') {
+        &unfilelock;
+        next if length($data->{done}) || length($data->{forwarded});
+        $cmonths= int(($startdate - $data->{date})/2592000); # 3600*24*30 (30 days)
+        next if $mode eq 'veryold' && $cmonths < 2;
+        if ($cmonths != $amonths) {
+            $msg= $cmonths == 0 ? "Submitted in the last month" :
+                  $cmonths == 1 ? "Over one month old" :
+                  $cmonths == 2 ? "Over two months old - attention is required" :
+                                  "OVER $cmonths MONTHS OLD - ATTENTION IS REQUIRED";
+            print "\n$msg:\n$head\n";
+            $amonths= $cmonths;
+        }
+        printf("%6d %-10.10s %-30.30s %-.31s\n", $f, $data->{package},
+               (length($data->{keywords}) ? $data->{keywords}.'/' : '').$data->{subject},
+               $data->{maintainer}) || &quit("output undone: $!");
+    } elsif ($mode eq 'bymaint') {
+        &unfilelock;
+        next if length($data->{done}) || length($data->{forwarded});
+        $string{$f}=
+            sprintf(" %-10.10s %6d  %-.59s\n", $data->{package}, $f, $data->{subject});
+        $data->{maintainer}= "(unknown)" if $data->{maintainer} =~ m/^\(unknown \-\-/;
+        $maintainercnt{$data->{maintainer}}++;
+        $maintainerlist{$data->{maintainer}}.= " $f";
+    } else {
+        &quit("badmode $mode");
+    }
+}
+
+if ($mode eq 'bymaint') {
+    print("$head\n") || &quit("output head: $!");
+    for $m (sort { $maintainercnt{$a} <=> $maintainercnt{$b} } keys %maintainercnt) {
+        printf("\n%s (%d $gBugs):\n",$m,$maintainercnt{$m})
+            || &quit("output mainthead: $!");
+        for $i (sort { $string{$a} cmp $string{$b} } split(/ /,$maintainerlist{$m})) {
+            printf($string{$i}) || &quit("output 1bymaint: $!");
+        }
+    }
+}
+
+close(STDOUT) || &quit("close stdout: $!");
diff --git a/scripts/summary.in b/scripts/summary.in
deleted file mode 100755 (executable)
index a1be697..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-#!/usr/bin/perl
-# $Id: summary.in,v 1.11 2004/04/17 17:31:04 cjwatson Exp $
-
-$config_path = '/etc/debbugs';
-$lib_path = '/usr/lib/debbugs';
-
-require("$config_path/config");
-require("$lib_path/errorlib");
-$ENV{'PATH'} = $lib_path.':'.$ENV{'PATH'};
-
-chdir("$gSpoolDir") || die "chdir spool: $!\n";
-
-#open(DEBUG,">&4");
-
-$mode= shift(@ARGV);
-
-open(M,"$gMaintainerFile") || &quit("open $gMaintainerFile: $!");
-while (<M>) {
-    m/^(\S+)\s+(\S.*\S)\s*$/ || warn "$_ ?";
-    ($a,$b)=($1,$2);
-    $a =~ y/A-Z/a-z/;
-    $maintainer{$a}= $b;
-}
-close(M);
-open(M,"$gMaintainerFileOverride") || &quit("open $gMaintainerFileOverride: $!");
-while (<M>) {
-    m/^(\S+)\s+(\S.*\S)\s*$/ || warn "$_ ?";
-    ($a,$b)=($1,$2);
-    $a =~ y/A-Z/a-z/;
-    $maintainer{$a}= $b;
-}
-close(M);
-               
-
-defined($startdate= time) || &quit("failed to get time: $!");
-
-opendir(DIR,"db-h") || &quit("opendir db-h: $!\n");
-@dirs = grep(s,^,db-h/,, grep(m/^\d+$/,readdir(DIR)));
-closedir(DIR);
-foreach my $dir (@dirs) {
-        opendir(DIR,$dir);
-        push @list, grep(s/\.status$//,grep(m/^\d+\.status$/,readdir(DIR)));
-        closedir(DIR);
-}
-@list = sort { $a <=> $b } @list;
-
-$head= $mode eq 'bymaint'
-    ? ' Package     Ref    Subject'
-    : ' Ref   Package    Keywords/Subject               Package maintainer';
-$amonths=-1;
-
-while (length($f=shift(@list))) {
-    if (!($data = lockreadbug($f))) { next; }
-    $_= $data->{package}; y/A-Z/a-z/; $_= $` if m/[^-+._a-z0-9]/;
-    $data->{maintainer}=
-        defined($maintainer{$_}) ? $maintainer{$_} :
-        length($_) ? "(unknown -- \`$_')" :
-        "(unknown)";
-    if ($mode eq 'undone' || $mode eq 'veryold') {
-        &unfilelock;
-        next if length($data->{done}) || length($data->{forwarded});
-        $cmonths= int(($startdate - $data->{date})/2592000); # 3600*24*30 (30 days)
-        next if $mode eq 'veryold' && $cmonths < 2;
-        if ($cmonths != $amonths) {
-            $msg= $cmonths == 0 ? "Submitted in the last month" :
-                  $cmonths == 1 ? "Over one month old" :
-                  $cmonths == 2 ? "Over two months old - attention is required" :
-                                  "OVER $cmonths MONTHS OLD - ATTENTION IS REQUIRED";
-            print "\n$msg:\n$head\n";
-            $amonths= $cmonths;
-        }
-        printf("%6d %-10.10s %-30.30s %-.31s\n", $f, $data->{package},
-               (length($data->{keywords}) ? $data->{keywords}.'/' : '').$data->{subject},
-               $data->{maintainer}) || &quit("output undone: $!");
-    } elsif ($mode eq 'bymaint') {
-        &unfilelock;
-        next if length($data->{done}) || length($data->{forwarded});
-        $string{$f}=
-            sprintf(" %-10.10s %6d  %-.59s\n", $data->{package}, $f, $data->{subject});
-        $data->{maintainer}= "(unknown)" if $data->{maintainer} =~ m/^\(unknown \-\-/;
-        $maintainercnt{$data->{maintainer}}++;
-        $maintainerlist{$data->{maintainer}}.= " $f";
-    } else {
-        &quit("badmode $mode");
-    }
-}
-
-if ($mode eq 'bymaint') {
-    print("$head\n") || &quit("output head: $!");
-    for $m (sort { $maintainercnt{$a} <=> $maintainercnt{$b} } keys %maintainercnt) {
-        printf("\n%s (%d $gBugs):\n",$m,$maintainercnt{$m})
-            || &quit("output mainthead: $!");
-        for $i (sort { $string{$a} cmp $string{$b} } split(/ /,$maintainerlist{$m})) {
-            printf($string{$i}) || &quit("output 1bymaint: $!");
-        }
-    }
-}
-
-close(STDOUT) || &quit("close stdout: $!");
diff --git a/scripts/text b/scripts/text
new file mode 100644 (file)
index 0000000..415aba0
--- /dev/null
@@ -0,0 +1,342 @@
+# -*- mode: cperl -*-
+
+use Debbugs::Config qw(:globals);
+
+############################################################################
+#  Here is a blurb to point people to ftp archive of directions.  It is
+#  used by the receive script when bouncing a badly formatted email
+#
+# $gTextInstructions = "$gBadEmailPrefix
+# $gBadEmailPrefix Instructions are available from ftp.debian.org in /debian
+# $gBadEmailPrefix and at all Debian mirror sites, in the files:
+# $gBadEmailPrefix  doc/bug-reporting.txt
+# $gBadEmailPrefix  doc/bug-log-access.txt
+# $gBadEmailPrefix  doc/bug-maint-info.txt
+# $gBadEmailPrefix";
+############################################################################
+$gBadEmailPrefix = '' unless defined $gBadEmailPrefix;
+$gTextInstructions = "$gBadEmailPrefix";
+
+
+############################################################################
+# Here is a blurb for any mirrors of the web site.  Here's a sample:
+#
+#$gHTMLCopies = "<p>Copies of the logs are available on the World Wide Web at<BR>
+# <A HREF=\"http://mirror1.domain\"><CODE>http://mirror1.domain</CODE></A><BR>
+# <A HREF=\"http://mirror2.domain\"><CODE>http://mirror2.domain</CODE></A>";
+############################################################################
+$gHTMLCopies = "";
+
+
+############################################################################
+# notice other links you want to note, like your list archives or project
+# home page.
+#
+#$gHTMLOtherPages = "Other Links of note:<BR>
+#      <A HREF=\"http://www.debian.org/\">The Debian Project</A><BR>
+#      <A HREF=\"http://another.domain\">Description of URL</A>";
+############################################################################
+$gHTMLOtherPages = "";
+
+
+############################################################################
+# list of other links you want to note, like your list archives or project
+# home page.  Some pages already have links in a list, this adds them to 
+# the end of the list.
+#
+#$gHTMLOtherPageList = "<LI><A HREF=\"http://www.debian.org/\">
+#      The Debian Project</A>
+#      <LI><A HREF=\"http://another.domain\">Description of URL</A>";
+############################################################################
+$gHTMLOtherPageList = "";
+
+
+############################################################################
+# gives explanation of bad maintainer situation and instructions on how to
+# correct.
+############################################################################
+$gBadMaintHtml = "";
+
+
+############################################################################
+# give directions here for how to find the proper title for Package:
+# pseudo header line.
+############################################################################
+$gHTMLFindPackage = "";
+
+
+############################################################################
+# If you have pseudo packages, place a blurb here.  For example:
+# $gHTMLPseudoDesc = "<p>There are some pseudo-packages available for putting in
+# the <CODE>Package</CODE> line when reporting a $gBug in something other than an
+# actual $gProject software package.  There is 
+# <A HREF="db/ix/pseudopackages.html"> a list of these</A> on the $gBugs WWW 
+# pages.";
+############################################################################
+$gHTMLPseudoDesc = "";
+
+
+############################################################################
+# List any extra information you would like included in bug reports. For
+# example:
+# $gXtraBugInfo = "<li>What kernel version you're using (type
+# <code>uname -a</code>), your shared C library (type <code>ls -l
+# /lib/libc.so.6</code> or <code>dpkg -s libc6 | grep ^Version</code>), and
+# any other details about your Debian system, if it seems appropriate.
+# For example, if you had a problem with a Perl script, you would want to
+# provide the version of the `perl' binary (type <code>perl -v</code> or
+# <code>dpkg -s perl-5.005 | grep ^Version:</code>).";
+############################################################################
+$gXtraBugInfo = "";
+
+
+############################################################################
+# List any extra information you would like about reporting bugs
+############################################################################
+$gXtraReportingInfo = "";
+
+
+############################################################################
+# Process used by system to create Maintainers index file
+############################################################################
+$gCreateMaintainers = "";
+
+
+###########################################################################
+# You shouldn't have to modify anything below here unless it's for personal
+# preference.  Be very careful and don't touch unless you *know* what
+# you're doing.  Much of the stuff has hardcoded duplicates elsewhere.
+
+
+############################################################################
+# Description of the severities
+############################################################################
+$gHTMLSeverityDesc = "<DT><CODE>critical</CODE>
+       <DD>makes unrelated software on the system (or the whole system) break,
+       or causes serious data loss, or introduces a security hole on systems 
+       where you install the package.
+
+       <DT><CODE>grave</CODE>
+       <DD>makes the package in question unusable or mostly so, or causes data
+       loss, or introduces a security hole allowing access to the accounts of
+       users who use the package.
+
+       <DT><CODE>normal</CODE>
+       <DD>the default value, for normal $gBugs.
+
+       <DT><CODE>wishlist</CODE>
+       <DD>for any feature request, and also for any $gBugs that are very 
+       difficult to fix due to major design considerations.";
+
+############################################################################
+# Description of the tags
+############################################################################
+$gHTMLTagDesc = "
+<dt><code>patch</code>
+  <dd>A patch or some other easy procedure for fixing the $gBug is included in
+  the $gBug logs. If there\'s a patch, but it doesn\'t resolve the $gBug
+  adequately or causes some other problems, this tag should not be used.
+
+<dt><code>wontfix</code>
+  <dd>This $gBug won\'t be fixed. Possibly because this is a choice between two
+  arbitrary ways of doing things and the maintainer and submitter prefer
+  different ways of doing things, possibly because changing the behaviour
+  will cause other, worse, problems for others, or possibly for other
+  reasons.
+
+<dt><code>moreinfo</code>
+  <dd>This $gBug can\'t be addressed until more information is provided by the
+  submitter. The $gBug will be closed if the submitter doesn\'t provide more
+  information in a reasonable (few months) timeframe. This is for $gBugs like
+  \"It doesn\'t work\". What doesn\'t work?
+
+<dt><code>unreproducible</code>
+  <dd>This $gBug can\'t be reproduced on the maintainer\'s system.  Assistance
+  from third parties is needed in diagnosing the cause of the problem.
+
+<dt><code>fixed</code>
+  <dd>This $gBug is fixed or worked around, but there\'s still an issue that
+  needs to be resolved.
+
+<dt><code>stable</code>
+  <dd>This $gBug affects the stable distribution in particular.  This is only
+  intended to be used for ease in identifying release critical $gBugs that
+  affect the stable distribution.  It\'ll be replaced eventually with
+  something a little more flexible, probably.
+";
+
+############################################################################
+# shows up at the start of (most) html pages.
+############################################################################
+$gHTMLStart = "<BODY>";
+
+############################################################################
+# shows up at the end of (most) html pages.
+############################################################################
+$gHTMLTail = "
+ <ADDRESS>$gMaintainer &lt;<A HREF=\"mailto:$gMaintainerEmail\">$gMaintainerEmail</A>&gt;.
+ Last modified:
+ <!--timestamp-->
+ SUBSTITUTE_DTIME
+ <!--timestamp-->
+         
+ <P>
+ <A HREF=\"http://$gWebDomain/\">Debian $gBug tracking system</A><BR>
+ Copyright (C) 1999 Darren O. Benham,
+ 1997,2003 nCipher Corporation Ltd,
+ 1994-97 Ian Jackson.
+ </ADDRESS>
+";
+
+############################################################################
+# Message on when reports are purged.
+############################################################################
+$gHTMLExpireNote = "(Closed $gBugs are archived $gRemoveAge days after the last related message is received.)";
+
+############################################################################
+# Makeup of the stamp page
+############################################################################
+$gHTMLStamp = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">
+       <HTML><HEAD><TITLE>$gProject $gBugs - timestamp page</TITLE>
+       <LINK REV=\"made\" HREF=\"mailto:$gMaintainerEmail\">
+       </HEAD>$gHTMLStart<H1>Is this $gBug log or mirror up to date?</H1>
+
+       Unlike all the other $gBug pages, this small timestamp page is updated every
+       time the update check job runs.  If the timestamp here is recent it\'s
+       likely that the mirror in which you\'re reading it is up to date.
+       <P>
+       The last
+       <!--updateupdate-->update<!--/updateupdate-->
+       was at 
+       <STRONG><!--updatetime-->SUBSTITUTE_DTIME<!--/updatetime--></STRONG>;
+       The logs are usually checked every hour and updated if necessary.
+       <P>
+       For the $gBug index or for other information about $gProject and the $gBug 
+       system, see the <A HREF=\"../../\">$gBug system main contents page</A>.
+
+       <HR>
+       <ADDRESS>
+       <A HREF=\"mailto:$gMaintainerEmail\">$gMaintainerEmail</A>,
+       through the <A HREF=\"../../\">$gProject $gBug database</a>
+       </ADDRESS>
+       <!--version 1.0-4.3-->";
+
+############################################################################
+# Makeup of the indices pages
+############################################################################
+$gFullIndex = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">
+       <HTML><HEAD><TITLE>$gProject $gBugs - full index</TITLE>
+       <LINK REV=\"make\" HREF=\"mailto:$gMaintainerEmail\">
+       </HEAD>$gHTMLStart<H1>$gProject $gBug report logs - index</H1>
+
+       This index gives access to $gBugs sent to <CODE>submit\@$gEmailDomain</CODE>
+       but not yet marked as done, and to $gBugs marked as done but not yet purged
+       from the database (this happens $gRemoveAge days after the last message relating to
+       the report).
+       <P>
+       For other kinds of indices or for other information about $gProject and
+       the $gBug system, see <A HREF=\"../../\">$gBug system top-level contents WWW
+       page</A>.
+
+
+       ";
+
+$gJunkIndex = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">
+       <HTML><HEAD><TITLE>$gProject $gBug reports - Junk</TITLE>
+       <LINK REV=\"made\" HREF=\"$gMaintainerEmail\">
+       </HEAD>$gHTMLStart<H1>$gProject $gBug reports - Junk</H1>
+
+       This is the index page for logs of messages not associated with a specific
+       $gBug report.
+       <P>
+       For other kinds of indices or for other information about $gProject and
+       the $gBug system, see <A HREF=\"../../\">$gBug system top-level contents WWW
+       page</A>.
+
+
+       ";
+
+$gMaintIndex = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">
+       <HTML><HEAD><TITLE>$gProject $gBug reports by maintainer</TITLE>
+       <LINK REF=\"made\" HREF=\"mailto:$gMaintainerEmail\">
+       </HEAD>$gHTMLStart<H1>$gProject $gBug reports by maintainer</H1>
+
+       This page lists the package maintainers against whose packages there are
+       outstanding, forwarded or recently-closed $gBug reports.  A maintainer who
+       has several versions of their email address in the <CODE>Maintainer</CODE>
+       package control file field may appear several times.<P>
+       If the maintainers information here is not accurate, please see 
+       <A HREF=\"../../Developer.html#maintincorrect\">the developers\'
+       instructions</A> to find how this can happen and what to do about it. <P>
+       For other kinds of indices or for other information about $gProject and
+       the $gBug system, see <A HREF=\"../../\">$gBug system top-level contents WWW
+       page</A>.
+
+
+       ";
+
+$gPackageIndex = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">
+       <HTML><HEAD><TITLE>$gProject $gBug reports by package</TITLE>
+       <LINK REF=\"made\" HREF=\"mailto:$gMaintainerEmail\">
+       </HEAD>$gHTMLStart<H1>$gProject $gBug reports by package</H1>
+
+       This page lists the package against which there are outstanding, forwarded or
+       recently-closed $gBug reports.  A multi-binary package may appear several
+       times, once for each binary package name and once for the source package
+       name (if it is different).<P>
+       For other kinds of indices or for other information about $gProject and
+       the $gBug system, see <A HREF=\"../../\">$gBug system top-level contents WWW
+       page</A>.
+
+
+       ";
+
+$gSummaryIndex = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">
+       <HTML><HEAD><TITLE>$gProject $gBug report logs - summary index</TITLE>
+       <LINK REF=\"made\" HREF=\"mailto:$gMaintainerEmail\">
+       </HEAD>$gHTMLStart<H1>$gProject $gBug report logs - summary index</H1>
+
+       This summary index briefly lists $gBugs sent to <CODE>submit\@$gEmailDomain
+       </CODE> but not yet marked as done, or as forwarded to an upstream author.  
+       Here they are sorted by reference number (and therefore by submission date, 
+       too).<P>
+       For other kinds of indices or for other information about $gProject and
+       the $gBug system, see <A HREF=\"../../\">$gBug system top-level contents WWW
+       page</A>.
+
+       <P>The * column lists the first letter of the severity of the $gBug.
+
+
+       ";
+
+$gPackageLog = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">
+       <HTML><HEAD><TITLE>$gProject $gBug report logs - index by package</TITLE>
+       <LINK REF=\"made\" HREF=\"mailto:$gMaintainerEmail\">
+       </HEAD>$gHTMLStart<H1>$gProject $gBug report logs - index by package</H1>
+
+       This summary index briefly lists $gBugs sent to <CODE>submit\@$gEmailDomain
+       </CODE> but not yet marked as done, or as forwarded to an upstream author.  
+       Here they are sorted by package name.<P>
+       For other kinds of indices or for other information about $gProject and
+       the $gBug system, see <A HREF=\"../../\">$gBug system top-level contents WWW
+       page</A>.
+
+
+       ";
+
+$gPseudoIndex = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">
+       <HTML><HEAD><TITLE>$gProject $gBug report pseudo-packages</TITLE>
+       <LINK REF=\"made\" HREF=\"mailto:$gMaintainerEmail\">
+       </HEAD>$gHTMLStart<H1>$gProject $gBug report pseudo-packages</H1>
+
+       This page lists the pseudo-packages available for use in the
+       <CODE>Package:</CODE> line in $gBug reports.<P>
+
+       See the <A HREF=\"../../Reporting.html\">instructions for reporting a
+       $gBug</A> for details of how to specify a <CODE>Package:</CODE> line.<P>
+       For other kinds of indices or for other information about $gProject and
+       the $gBug system, see <A HREF=\"../../\">$gBug system top-level contents WWW
+       page</A>.
+
+
+       ";
diff --git a/scripts/text.in b/scripts/text.in
deleted file mode 100644 (file)
index 415aba0..0000000
+++ /dev/null
@@ -1,342 +0,0 @@
-# -*- mode: cperl -*-
-
-use Debbugs::Config qw(:globals);
-
-############################################################################
-#  Here is a blurb to point people to ftp archive of directions.  It is
-#  used by the receive script when bouncing a badly formatted email
-#
-# $gTextInstructions = "$gBadEmailPrefix
-# $gBadEmailPrefix Instructions are available from ftp.debian.org in /debian
-# $gBadEmailPrefix and at all Debian mirror sites, in the files:
-# $gBadEmailPrefix  doc/bug-reporting.txt
-# $gBadEmailPrefix  doc/bug-log-access.txt
-# $gBadEmailPrefix  doc/bug-maint-info.txt
-# $gBadEmailPrefix";
-############################################################################
-$gBadEmailPrefix = '' unless defined $gBadEmailPrefix;
-$gTextInstructions = "$gBadEmailPrefix";
-
-
-############################################################################
-# Here is a blurb for any mirrors of the web site.  Here's a sample:
-#
-#$gHTMLCopies = "<p>Copies of the logs are available on the World Wide Web at<BR>
-# <A HREF=\"http://mirror1.domain\"><CODE>http://mirror1.domain</CODE></A><BR>
-# <A HREF=\"http://mirror2.domain\"><CODE>http://mirror2.domain</CODE></A>";
-############################################################################
-$gHTMLCopies = "";
-
-
-############################################################################
-# notice other links you want to note, like your list archives or project
-# home page.
-#
-#$gHTMLOtherPages = "Other Links of note:<BR>
-#      <A HREF=\"http://www.debian.org/\">The Debian Project</A><BR>
-#      <A HREF=\"http://another.domain\">Description of URL</A>";
-############################################################################
-$gHTMLOtherPages = "";
-
-
-############################################################################
-# list of other links you want to note, like your list archives or project
-# home page.  Some pages already have links in a list, this adds them to 
-# the end of the list.
-#
-#$gHTMLOtherPageList = "<LI><A HREF=\"http://www.debian.org/\">
-#      The Debian Project</A>
-#      <LI><A HREF=\"http://another.domain\">Description of URL</A>";
-############################################################################
-$gHTMLOtherPageList = "";
-
-
-############################################################################
-# gives explanation of bad maintainer situation and instructions on how to
-# correct.
-############################################################################
-$gBadMaintHtml = "";
-
-
-############################################################################
-# give directions here for how to find the proper title for Package:
-# pseudo header line.
-############################################################################
-$gHTMLFindPackage = "";
-
-
-############################################################################
-# If you have pseudo packages, place a blurb here.  For example:
-# $gHTMLPseudoDesc = "<p>There are some pseudo-packages available for putting in
-# the <CODE>Package</CODE> line when reporting a $gBug in something other than an
-# actual $gProject software package.  There is 
-# <A HREF="db/ix/pseudopackages.html"> a list of these</A> on the $gBugs WWW 
-# pages.";
-############################################################################
-$gHTMLPseudoDesc = "";
-
-
-############################################################################
-# List any extra information you would like included in bug reports. For
-# example:
-# $gXtraBugInfo = "<li>What kernel version you're using (type
-# <code>uname -a</code>), your shared C library (type <code>ls -l
-# /lib/libc.so.6</code> or <code>dpkg -s libc6 | grep ^Version</code>), and
-# any other details about your Debian system, if it seems appropriate.
-# For example, if you had a problem with a Perl script, you would want to
-# provide the version of the `perl' binary (type <code>perl -v</code> or
-# <code>dpkg -s perl-5.005 | grep ^Version:</code>).";
-############################################################################
-$gXtraBugInfo = "";
-
-
-############################################################################
-# List any extra information you would like about reporting bugs
-############################################################################
-$gXtraReportingInfo = "";
-
-
-############################################################################
-# Process used by system to create Maintainers index file
-############################################################################
-$gCreateMaintainers = "";
-
-
-###########################################################################
-# You shouldn't have to modify anything below here unless it's for personal
-# preference.  Be very careful and don't touch unless you *know* what
-# you're doing.  Much of the stuff has hardcoded duplicates elsewhere.
-
-
-############################################################################
-# Description of the severities
-############################################################################
-$gHTMLSeverityDesc = "<DT><CODE>critical</CODE>
-       <DD>makes unrelated software on the system (or the whole system) break,
-       or causes serious data loss, or introduces a security hole on systems 
-       where you install the package.
-
-       <DT><CODE>grave</CODE>
-       <DD>makes the package in question unusable or mostly so, or causes data
-       loss, or introduces a security hole allowing access to the accounts of
-       users who use the package.
-
-       <DT><CODE>normal</CODE>
-       <DD>the default value, for normal $gBugs.
-
-       <DT><CODE>wishlist</CODE>
-       <DD>for any feature request, and also for any $gBugs that are very 
-       difficult to fix due to major design considerations.";
-
-############################################################################
-# Description of the tags
-############################################################################
-$gHTMLTagDesc = "
-<dt><code>patch</code>
-  <dd>A patch or some other easy procedure for fixing the $gBug is included in
-  the $gBug logs. If there\'s a patch, but it doesn\'t resolve the $gBug
-  adequately or causes some other problems, this tag should not be used.
-
-<dt><code>wontfix</code>
-  <dd>This $gBug won\'t be fixed. Possibly because this is a choice between two
-  arbitrary ways of doing things and the maintainer and submitter prefer
-  different ways of doing things, possibly because changing the behaviour
-  will cause other, worse, problems for others, or possibly for other
-  reasons.
-
-<dt><code>moreinfo</code>
-  <dd>This $gBug can\'t be addressed until more information is provided by the
-  submitter. The $gBug will be closed if the submitter doesn\'t provide more
-  information in a reasonable (few months) timeframe. This is for $gBugs like
-  \"It doesn\'t work\". What doesn\'t work?
-
-<dt><code>unreproducible</code>
-  <dd>This $gBug can\'t be reproduced on the maintainer\'s system.  Assistance
-  from third parties is needed in diagnosing the cause of the problem.
-
-<dt><code>fixed</code>
-  <dd>This $gBug is fixed or worked around, but there\'s still an issue that
-  needs to be resolved.
-
-<dt><code>stable</code>
-  <dd>This $gBug affects the stable distribution in particular.  This is only
-  intended to be used for ease in identifying release critical $gBugs that
-  affect the stable distribution.  It\'ll be replaced eventually with
-  something a little more flexible, probably.
-";
-
-############################################################################
-# shows up at the start of (most) html pages.
-############################################################################
-$gHTMLStart = "<BODY>";
-
-############################################################################
-# shows up at the end of (most) html pages.
-############################################################################
-$gHTMLTail = "
- <ADDRESS>$gMaintainer &lt;<A HREF=\"mailto:$gMaintainerEmail\">$gMaintainerEmail</A>&gt;.
- Last modified:
- <!--timestamp-->
- SUBSTITUTE_DTIME
- <!--timestamp-->
-         
- <P>
- <A HREF=\"http://$gWebDomain/\">Debian $gBug tracking system</A><BR>
- Copyright (C) 1999 Darren O. Benham,
- 1997,2003 nCipher Corporation Ltd,
- 1994-97 Ian Jackson.
- </ADDRESS>
-";
-
-############################################################################
-# Message on when reports are purged.
-############################################################################
-$gHTMLExpireNote = "(Closed $gBugs are archived $gRemoveAge days after the last related message is received.)";
-
-############################################################################
-# Makeup of the stamp page
-############################################################################
-$gHTMLStamp = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">
-       <HTML><HEAD><TITLE>$gProject $gBugs - timestamp page</TITLE>
-       <LINK REV=\"made\" HREF=\"mailto:$gMaintainerEmail\">
-       </HEAD>$gHTMLStart<H1>Is this $gBug log or mirror up to date?</H1>
-
-       Unlike all the other $gBug pages, this small timestamp page is updated every
-       time the update check job runs.  If the timestamp here is recent it\'s
-       likely that the mirror in which you\'re reading it is up to date.
-       <P>
-       The last
-       <!--updateupdate-->update<!--/updateupdate-->
-       was at 
-       <STRONG><!--updatetime-->SUBSTITUTE_DTIME<!--/updatetime--></STRONG>;
-       The logs are usually checked every hour and updated if necessary.
-       <P>
-       For the $gBug index or for other information about $gProject and the $gBug 
-       system, see the <A HREF=\"../../\">$gBug system main contents page</A>.
-
-       <HR>
-       <ADDRESS>
-       <A HREF=\"mailto:$gMaintainerEmail\">$gMaintainerEmail</A>,
-       through the <A HREF=\"../../\">$gProject $gBug database</a>
-       </ADDRESS>
-       <!--version 1.0-4.3-->";
-
-############################################################################
-# Makeup of the indices pages
-############################################################################
-$gFullIndex = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">
-       <HTML><HEAD><TITLE>$gProject $gBugs - full index</TITLE>
-       <LINK REV=\"make\" HREF=\"mailto:$gMaintainerEmail\">
-       </HEAD>$gHTMLStart<H1>$gProject $gBug report logs - index</H1>
-
-       This index gives access to $gBugs sent to <CODE>submit\@$gEmailDomain</CODE>
-       but not yet marked as done, and to $gBugs marked as done but not yet purged
-       from the database (this happens $gRemoveAge days after the last message relating to
-       the report).
-       <P>
-       For other kinds of indices or for other information about $gProject and
-       the $gBug system, see <A HREF=\"../../\">$gBug system top-level contents WWW
-       page</A>.
-
-
-       ";
-
-$gJunkIndex = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">
-       <HTML><HEAD><TITLE>$gProject $gBug reports - Junk</TITLE>
-       <LINK REV=\"made\" HREF=\"$gMaintainerEmail\">
-       </HEAD>$gHTMLStart<H1>$gProject $gBug reports - Junk</H1>
-
-       This is the index page for logs of messages not associated with a specific
-       $gBug report.
-       <P>
-       For other kinds of indices or for other information about $gProject and
-       the $gBug system, see <A HREF=\"../../\">$gBug system top-level contents WWW
-       page</A>.
-
-
-       ";
-
-$gMaintIndex = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">
-       <HTML><HEAD><TITLE>$gProject $gBug reports by maintainer</TITLE>
-       <LINK REF=\"made\" HREF=\"mailto:$gMaintainerEmail\">
-       </HEAD>$gHTMLStart<H1>$gProject $gBug reports by maintainer</H1>
-
-       This page lists the package maintainers against whose packages there are
-       outstanding, forwarded or recently-closed $gBug reports.  A maintainer who
-       has several versions of their email address in the <CODE>Maintainer</CODE>
-       package control file field may appear several times.<P>
-       If the maintainers information here is not accurate, please see 
-       <A HREF=\"../../Developer.html#maintincorrect\">the developers\'
-       instructions</A> to find how this can happen and what to do about it. <P>
-       For other kinds of indices or for other information about $gProject and
-       the $gBug system, see <A HREF=\"../../\">$gBug system top-level contents WWW
-       page</A>.
-
-
-       ";
-
-$gPackageIndex = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">
-       <HTML><HEAD><TITLE>$gProject $gBug reports by package</TITLE>
-       <LINK REF=\"made\" HREF=\"mailto:$gMaintainerEmail\">
-       </HEAD>$gHTMLStart<H1>$gProject $gBug reports by package</H1>
-
-       This page lists the package against which there are outstanding, forwarded or
-       recently-closed $gBug reports.  A multi-binary package may appear several
-       times, once for each binary package name and once for the source package
-       name (if it is different).<P>
-       For other kinds of indices or for other information about $gProject and
-       the $gBug system, see <A HREF=\"../../\">$gBug system top-level contents WWW
-       page</A>.
-
-
-       ";
-
-$gSummaryIndex = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">
-       <HTML><HEAD><TITLE>$gProject $gBug report logs - summary index</TITLE>
-       <LINK REF=\"made\" HREF=\"mailto:$gMaintainerEmail\">
-       </HEAD>$gHTMLStart<H1>$gProject $gBug report logs - summary index</H1>
-
-       This summary index briefly lists $gBugs sent to <CODE>submit\@$gEmailDomain
-       </CODE> but not yet marked as done, or as forwarded to an upstream author.  
-       Here they are sorted by reference number (and therefore by submission date, 
-       too).<P>
-       For other kinds of indices or for other information about $gProject and
-       the $gBug system, see <A HREF=\"../../\">$gBug system top-level contents WWW
-       page</A>.
-
-       <P>The * column lists the first letter of the severity of the $gBug.
-
-
-       ";
-
-$gPackageLog = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">
-       <HTML><HEAD><TITLE>$gProject $gBug report logs - index by package</TITLE>
-       <LINK REF=\"made\" HREF=\"mailto:$gMaintainerEmail\">
-       </HEAD>$gHTMLStart<H1>$gProject $gBug report logs - index by package</H1>
-
-       This summary index briefly lists $gBugs sent to <CODE>submit\@$gEmailDomain
-       </CODE> but not yet marked as done, or as forwarded to an upstream author.  
-       Here they are sorted by package name.<P>
-       For other kinds of indices or for other information about $gProject and
-       the $gBug system, see <A HREF=\"../../\">$gBug system top-level contents WWW
-       page</A>.
-
-
-       ";
-
-$gPseudoIndex = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">
-       <HTML><HEAD><TITLE>$gProject $gBug report pseudo-packages</TITLE>
-       <LINK REF=\"made\" HREF=\"mailto:$gMaintainerEmail\">
-       </HEAD>$gHTMLStart<H1>$gProject $gBug report pseudo-packages</H1>
-
-       This page lists the pseudo-packages available for use in the
-       <CODE>Package:</CODE> line in $gBug reports.<P>
-
-       See the <A HREF=\"../../Reporting.html\">instructions for reporting a
-       $gBug</A> for details of how to specify a <CODE>Package:</CODE> line.<P>
-       For other kinds of indices or for other information about $gProject and
-       the $gBug system, see <A HREF=\"../../\">$gBug system top-level contents WWW
-       page</A>.
-
-
-       ";